简体   繁体   English

bash可编程选项卡完成,带有增量目录完成

[英]bash programmable tab completion with incremental directory completion

I have a program that can accept a large number of valid arguments. 我有一个可以接受大量有效参数的程序。 Many of these have common prefixes and are in a directory tree structure. 其中许多具有共同的前缀,并且处于目录树结构中。 The options often do not exist and some directories can be used with or without the slash but some can only be used without the slash. 这些选项通常不存在,并且某些目录可以带斜线或不带斜线使用,但有些目录只能不带斜线使用。

If I already have bash completion that shows me this list of alternatives and changes the commandline as I press and partially complete an argument as below: 如果我已经有了bash补全功能,可以显示此替代列表,并在按下时更改命令行,并部分完成以下参数:

$ myprog <TAB><TAB>
foo
somedirectory/a
somedirectory/b
somedirectory/c
zibble
zibble/a
zibble/b
zibble/c
$ myprog so<TAB>
$ myprog somedirectory<TAB><TAB>
somedirectory/a
somedirectory/b
somedirectory/c
$ myprog somedirectory/<TAB><TAB>
a
b
c

How do I change it for bash completion that does this like regular file/dir completion: 我如何更改它来完成bash补全,就像常规的文件/目录补全一样:

$ myprog <TAB><TAB>
foo
somedirectory/
zibble
zibble/
$ myprog so<TAB>
$ myprog somedirectory/<TAB><TAB>
a
b
c

and

$ myprog <TAB><TAB>
foo
somedirectory/
zibble
zibble/
$ myprog zi<TAB>
$ myprog zibble<TAB><TAB>
zibble
zibble/
$ myprog zibble/<TAB><TAB>
a
b
c

The key is to add this line just before returning completions for the the word that should behave like directory completion: [[ $COMPREPLY == */ ]] && compopt -o nospace 关键是在返回补全字样的单词之前添加此行,该单词的行为应类似于目录[[ $COMPREPLY == */ ]] && compopt -o nospace[[ $COMPREPLY == */ ]] && compopt -o nospace

By running this after compgen the only suggestions in the COMPREPLY array match the current word being completed. 通过在compgen之后运行此命令,COMPREPLY数组中的唯一建议与正在完成的当前单词匹配。 If there is more than one suggestion it will only be partially completed and no space will be added (standard behaviour). 如果有多个建议,它将仅部分完成,并且不会添加任何空间(标准行为)。 If there is only one suggestion then it is used to decide whether to add a space. 如果只有一个建议,那么它将用于决定是否添加空格。

Now if you trim all the filenames under zibble/ bash will not add a space separator when it figures out that zibble/ is the only match for the word being completed. 现在,如果修剪掉zibble /下的所有文件名,bash会发现zibble /是要完成的单词的唯一匹配项,则不会添加空格分隔符。

Here's a patch against the bash completion of debian http://anonscm.debian.org/gitweb/?p=bash-completion/bash-completion.git - so there is some weird stuff in the patch context. 这是一个针对Debian的bash完成的补丁程序, http ://anonscm.debian.org/gitweb/ ? p=bash-completion/bash-completion.git-因此,补丁程序上下文中有些奇怪的东西。

It applies cleanly against this version using the git apply command of the "git" version control program: http://anonscm.debian.org/gitweb/?p=bash-completion/bash-completion.git;a=commit;h=2897e62fe7e535eb048f7e08f03ac3fbc3a84fa5 使用“ git”版本控制程序的git apply命令,可以完全针对该版本进行应用: http : //anonscm.debian.org/gitweb/? p=bash-completion/bash-completion.git;a=commit; h = 2897e62fe7e535eb048f7e08f03ac3fbc3a84fa5

diff --git a/completions/make b/completions/make
index aa19b24..345deea 100644
--- a/completions/make
+++ b/completions/make
@@ -1,11 +1,66 @@
 # bash completion for GNU make                             -*- shell-script -*-

