Skip to contentSkip to conversation

completion: exclude previous file arguments in Zsh

PATCH In review Fionn via GitGitGadget 5 participants 5 messages Oct 3 – Oct 5

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 completion: exclude previous file arguments in Zsh

From: Fionn Fitzmaurice <[email protected]>

When using the _git completion function bundled with Zsh
(https://sf.net/p/zsh/code/ci/master/tree/Completion/Unix/Command/_git),
duplicate files in an argument list will not be offered as completion candidates.

For example, suppose we have untracked files aa and ab (only). Then with the Zsh completion,

git add aa a<tab>

would not offer both aa and ab as completion candidates, rather it would complete ab only.

This behaviour is not present in git-completion.zsh shipped with Git, which does not deduplicate arguments.

We can get this with minor changes, however. Here we introduce an array __git_file_exclude which we populate with existing arguments and then tell compadd to exclude them, which closely matches the Zsh _git completion behaviour (as well as common programs such as rm).

---
completion: exclude previous file arguments in Zsh

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2216%2Ffionn%2Fzsh-completion-exclude-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2216/fionn/zsh-completion-exclude-v1
Pull-Request: https://github.com/git/git/pull/2216

 contrib/completion/git-completion.zsh | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
contrib/completion/git-completion.zsh +4 -1
index d5c526665b..26444923c1 100644
@@ -117,7 +117,7 @@ __gitcomp_file ()
 	emulate -L zsh
 
 	compset -P '*[=:]'
-	compadd -f -p "${2-}" -- ${(f)1} && _ret=0
+	compadd -f -p "${2-}" -F __git_file_exclude -- ${(f)1} && _ret=0
 }
 
 __gitcomp_direct_append ()
@@ -284,6 +284,8 @@ __git_zsh_main ()
 
 		(( $+opt_args[--help] )) && command='help'
 
+		__git_file_exclude=(${words[2,-1]:#${words[CURRENT]}})
+
 		words=( ${orig_words[@]} )
 
 		__git_zsh_bash_func $command
@@ -296,6 +298,7 @@ _git ()
 	local _ret=1
 	local cur cword prev
 	local __git_repo_path
+	local -a __git_file_exclude
 
 	cur=${words[CURRENT]}
 	prev=${words[CURRENT-1]}

base-commit: c46c1e37724f0478939de636ab8ea5a89086d532

Signature · 1 line
gitgitgadget
#1 lore raw
Quoted 2 lines · Fionn via GitGitGadget

We can get this with minor changes, however. Here we introduce an array __git_file_exclude which we populate with existing arguments and then

"existing arguments" of what?

contrib/completion/git-completion.zsh __git_zsh_main () · line 287

Quoted 9 lines

tell compadd to exclude them, which closely matches the Zsh _git completion behaviour (as well as common programs such as rm).

contrib/completion/git-completion.zsh +1 -0
@@ -284,6 +284,8 @@ __git_zsh_main ()
 
 		(( $+opt_args[--help] )) && command='help'
 
+		__git_file_exclude=(${words[2,-1]:#${words[CURRENT]}})

I don't do Zsh, but that 2 as index looks suspicious.

What will be excluded in the following command line:

git -C dir -C subdir -c foo.bar=baz add file1 file2 <TAB>

I think we should exclude only those arguments that come after the git command, in this case after "add", i.e. "file1" and "file2", but I suspect that everything starting with "dir" will get excluded.

Quoted 4 lines
contrib/completion/git-completion.zsh +1 -0
+
 		words=( ${orig_words[@]} )
 
 		__git_zsh_bash_func $command
#2 lore raw
Quoted 1 line

"existing arguments" of what?

Of a subcommand that takes paths.

Quoted 1 line

I don't do Zsh, but that 2 as index looks suspicious.

Fair to be suspicious. word[1] is the subcommand (e.g. "add"), so we index from 2 on in order to skip it. This is independent of where the subcommand appears in the entire command line.

Quoted 7 lines

What will be excluded in the following command line:

git -C dir -C subdir -c foo.bar=baz add file1 file2 <TAB>

I think we should exclude only those arguments that come after the git command, in this case after "add", i.e. "file1" and "file2", but I suspect that everything starting with "dir" will get excluded.

In my testing this works correctly (i.e. file1 and file2 are not offered as completion candidates any more, but if e.g. dir or foo exist in the subdirectory, they would be offered).

I've been dogfooding this for about 8 months on and off. Occasionally completion candidates I'd hope would be excluded are present, but this is because the fallback completion bypasses __gitcomp_file, which is a separate issue. I have not yet encountered completion candidates being unexpectedly excluded.

An easy way to test this is to link git-completion.zsh to _git and then add

fpath=(/path/to/directory/containing/_git/file $fpath[@])

to ~/.zshrc, or similar.

#3 lore raw
In reply to SZEDER Gábor
Quoted 7 lines · SZEDER Gábor

What will be excluded in the following command line:

git -C dir -C subdir -c foo.bar=baz add file1 file2 <TAB>

I think we should exclude only those arguments that come after the git command, in this case after "add", i.e. "file1" and "file2", but I suspect that everything starting with "dir" will get excluded.

I was writing the same message when I saw yours.

If you had a file called 'add' in the working tree and then typed "a<TAB>" to complete, is 'add' offered together with other files whose name begins with 'a'?

#4 lore raw
Quoted 3 lines

If you had a file called 'add' in the working tree and then typed "a<TAB>" to complete, is 'add' offered together with other files whose name begins with 'a'?

It is indeed.