Hello,

I'm new here, so let me know if there already is an ongoing discussion on this topic or if I get anything wrong about this.

I noticed that some extensions for pass (like pass-update or pass-tomb) provide completion files, which are poorly integrated in pass. This is due to the fact, that completion files are loaded dynamically on demand. For example in bash the file $BASHCOMPDIR/pass is loaded when you enter "pass <TAB>". As the completion file for pass-update is stored in $BASHCOMPDIR/pass-update it is not loaded unless you type "pass-update <TAB>". The completion files in fish and zsh are handled in a similar manner.

I'd like to suggest a fix for that issue. The completion files of pass may automatically seek completion files of extensions and source them. Enclosed you find a patch that provides that feature together with example completion files for the extension pass-file.

Regards,
Simon Blum

From 2a0749c2e5191d1a8c185fee8a7f75a4b363d15c Mon Sep 17 00:00:00 2001
From: Simon Blum <[email protected]>
Date: Thu, 1 Dec 2022 18:21:09 +0100
Subject: [PATCH] automatically source extension completion files

Currently the only way to provide completion for extensions is to modify
the completion files of pass.

With this patch the completion files of pass automatically source
completion files of extensions or call their init function (zsh).
---
 src/completion/pass.bash-completion | 23 ++++++++++++++++-------
 src/completion/pass.fish-completion |  7 +++++++
 src/completion/pass.zsh-completion  | 21 +++++++++++++++++++--
 3 files changed, 42 insertions(+), 9 deletions(-)

diff --git a/src/completion/pass.bash-completion b/src/completion/pass.bash-completion
index 2d23cbf..9ca9ec1 100644
--- a/src/completion/pass.bash-completion
+++ b/src/completion/pass.bash-completion
@@ -4,6 +4,22 @@
 # Brian Mattern <[email protected]>. All Rights Reserved.
 # This file is licensed under the GPLv2+. Please see COPYING for more information.
 
+# To add completion for an extension command define a function like this:
+# __password_store_extension_complete_<COMMAND>() {
+#     COMPREPLY+=($(compgen -W "-o --option" -- ${cur}))
+#     _pass_complete_entries 1
+# }
+#
+# and add the command to the $PASSWORD_STORE_EXTENSION_COMMANDS array
+# within a file pass.extension.<EXTENSION_NAME> residing in the same
+# directory as this file
+readarray -d "" extension_compfiles < <(find $(dirname $BASH_SOURCE) -maxdepth 1 -name "pass.extension.*" \( -type f -o -type l \) -print0)
+for compfile in "${extension_compfiles[@]}"; do
+	source "$compfile"
+done
+unset compfile
+unset extension_compfiles
+
 _pass_complete_entries () {
 	local prefix="${PASSWORD_STORE_DIR:-$HOME/.password-store/}"
 	prefix="${prefix%/}/"
@@ -125,13 +141,6 @@ _pass()
 				;;
 		esac
 
-		# To add completion for an extension command define a function like this:
-		# __password_store_extension_complete_<COMMAND>() {
-		#     COMPREPLY+=($(compgen -W "-o --option" -- ${cur}))
-		#     _pass_complete_entries 1
-		# }
-		#
-		# and add the command to the $PASSWORD_STORE_EXTENSION_COMMANDS array
 		if [[ " ${PASSWORD_STORE_EXTENSION_COMMANDS[*]} " == *" ${COMP_WORDS[1]} "* ]] && type "__password_store_extension_complete_${COMP_WORDS[1]}" &> /dev/null; then
 			"__password_store_extension_complete_${COMP_WORDS[1]}"
 		fi
diff --git a/src/completion/pass.fish-completion b/src/completion/pass.fish-completion
index 0f57dd2..af4002f 100644
--- a/src/completion/pass.fish-completion
+++ b/src/completion/pass.fish-completion
@@ -1,6 +1,13 @@
 # Copyright (C) 2012-2014 Dmitry Medvinsky <[email protected]>. All Rights Reserved.
 # This file is licensed under the GPLv2+. Please see COPYING for more information.
 
+# Completion files for extensions should be named pass.extension.<EXTENSION_NAME>.fish
+# and reside in the same directory as this file
+set -l extension_compfiles (find $(path dirname $(status -f)) -maxdepth 1 -name "pass.extension.*.fish" \( -type f -o -type l \) -print0 | string split0)
+for compfile in $extension_compfiles
+	source "$compfile"
+end
+
 set -l PROG 'pass'
 
 function __fish_pass_get_prefix
diff --git a/src/completion/pass.zsh-completion b/src/completion/pass.zsh-completion
index d911e12..d4d038d 100644
--- a/src/completion/pass.zsh-completion
+++ b/src/completion/pass.zsh-completion
@@ -17,6 +17,19 @@
 #   PASSWORD_STORE_DIR=$HOME/work/pass pass $@
 # }
 
+# To add completion for an extension command create a file named
+# _pass.extension.<EXTENSION_NAME> in the same directory as this file
+# with following content:
+# #autoload
+# PASSWORD_STORE_EXTENSION_COMMANDS+=("<COMMAND>:<COMMAND_DESCRIPTION>")
+# __password_store_extension_complete_<COMMAND>() {
+#     <COMPLETION_FOR_COMMAND>
+# }
+# unfunction $(basename $(print -P %x))
+local -a extensions=($(print -l ${(ok)functions[(I)_pass.extension.*]}))
+for extension in "$extensions[@]"; do
+	$extension
+done
 
 _pass () {
 	local cmd
@@ -84,10 +97,14 @@ _pass () {
 				)
 				_describe -t commands 'pass git' subcommands
 				;;
-			show|*)
+			show|-*)
 				_pass_cmd_show
 				;;
 		esac
