0

問題: 2 つの単語を取り、それらから Man の最適な一致を計算し、最適な一致を返すタブ補完を使用する

例:次の疑似コードは、少なくとも Zsh の reverse-menu-complete -command を提供するはずです。現在、zgrep がないとマニュアル内のマニュアルを検索できません。

man zsh:reverse <TAB>

ここで、「:」は必要なセパレーターです。

初期の問題: Zsh でマニュアルを検索する際に TAB を押して 1 つの単語を検索すると、TAB 補完が実行されるのはどのファイルですか?

4

1 に答える 1

2

zsh完了システムがどのように機能するかについての洞察を提供しようとしますが、この問題は不完全です。

manzshでTAB補完を使用するときに実行されるファイルは、/usr/share/zsh/${ZSH_VERSION}/functionsディレクトリの下にあります。ツリーはディストリビューションによって異なりますが、ファイルの名前は、、および_manの補完を提供します。manaproposwhatis

_manが呼び出されると、次のように機能します(大まかな説明)。

  1. 完了しman--local-file最初のフラグとして指定された場合は、標準ファイルの完了を呼び出します(_files
  2. manpathデフォルトのセット/から変数を作成します$MANPATH。これは、マンページが検索される場所です
  3. セクション番号パラメーターを使用して呼び出したかどうかを判別manします。はいの場合、それらのセクションのみが検索されます
  4. が使用された場合はzstyle ':completion:*:manuals' separate-sections true、出力のセクションを分離します(それらを混在させないでください)
  5. 呼び出し_man_pagesて、一致するマニュアルページのリストを提供します
  6. _man_pagesで少し魔法をかけcompfiles -p pages '' '' "$matcher" '' dummy '*'ます。pagesは、要求されたセクションのマンページを含むすべてのディレクトリを持つ変数です。実際のグロブパターンは、暗黙のパラメータ$PREFIXと最後のパラメータからcompfiles-*この場合は-に構築されます。これにより、次の/usr/share/man/man1ように変換されます/usr/share/man/man1/foo*
  7. グロブパターンの新しいリストがグロブされ、パターンに一致するすべてのファイルが取得されます
  8. _man_pages次に、ファイルからサフィックスを取り除き、次を使用してそれらを選択の完了ウィジェットリストに追加します。compadd

ご覧のとおり、マンページのリストは$PREFIX変数によって直接決定されます。単語を含むzsh:foomanページのみをリストするためには、文字(存在する場合)に分割する必要があります。zsh*foo:

次の追加により_man_pages、問題が部分的に解決されます(zsh 4.3.4)。

オリジナル:

_man_pages() {
  local matcher pages dummy sopt

  zparseopts -E M+:=matcher

  if (( $#matcher )); then
    matcher=( ${matcher:#-M} )
    matcher="$matcher"
  else
    matcher=
  fi

  pages=( ${(M)dirs:#*$sect/} )

  compfiles -p pages '' '' "$matcher" '' dummy '*'
  pages=( ${^~pages}(N:t) )

  (($#mrd)) && pages[$#pages+1]=($(awk $awk $mrd))

  # Remove any compression suffix, then remove the minimum possible string
  # beginning with .<->: that handles problem cases like files called
  # `POSIX.1.5'.

  [[ $OSTYPE = solaris* ]] && sopt='-s '
  if ((CURRENT > 2)) ||
      ! zstyle -t ":completion:${curcontext}:manuals.$sect" insert-sections
  then
    compadd "$@" - ${pages%.(?|<->*(|.gz|.bz2|.Z))}
  else
    compadd "$@" -P "$sopt$sect " - ${pages%.(?|<->*(|.gz|.bz2|.Z))}
  fi
}

変更(## modコメントを探す):

_man_pages() {
  local matcher pages dummy sopt

  zparseopts -E M+:=matcher

  if (( $#matcher )); then
    matcher=( ${matcher:#-M} )
    matcher="$matcher"
  else
    matcher=
  fi

  pages=( ${(M)dirs:#*$sect/} )

  ##mod
  # split components by the ":" character
  local pref_words manpage_grep orig_prefix
  # save original prefix (just in case)
  orig_prefix=${PREFIX}
  # split $PREFIX by ':' and make it an array
  pref_words=${PREFIX//:/ }
  set -A pref_words ${=pref_words}
  # if there are both manpage name and grep string, use both
  if (( $#pref_words == 2 )); then
      manpage_grep=$pref_words[2]
      # PREFIX is used internally by compfiles
      PREFIX=$pref_words[1]
  elif (( $#pref_words == 1 )) && [[ ${PREFIX[1,1]} == ":" ]]; then
      # otherwise, prefix is empty and only grep string exists
      PREFIX=
      manpage_grep=$pref_words[1]
  fi


  compfiles -p pages '' '' "$matcher" '' dummy '*'
  ##mod: complete, but don't strip path names
  pages=( ${^~pages} )

  (($#mrd)) && pages[$#pages+1]=($(awk $awk $mrd))

  ##mod: grep pages
  # Build a list of matching pages that pass the grep
  local matching_pages
  typeset -a matching_pages

  # manpage_grep exists and not empty 
  if (( ${#manpage_grep} > 0 )); then
      for p in ${pages}; do
          zgrep "${manpage_grep}" $p > /dev/null
          if (( $? == 0 )); then
              #echo "$p matched $manpage_grep"
              matching_pages+=($p)
          fi
      done
  else
  # there's no manpage_grep, so all pages match
      matching_pages=( ${pages} )
  fi

  #echo "\nmatching manpages: "${matching_pages}
  pages=( ${matching_pages}(N:t) )
  # keep the stripped prefix for now
  #PREFIX=${orig_prefix}


  # Remove any compression suffix, then remove the minimum possible string
  # beginning with .<->: that handles problem cases like files called
  # `POSIX.1.5'.


  [[ $OSTYPE = solaris* ]] && sopt='-s '
  if ((CURRENT > 2)) ||
      ! zstyle -t ":completion:${curcontext}:manuals.$sect" insert-sections
  then
    compadd "$@" - ${pages%.(?|<->*(|.gz|.bz2|.Z))}
  else
    compadd "$@" -P "$sopt$sect " - ${pages%.(?|<->*(|.gz|.bz2|.Z))}
  fi
}

ただし、まだ完全には機能していません(#echo "$p matched $manpage_grep"行のコメントを外すと、リストが作成されていることがわかります)-内部のどこかで、完了システムは、たとえば、「zshcompctl」がプレフィックス「zsh」と一致していないことを認識していると思われます。 :foo "であり、結果の一致は表示されません。grep文字列を削除した後、そのままにしておこうとしまし$PREFIXたが、それでも機能しません。

とにかく、これは少なくともあなたが始めるはずです。

于 2009-05-22T07:47:17.067 に答える