Subject: [PATCH 2/3] Completion: New helper _absolute_command_paths.
From: Daniel Shahaf <[email-masked]>
To: [email-masked]
Date: Mon, 18 Jan 2016 02:36:59 +0000
List-Id: Zsh Workers List <zsh-workers.zsh.org>

---
 Completion/Unix/Command/_git                 |  8 ++++----
 Completion/Unix/Type/_absolute_command_paths | 29 ++++++++++++++++++++++++++++
 Doc/Zsh/compsys.yo                           |  6 ++++++
 3 files changed, 39 insertions(+), 4 deletions(-)
 create mode 100644 Completion/Unix/Type/_absolute_command_paths

diff --git a/Completion/Unix/Command/_git b/Completion/Unix/Command/_git
index 2a124c1..ce35219 100644
--- a/Completion/Unix/Command/_git
+++ b/Completion/Unix/Command/_git
@@ -1941,7 +1941,7 @@ _git-config () {
     'branch.*.pushremote:what remote git push should push to::__git_remotes'
     'branch.*.rebase:rebase on top of fetched branch::->bool:false'
     'browser.*.cmd:browser command to use:browser:_cmdstring'
-    'browser.*.path:path to use for the browser:absolute browser path:_files -g "*(*)"'
+    'browser.*.path:path to use for the browser:absolute browser path:_absolute_command_paths'
     clean.requireForce:'require --force for git clean to actually do something::->bool:true'
     color.branch:'color output of git branch::->color-bool:false'
     color.branch.current:'color of the current branch::->color'
@@ -2013,7 +2013,7 @@ _git-config () {
     diff.suppressBlankEmpty:'inhibit printing space before empty output lines::->bool:false'
     diff.tool:'diff tool to use::__git_difftools'
     'difftool.*.cmd:command to invoke for the diff tool::_cmdstring'
-    'difftool.*.path:path to use for the diff tool:absolute diff tool path:_files -g "*(*)"'
+    'difftool.*.path:path to use for the diff tool:absolute diff tool path:_absolute_command_paths'
     difftool.prompt:'prompt before each invocation of the diff tool::->bool:true'
     diff.wordRegex:'regex used to determine what a word is when performing word-by-word diff:regular expression:->string'
     diff.guitool:'diff tool with gui to use::__git_difftools'
@@ -2180,7 +2180,7 @@ _git-config () {
     mailmap.file:'augmenting mailmap file:mailmap file:_files'
     man.viewer:'man viewer to use for help in man format::__git_man_viewers'
     'man.*.cmd:the command to invoke the specified man viewer:man command:_cmdstring'
-    'man.*.path:path to use for the man viewer:absolute man tool path:_files -g "*(*)"'
+    'man.*.path:path to use for the man viewer:absolute man tool path:_absolute_command_paths'
     merge.branchdesc:'populate the log message with the branch description text as well::->bool:false'
     merge.conflictstyle:'style used for conflicted hunks::->merge.conflictstyle:merge'
     merge.defaultToUpstream:'merge the upstream branches configured for the current branch by default::->bool:true'
@@ -2194,7 +2194,7 @@ _git-config () {
     'merge.*.name:human-readable name for custom low-level merge driver:name:->string'
     'merge.*.driver:command that implements a custom low-level merge driver:merge command:_cmdstring'
     'merge.*.recursive:low-level merge driver to use when performing internal merge between common ancestors::__git_builtin_merge_drivers'
-    'mergetool.*.path:path to use for the merge tool:absolute merge tool path:_files -g "*(*)"'
+    'mergetool.*.path:path to use for the merge tool:absolute merge tool path:_absolute_command_paths'
     'mergetool.*.cmd:command to invoke for the merge tool:merge command:_cmdstring'
     'mergetool.*.trustExitCode:trust the exit code of the merge tool::->bool:false'
     mergetool.keepBackup:'keep the original file with conflict markers::->bool:true'
diff --git a/Completion/Unix/Type/_absolute_command_paths b/Completion/Unix/Type/_absolute_command_paths
new file mode 100644
index 0000000..e9ab170
--- /dev/null
+++ b/Completion/Unix/Type/_absolute_command_paths
@@ -0,0 +1,29 @@
+#autoload
+
+# This function completes 'ls' to '/bin/ls'
+_hashed_absolute_command_paths() {
+  local -aU set_of_dirs_of_hashed_commands=( ${^commands%/*}/ )
+  local i
+  integer ret=1
+  for i in $set_of_dirs_of_hashed_commands
+  do
+    compadd -M "l:|=$i" "$expl[@]" -a 'commands[(R)${~i}[^/]#]'
+    ret=0
+  done
+  return ret
+}
+
+# This function completes absolute pathnames of executables, e.g., /etc/rc.local
+_typed-in_absolute_command_paths() {
+  # TODO: the description "full path to an executable" and tag in the caller are ignored by _path_files
+  _path_files -/ -g '*(-*)' -P / -W /
+}
+
+_absolute_command_paths() {
+  _alternative \
+    'commands:hashed command by absolute path:_hashed_absolute_command_paths' \
+    'commands:full path to an executable:_typed-in_absolute_command_paths'
+}
+
+
+_absolute_command_paths "$@"
diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo
index 9954f30..2aa0740 100644
--- a/Doc/Zsh/compsys.yo
+++ b/Doc/Zsh/compsys.yo
@@ -3461,6 +3461,12 @@ completion has been tried.  A function will only be called once unless
 it explicitly reinserts itself into the array.
 
 startitem()
+findex(_absolute_command_paths)
+item(tt(_absolute_command_paths))(
+This function completes external commands as absolute paths (unlike
+tt(_command_names -e) which completes their basenames).  It takes no
+arguments.
+)
 findex(_all_labels)
 item(tt(_all_labels) [ tt(-x) ] [ tt(-12VJ) ] var(tag) var(name) var(descr) [ var(command) var(arg) ... ])(
 This is a convenient interface to the tt(_next_label) function below,
-- 
2.1.4