+
+		if [[ " ${PASSWORD_STORE_EXTENSION_COMMANDS[@]%%:*} " == *" ${cmd} "* ]] && type "__password_store_extension_complete_${cmd}" &> /dev/null; then
+			"__password_store_extension_complete_${cmd}"
+		fi
 	else
 		local -a subcommands
 		subcommands=(
@@ -106,7 +123,7 @@ _pass () {
 			"version:Output version information"
 			"help:Output help message"
 		)
-		_describe -t commands 'pass' subcommands
+		_describe -t commands 'pass' subcommands -- PASSWORD_STORE_EXTENSION_COMMANDS
 		_arguments : \
 			"--version[Output version information]" \
 			"--help[Output help message]"
-- 
2.38.1

# completion file for bash

PASSWORD_STORE_EXTENSION_COMMANDS+=("file")

__password_store_extension_complete_file() {
  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$COMP_CWORD" in
    2)
      COMPREPLY+=($(compgen -W "add get" -- ${cur}))
      ;;
    3)
      case "${COMP_WORDS[2]}" in
        a|add)
          COMPREPLY+=($(compgen -f ${cur}))
          ;;
        g|get)
          _pass_complete_entries 1
          ;;
      esac
      ;;
    4)
      case "${COMP_WORDS[2]}" in
        a|add)
          _pass_complete_entries
          ;;
      esac
      ;;
  esac
}
#autoload

PASSWORD_STORE_EXTENSION_COMMANDS+=("file:add/get file to/from password-store")

__password_store_extension_complete_file () {
  case "$CURRENT" in
    2)
      local -a subcommands
      subcommands=(
        "add:add file to password-store"
        "get:get file from password-store"
      )
      _describe -t commands 'pass' subcommands 
      ;;
    3)
      local cmd=${words[2]}
      case "${cmd}" in
        a|add)
          _files
          ;;
        g|get)
          _pass_complete_entries
          ;;
      esac
      ;;
    4)
      local cmd=${words[2]}
      case "${cmd}" in
        a|add)
          _pass_complete_entries
          ;;
      esac
      ;;
  esac
}

unfunction $(basename $(print -P %x))
set -l PROG 'pass'

function __fish_pass-file_needs_command
  set -l cmd (commandline -opc)
  if [ (count $cmd) -eq 2 -a "$cmd[2]" = file ]
    return 0
  end
  return 1
end

function __fish_pass-file_arg_nr_of_command
  set -l cmd (commandline -opc)
  set -l expected_length (math 2 + $argv[1])
  if [ (count $cmd) -eq $expected_length -a "$cmd[2]" = "file" -a "$cmd[3]" = 
$argv[2]  ]
    return 0
  end
  return 1
end

complete -c $PROG -f -n '__fish_pass_needs_command' -a file -d 'Command: 
add/get file to/from password-store'

complete -c $PROG -f -n '__fish_pass-file_needs_command' -a add -d 'add file to 
password-store'
complete -c $PROG    -n '__fish_pass-file_arg_nr_of_command 1 a'
complete -c $PROG    -n '__fish_pass-file_arg_nr_of_command 1 add'
complete -c $PROG -f -n '__fish_pass-file_arg_nr_of_command 2 a'   -a 
"(__fish_pass_print_entries)"
complete -c $PROG -f -n '__fish_pass-file_arg_nr_of_command 2 add' -a 
"(__fish_pass_print_entries)"

complete -c $PROG -f -n '__fish_pass-file_needs_command' -a get -d 'get file 
from password-store'
complete -c $PROG -f -n '__fish_pass-file_arg_nr_of_command 1 g'   -a 
"(__fish_pass_print_entries)"
complete -c $PROG -f -n '__fish_pass-file_arg_nr_of_command 1 get' -a 
"(__fish_pass_print_entries)"

Reply via email to