+function _make_target_extract_script()
+{
+    local prefix=$(printf "%s\n" "$1" | sed 's/[][\.*^$(){}?+|/]/\\&/g')
+
+    cat <<EOF
+    /^# Make data base/,/^# Files/d             # skip until files section
+    /^# Not a target/,/^$/        d             # skip not target blocks
+    /^${prefix}/,/^$/!            d             # skip anything user dont want
+
+    # The stuff above here describes lines that are not
+    #  explicit targets or not targets other than special ones
+    # The stuff below here decides whether an explicit target
+    #  should be output.
+
+    /^# File is an intermediate prerequisite/ {
+      s/^.*$//;x                                # unhold target
+      d                                         # delete line
+    }
+
+    /^$/ {                                      # end of target block
+      x                                         # unhold target
+      s/^(${prefix}[^:/]*\/).*:.*$/\1/p         # write targets for subdirs
+      s/:.*$/ /p                                 # write complete targets
+      d                                         # hide any bugs
+    }
+
+    /^[^#\t:%]+:/ {         # found target block
+
+      /^\.PHONY/                  d             # special target
+      /^\.SUFFIXES/               d             # special target
+      /^\.DEFAULT/                d             # special target
+      /^\.PRECIOUS/               d             # special target
+      /^\.INTERMEDIATE/           d             # special target
+      /^\.SECONDARY/              d             # special target
+      /^\.SECONDEXPANSION/        d             # special target
+      /^\.DELETE_ON_ERROR/        d             # special target
+      /^\.IGNORE/                 d             # special target
+      /^\.LOW_RESOLUTION_TIME/    d             # special target
+      /^\.SILENT/                 d             # special target
+      /^\.EXPORT_ALL_VARIABLES/   d             # special target
+      /^\.NOTPARALLEL/            d             # special target
+      /^\.ONESHELL/               d             # special target
+      /^\.POSIX/                  d             # special target
+      /^\.NOEXPORT/               d             # special target
+      /^\.MAKE/                   d             # special target
+
+      /^[^a-zA-Z0-9]/             d             # convention for hidden tgt
+
+      h                                         # hold target
+      d                                         # delete line
+    }
+
+EOF
+}
+
 _make()
 {
     local cur prev words cword split
     _init_completion -s || return

-    local file makef makef_dir="." makef_inc i
+    local file makef makef_dir=( "-C" "." ) makef_inc i

     case $prev in
         -f|--file|--makefile|-o|--old-file|--assume-old|-W|--what-if|\
@@ -49,7 +104,7 @@ _make()
         for (( i=0; i < ${#words[@]}; i++ )); do
             if [[ ${words[i]} == -@(C|-directory) ]]; then
                 # eval for tilde expansion
-                eval makef_dir=${words[i+1]}
+                eval makef_dir=( -C "${words[i+1]}" )
                 break
             fi
         done
@@ -59,18 +114,17 @@ _make()
         for (( i=0; i < ${#words[@]}; i++ )); do
             if [[ ${words[i]} == -@(f|-?(make)file) ]]; then
                 # eval for tilde expansion
-                eval makef=${words[i+1]}
+                eval makef=( -f "${words[i+1]}" )
                 break
             fi
         done

-        [[ -n $makef ]] && makef="-f ${makef}"
-        [[ -n $makef_dir ]] && makef_dir="-C ${makef_dir}"
+        COMPREPLY=( $( compgen -W "$( 
+            make -npq "${makef[@]}" "${makef_dir[@]}" .DEFAULT 2>/dev/null | \
+            sed -n -r -f <(_make_target_extract_script "$cur")
+       )" -- "$cur" ) )

-        COMPREPLY=( $( compgen -W "$( make -qp $makef $makef_dir 2>/dev/null | \
-            awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ \
-            {split($1,A,/ /);for(i in A)print A[i]}' )" \
-            -- "$cur" ) )
+        [[ $COMPREPLY == */ ]] && compopt -o nospace

     fi
 } &&

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM