Невозможно выполнить поиск по двум словам в TAB-дополнении Zsh для Man.

Проблема: наличие автодополнения табуляции, которое берет два слова и вычисляет из них наилучшее совпадение для Man, а затем возвращает наилучшие совпадения.

Пример: Следующий псевдокод должен дать мне, по крайней мере, команду reverse-menu-complete Zsh. Прямо сейчас я не могу искать руководства внутри руководств без zgrep.

man zsh:reverse <TAB>

где ":" - разделитель, который я хочу.

Первоначальная проблема: какие файлы запускаются с помощью TAB, когда я нажимаю TAB для одного слова в поиске руководств по Zsh?


person Léo Léopold Hertz 준영    schedule 09.05.2009    source источник


Ответы (1)


Я попытаюсь дать представление о том, как работает система завершения zsh, и неполный подход к этой проблеме.

Файл, который запускается, когда вы используете завершение TAB для man в zsh, находится в каталоге /usr/share/zsh/${ZSH_VERSION}/functions. Дерево различается в зависимости от дистрибутива, но файл называется _man и обеспечивает завершение для man, apropos и whatis.

После вызова _man он работает следующим образом (приблизительное описание):

  1. если завершение для man и --local-file было указано в качестве первого флага, вызвать стандартное завершение файлов (_files)
  2. создать переменную manpath из набора значений по умолчанию / $MANPATH. Здесь будут искать man-страницы
  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: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", и не отображает полученные совпадения. Я пытался сохранить $PREFIX как есть после удаления строки grep, но он все равно не хочет работать.

Во всяком случае, это, по крайней мере, должно помочь вам начать.

person ASk    schedule 22.05.2009