threads / patch / 33656

patch, 2 partscompletion: zsh: nice stuff

Subject: [PATCH 0/2] completion: zsh: nice stuff

## tl;dr

5 messages between Apr 27, 2013 and Apr 27, 2013. Diffs are folded; open one to read it.

replies: 4people: 2as markdown or json

Felipe Contreras· Apr 27, 2013, 20:34 UTC · lore
Hi,

Our thin wrapper seems to be doing a good job of providing all the bash functionality, to zsh, however, we can do more.

This patch enables zsh-specific functionality to list the main commands, and general options. So we get:

  % git <tab>
  add       -- add file contents to the index
  bisect    -- find by binary search the change that introduced a bug
  branch    -- list, create, or delete branches
  checkout  -- checkout a branch or paths to the working tree
  clone     -- clone a repository into a new directory
  ...
And:
  % git --
  --bare      -- treat the repository as a bare repository
  --exec-path -- path to where your core git programs are ...
  --git-dir   -- set the path to the repository
  --help      -- prints the synopsis and a list of the ..
  --html-path -- print the path where gits HTML ...
  --info-path -- print the path where the Info files are ...
  --man-path  -- print the manpath (see `man(1)`) for the ...
And:
  % git --git-dir<tab>
Automatically adds the space, and then lists only directories.
Plus a lot of very specific argument idioms that are only possible in zsh.

In addition, you can complete commands that are not shown in the list, like 'git rerere'.

Plus, the user can configure the main output of commands:
  zstyle ':completion:*:*:git:*' tag-order '*'
Or:
  zstyle ':completion:*:*:git:*' tag-order 'alias-commands common-commands'
In short, pure awesomeness :)
Felipe Contreras (2):
  complete: zsh: trivial simplification
  complete: zsh: use zsh completion for the main cmd
 contrib/completion/git-completion.zsh | 134 ++++++++++++++++++++++++++++++++--
 1 file changed, 126 insertions(+), 8 deletions(-)
-- 
1.8.2.1.1031.g2ee5873
Felipe Contreras· Apr 27, 2013, 20:34 UTC · re: Felipe Contreras · lore

[PATCH 1/2] complete: zsh: trivial simplification

There should be no functional changes.

The only reason I wrapped this code around a sub-function is because zsh did the same in it's bashcompinit script in order to declare the special variable 'words' as hidden, but only in this context.

There's no need for that any more since we access __git_main directly, so 'words' is not modified, so there's no need for the sub-function.

In zsh mode the array indexes are different though.
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 contrib/completion/git-completion.zsh | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)
Show changes to contrib/completion/git-completion.zsh +8 −8
diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh
index cf8116d..93d8f42 100644
--- a/contrib/completion/git-completion.zsh
+++ b/contrib/completion/git-completion.zsh
@@ -72,14 +72,14 @@ __gitcomp_file ()
 _git ()
 {
 	local _ret=1
-	() {
-		emulate -L ksh
-		local cur cword prev
-		cur=${words[CURRENT-1]}
-		prev=${words[CURRENT-2]}
-		let cword=CURRENT-1
-		__${service}_main
-	}
+	local cur cword prev
+
+	cur=${words[CURRENT]}
+	prev=${words[CURRENT-1]}
+	let cword=CURRENT-1
+
+	emulate ksh -c __${service}_main
+
 	let _ret && _default -S '' && _ret=0
 	return _ret
 }
-- 
1.8.2.1.1031.g2ee5873
Felipe Contreras· Apr 27, 2013, 20:34 UTC · re: Felipe Contreras · lore

[PATCH 2/2] complete: zsh: use zsh completion for the main cmd

So that we can have a nice zsh completion output:

% git <tab> add -- add file contents to the index bisect -- find by binary search the change that introduced a bug branch -- list, create, or delete branches checkout -- checkout a branch or paths to the working tree clone -- clone a repository into a new directory commit -- record changes to the repository diff -- show changes between commits, commit and working tree, etc fetch -- download objects and refs from another repository grep -- print lines matching a pattern init -- create an empty Git repository or reinitialize an existing one log -- show commit logs merge -- join two or more development histories together mv -- move or rename a file, a directory, or a symlink pull -- fetch from and merge with another repository or a local branch push -- update remote refs along with associated objects rebase -- forward-port local commits to the updated upstream head reset -- reset current HEAD to the specified state rm -- remove files from the working tree and from the index show -- show various types of objects status -- show the working tree status tag -- create, list, delete or verify a tag object signed with GPG

And other niceties, like 'git --git-dir=<tab>' showing only directories.
For the rest, the bash completion stuff is still used.
Also, add my copyright, since this more than a thin wrapper.
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 contrib/completion/git-completion.zsh | 120 +++++++++++++++++++++++++++++++++-
 1 file changed, 119 insertions(+), 1 deletion(-)
