0

问题:有一个制表符补全,它接受两个单词并从中为 Man 计算最佳匹配,然后返回最佳匹配

示例:下面的伪代码至少应该给我 Zsh 的 reverse-menu-complete 命令。现在,如果没有 zgrep,我无法在手册中搜索手册。

man zsh:reverse <TAB>

其中“:”是我想要的分隔符。

初始问题:当我在 Zsh 搜索手册中按 TAB 一个单词时,TAB 完成会运行哪些文件?

4

1 回答 1

2

我将尝试提供有关 zsh 补全系统如何工作的见解以及对这个问题的不完全理解。

在 zsh 中使用 TAB 补全时运行的文件man位于该/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. 新的 glob 模式列表是 glob 的,获取与该模式匹配的所有文件
  8. _man_pages然后从文件中删除任何后缀并将它们添加到选择的完成小部件列表中compadd

现在,如您所见,联机帮助页列表直接由$PREFIX变量确定。为了zsh:foo仅列出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原样,但它仍然不想工作。

At any rate, this at least should get you started.

于 2009-05-22T07:47:17.067 回答