【问题标题】:Unable to have two-word-search in Zsh's TAB completion for Man无法在 Zsh 的 Man TAB 完成中进行两个词搜索
【发布时间】:2009-05-09 02:48:17
【问题描述】:

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

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

man zsh:reverse <TAB>

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

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

【问题讨论】:

    标签: search zsh zsh-completion


    【解决方案1】:

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

    当您在 zsh 中对 man 使用 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 是所有目录的变量,其中包含所请求部分的联机帮助页。实际的通配模式由隐式参数$PREFIXcompfiles 的最后一个参数构成 - 在这种情况下为*。这导致/usr/share/man/man1 被转换为/usr/share/man/man1/foo*
    7. 新的 glob 模式列表被 glob,获取所有匹配该模式的文件
    8. _man_pages 然后从文件中删除任何后缀,并使用 compadd 将它们添加到选项的完成小部件列表中

    现在,如您所见,联机帮助页列表直接由$PREFIX 变量确定。为了使zsh:foo 仅列出包含单词foozsh* 的手册页,需要将其拆分为: 字符(如果有)。

    _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 cmets):

    _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 不变,但它仍然不想工作。

    无论如何,这至少应该让你开始。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多