Show changes to contrib/completion/git-completion.zsh +119 −1
diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh
index 93d8f42..49f0cb8 100644
--- a/contrib/completion/git-completion.zsh
+++ b/contrib/completion/git-completion.zsh
@@ -2,6 +2,8 @@
 
 # zsh completion wrapper for git
 #
+# Copyright (c) 2012-2013 Felipe Contreras <felipe.contreras@gmail.com>
+#
 # You need git's bash completion script installed somewhere, by default on the
 # same directory as this script.
 #
@@ -21,6 +23,9 @@ complete ()
 	return 0
 }
 
+zstyle -T ':completion:*:*:git:*' tag-order && \
+	zstyle ':completion:*:*:git:*' tag-order 'common-commands'
+
 zstyle -s ":completion:*:*:git:*" script script
 test -z "$script" && script="$(dirname ${funcsourcetrace[1]%:*})"/git-completion.bash
 ZSH_VERSION='' . "$script"
@@ -69,6 +74,115 @@ __gitcomp_file ()
 	compadd -Q -p "${2-}" -f -- ${=1} && _ret=0
 }
 
+__git_zsh_bash_func ()
+{
+	emulate -L ksh
+
+	local command=$1
+
+	local completion_func="_git_${command//-/_}"
+	declare -f $completion_func >/dev/null && $completion_func && return
+
+	local expansion=$(__git_aliased_command "$command")
+	if [ -n "$expansion" ]; then
+		completion_func="_git_${expansion//-/_}"
+		declare -f $completion_func >/dev/null && $completion_func
+	fi
+}
+
+__git_zsh_cmd_common ()
+{
+	local -a list
+	list=(
+	add:'add file contents to the index'
+	bisect:'find by binary search the change that introduced a bug'
+	branch:'list, create, or delete branches'
+	checkout:'checkout a branch or paths to the working tree'
+	clone:'clone a repository into a new directory'
+	commit:'record changes to the repository'
+	diff:'show changes between commits, commit and working tree, etc'
+	fetch:'download objects and refs from another repository'
+	grep:'print lines matching a pattern'
+	init:'create an empty Git repository or reinitialize an existing one'
+	log:'show commit logs'
+	merge:'join two or more development histories together'
+	mv:'move or rename a file, a directory, or a symlink'
+	pull:'fetch from and merge with another repository or a local branch'
+	push:'update remote refs along with associated objects'
+	rebase:'forward-port local commits to the updated upstream head'
+	reset:'reset current HEAD to the specified state'
+	rm:'remove files from the working tree and from the index'
+	show:'show various types of objects'
+	status:'show the working tree status'
+	tag:'create, list, delete or verify a tag object signed with GPG')
+	_describe -t common-commands 'common commands' list && _ret=0
+}
+
+__git_zsh_cmd_alias ()
+{
+	local -a list
+	list=(${${${(0)"$(git config -z --get-regexp '^alias\.')"}#alias.}%$'\n'*})
+	_describe -t alias-commands 'aliases' list $* && _ret=0
+}
+
+__git_zsh_cmd_all ()
+{
+	local -a list
+	emulate ksh -c __git_compute_all_commands
+	list=( ${=__git_all_commands} )
+	_describe -t all-commands 'all commands' list && _ret=0
+}
+
+__git_zsh_main ()
+{
+	local curcontext="$curcontext" state state_descr line
+	typeset -A opt_args
+	local -a orig_words
+
+	orig_words=( ${words[@]} )
+
+	_arguments -C \
+		'(-p --paginate --no-pager)'{-p,--paginate}'[pipe all output into ''less'']' \
+		'(-p --paginate)--no-pager[do not pipe git output into a pager]' \
+		'--git-dir=-[set the path to the repository]: :_directories' \
+		'--bare[treat the repository as a bare repository]' \
+		'(- :)--version[prints the git suite version]' \
+		'--exec-path=-[path to where your core git programs are installed]:: :_directories' \
+		'--html-path[print the path where git''s HTML documentation is installed]' \
+		'--info-path[print the path where the Info files are installed]' \
+		'--man-path[print the manpath (see `man(1)`) for the man pages]' \
+		'--work-tree=-[set the path to the working tree]: :_directories' \
+		'--namespace=-[set the git namespace]' \
+		'--no-replace-objects[do not use replacement refs to replace git objects]' \
+		'(- :)--help[prints the synopsis and a list of the most commonly used commands]: :->arg' \
+		'(-): :->command' \
+		'(-)*:: :->arg' && return
+
+	case $state in
+	(command)
+		_alternative \
+                         'alias-commands:alias:__git_zsh_cmd_alias' \
+                         'common-commands:common:__git_zsh_cmd_common' \
+                         'all-commands:all:__git_zsh_cmd_all' && _ret=0
+		;;
+	(arg)
+		local command="${words[1]}" __git_dir
+
+		if (( $+opt_args[--bare] )); then
+			__git_dir='.'
+		else
+			__git_dir=${opt_args[--git-dir]}
+		fi
+
+		(( $+opt_args[--help] )) && command='help'
+
+		words=( ${orig_words[@]} )
+
+		__git_zsh_bash_func $command
+		;;
+	esac
+}
+
 _git ()
 {
 	local _ret=1
@@ -78,7 +192,11 @@ _git ()
 	prev=${words[CURRENT-1]}
 	let cword=CURRENT-1
 
-	emulate ksh -c __${service}_main
+	if (( $+functions[__${service}_zsh_main] )); then
+		__${service}_zsh_main
+	else
+		emulate ksh -c __${service}_main
+	fi
 
 	let _ret && _default -S '' && _ret=0
 	return _ret
-- 
1.8.2.1.1031.g2ee5873
Junio C Hamano· Apr 27, 2013, 21:39 UTC · re: Felipe Contreras · lore

Re: [PATCH 2/2] complete: zsh: use zsh completion for the main cmd

Felipe Contreras <felipe.contreras@gmail.com> writes:
Show 25 quoted lines
> +__git_zsh_cmd_common ()
> +{
> +	local -a list
> +	list=(
> +	add:'add file contents to the index'
> +	bisect:'find by binary search the change that introduced a bug'
> +	branch:'list, create, or delete branches'
> +	checkout:'checkout a branch or paths to the working tree'
> +	clone:'clone a repository into a new directory'
> +	commit:'record changes to the repository'
> +	diff:'show changes between commits, commit and working tree, etc'
> +	fetch:'download objects and refs from another repository'
> +	grep:'print lines matching a pattern'
> +	init:'create an empty Git repository or reinitialize an existing one'
> +	log:'show commit logs'
> +	merge:'join two or more development histories together'
> +	mv:'move or rename a file, a directory, or a symlink'
> +	pull:'fetch from and merge with another repository or a local branch'
> +	push:'update remote refs along with associated objects'
> +	rebase:'forward-port local commits to the updated upstream head'
> +	reset:'reset current HEAD to the specified state'
> +	rm:'remove files from the working tree and from the index'
> +	show:'show various types of objects'
> +	status:'show the working tree status'
> +	tag:'create, list, delete or verify a tag object signed with GPG')

Maintaining this list feels somewhat painful. Isn't this something we can help by tweaking "git help"?

Felipe Contreras· Apr 27, 2013, 21:53 UTC · re: Junio C Hamano · lore

Re: [PATCH 2/2] complete: zsh: use zsh completion for the main cmd

On Sat, Apr 27, 2013 at 4:39 PM, Junio C Hamano <gitster@pobox.com> wrote:
Show 30 quoted lines
> Felipe Contreras <felipe.contreras@gmail.com> writes:
>
>> +__git_zsh_cmd_common ()
>> +{
>> +     local -a list
>> +     list=(
>> +     add:'add file contents to the index'
>> +     bisect:'find by binary search the change that introduced a bug'
>> +     branch:'list, create, or delete branches'
>> +     checkout:'checkout a branch or paths to the working tree'
>> +     clone:'clone a repository into a new directory'
>> +     commit:'record changes to the repository'
>> +     diff:'show changes between commits, commit and working tree, etc'
>> +     fetch:'download objects and refs from another repository'
>> +     grep:'print lines matching a pattern'
>> +     init:'create an empty Git repository or reinitialize an existing one'
>> +     log:'show commit logs'
>> +     merge:'join two or more development histories together'
>> +     mv:'move or rename a file, a directory, or a symlink'
>> +     pull:'fetch from and merge with another repository or a local branch'
>> +     push:'update remote refs along with associated objects'
>> +     rebase:'forward-port local commits to the updated upstream head'
>> +     reset:'reset current HEAD to the specified state'
>> +     rm:'remove files from the working tree and from the index'
>> +     show:'show various types of objects'
>> +     status:'show the working tree status'
>> +     tag:'create, list, delete or verify a tag object signed with GPG')
>
> Maintaining this list feels somewhat painful.  Isn't this something
> we can help by tweaking "git help"?

Yes, that's the way Bazaar does it (bzr shell-complete), but for now this does the trick.

Also, I've been carrying around the patches from Stephen Boyd that add --dump-raw-long-options. I think it would be better if somehow 'git help' learned how to throw this output for both commands and arguments, and then use the first part for bash, or specify bash format somehow. Of course, we would need to add support for descriptions in the option parser.

It's the big world-domination plan for completions, but I haven't had time to work on that.

Cheers.
-- 
Felipe Contreras

← back to recent threads