[PATCHv2] completion: make compatible with zsh

Subsystems: the rest

5 messages, 4 authors, 2016-06-15 · open the first message on its own page

[PATCHv2] completion: make compatible with zsh

From: Mark Lodato <hidden>
Date: 2016-06-15 22:49:23

Modify git-completion.bash so that it also works with zsh when using
bashcompinit.  In particular:

declare -F
    Zsh doesn't have the same 'declare -F' as bash, but 'declare -f'
    is the same, and it works just as well for our purposes.

${var:2}
    Zsh does not implement ${var:2} to skip the first 2 characters, but
    ${var#??} works in both shells to replace the first 2 characters
    with nothing.  Thanks to Jonathan Nieder for the suggestion.

for (( n=1; "$n" ... ))
    Zsh does not allow "$var" in arithmetic loops.  Instead, pre-compute
    the endpoint and use the variables without $'s or quotes.

shopt
    Zsh uses 'setopt', which has a different syntax than 'shopt'.  Since
    'shopt' is used infrequently in git-completion, we provide
    a bare-bones emulation.

emulate -L bash
KSH_TYPESET
    Zsh offers bash emulation, which turns on a set of features to
    closely resemble bash. In particular, this enables SH_WORDSPLIT,
    which splits scalar variables on word boundaries in 'for' loops.
    We also need to set KSH_TYPESET, to fix "local var=$(echo foo bar)"
    issues.

The last set of options are turned on only in _git and _gitk.  Some of
the sub-functions may not work correctly if called directly.

Signed-off-by: Mark Lodato <redacted>
---

Those on the CC list either responded to the original patch or are the authors
of the lines affected by this patch:
    - Andrew wrote __git_ps1_show_upstream().
    - SZEDER wrote the part using 'declare -F'.

 contrib/completion/git-completion.bash |   50 +++++++++++++++++++++++++++++--
 1 files changed, 46 insertions(+), 4 deletions(-)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 6756990..6a7aae6 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -21,6 +21,11 @@
 #    2) Added the following line to your .bashrc:
 #        source ~/.git-completion.sh
 #
+#       Or, add the following lines to your .zshrc:
+#        autoload bashcompinit
+#        bashcompinit
+#        source ~/.git-completion.sh
+#
 #    3) Consider changing your PS1 to also show the current branch:
 #        PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
 #
@@ -138,11 +143,12 @@ __git_ps1_show_upstream ()
 		# get the upstream from the "git-svn-id: ..." in a commit message
 		# (git-svn uses essentially the same procedure internally)
 		local svn_upstream=($(git log --first-parent -1 \
-					--grep="^git-svn-id: \(${svn_url_pattern:2}\)" 2>/dev/null))
+					--grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null))
 		if [[ 0 -ne ${#svn_upstream[@]} ]]; then
 			svn_upstream=${svn_upstream[ ${#svn_upstream[@]} - 2 ]}
 			svn_upstream=${svn_upstream%@*}
-			for ((n=1; "$n" <= "${#svn_remote[@]}"; ++n)); do
+			local n_stop="${#svn_remote[@]}"
+			for ((n=1; n <= n_stop; ++n)); do
 				svn_upstream=${svn_upstream#${svn_remote[$n]}}
 			done
 
@@ -2339,6 +2345,11 @@ _git ()
 {
 	local i c=1 command __git_dir
 
+	if [[ -n $ZSH_VERSION ]]; then
+		emulate -L bash
+		setopt KSH_TYPESET
+	fi
+
 	while [ $c -lt $COMP_CWORD ]; do
 		i="${COMP_WORDS[c]}"
 		case "$i" in
@@ -2372,17 +2383,22 @@ _git ()
 	fi
 
 	local completion_func="_git_${command//-/_}"
-	declare -F $completion_func >/dev/null && $completion_func && return
+	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
+		declare -f $completion_func >/dev/null && $completion_func
 	fi
 }
 
 _gitk ()
 {
+	if [[ -n $ZSH_VERSION ]]; then
+		emulate -L bash
+		setopt KSH_TYPESET
+	fi
+
 	__git_has_doubledash && return
 
 	local cur="${COMP_WORDS[COMP_CWORD]}"
@@ -2417,3 +2433,29 @@ if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
 complete -o bashdefault -o default -o nospace -F _git git.exe 2>/dev/null \
 	|| complete -o default -o nospace -F _git git.exe
 fi
+
+if [[ -z $ZSH_VERSION ]]; then
+	shopt () {
+		local option
+		if [ $# -ne 2 ]; then
+			echo "USAGE: $0 (-q|-s|-u) <option>" >&2
+			return 1
+		fi
+		case "$2" in
+		nullglob)
+			option="$2"
+			;;
+		*)
+			echo "$0: invalid option: $2" >&2
+			return 1
+		esac
+		case "$1" in
+		-q)	setopt | grep -q "$option" ;;
+		-u)	unsetopt "$option" ;;
+		-s)	setopt "$option" ;;
+		*)
+			echo "$0: invalid flag: $1" >&2
+			return 1
+		esac
+	}
+fi
-- 
1.7.2.2

Re: [PATCHv2] completion: make compatible with zsh

From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2016-06-15 22:49:24

On Fri, Aug 27, 2010 at 02:45, Mark Lodato [off-list ref] wrote:
Modify git-completion.bash so that it also works with zsh when using
bashcompinit.  In particular:
FWIW:

Reviewed-by: Ævar Arnfjörð Bjarmason <redacted>

Re: [PATCHv2] completion: make compatible with zsh

From: SZEDER Gábor <hidden>
Date: 2016-06-15 22:49:24

Hi,


On Thu, Aug 26, 2010 at 10:45:56PM -0400, Mark Lodato wrote:
Modify git-completion.bash so that it also works with zsh when using
bashcompinit.  In particular:

declare -F
    Zsh doesn't have the same 'declare -F' as bash, but 'declare -f'
    is the same, and it works just as well for our purposes.
quoted hunk
@@ -2372,17 +2383,22 @@ _git ()
 	fi
 
 	local completion_func="_git_${command//-/_}"
-	declare -F $completion_func >/dev/null && $completion_func && return
+	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
+		declare -f $completion_func >/dev/null && $completion_func
 	fi
 }
I chose -F back then because it does not print the definition of the
completion function, so there is less stuff to throw away immediately.
However, the run-time difference seems to be barely measureable (i.e.
'time declare -f _git_config >/dev/null' vs. 'declare -F _git_config
 >/dev/null' sometimes shows 0.001s difference), so I'm fine with this
change.

quoted hunk
@@ -2417,3 +2433,29 @@ if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
 complete -o bashdefault -o default -o nospace -F _git git.exe 2>/dev/null \
 	|| complete -o default -o nospace -F _git git.exe
 fi
+
+if [[ -z $ZSH_VERSION ]]; then
-z?  I think you wanted to use -n here, like at the other places.

Nit: why "if [[ ... ]]"?  FWIW "if [ ... ]" would be enough.
+	shopt () {
+		local option
+		if [ $# -ne 2 ]; then
+			echo "USAGE: $0 (-q|-s|-u) <option>" >&2
+			return 1
+		fi
+		case "$2" in
+		nullglob)
+			option="$2"
+			;;
+		*)
+			echo "$0: invalid option: $2" >&2
+			return 1
+		esac
+		case "$1" in
+		-q)	setopt | grep -q "$option" ;;
+		-u)	unsetopt "$option" ;;
+		-s)	setopt "$option" ;;
+		*)
+			echo "$0: invalid flag: $1" >&2
+			return 1
+		esac
+	}
+fi
-- 
1.7.2.2

Re: [PATCHv2] completion: make compatible with zsh

From: Mark Lodato <hidden>
Date: 2016-06-15 22:49:25

2010/8/30 SZEDER Gábor [off-list ref]
On Thu, Aug 26, 2010 at 10:45:56PM -0400, Mark Lodato wrote:
quoted
@@ -2417,3 +2433,29 @@ if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
 complete -o bashdefault -o default -o nospace -F _git git.exe 2>/dev/null \
      || complete -o default -o nospace -F _git git.exe
 fi
+
+if [[ -z $ZSH_VERSION ]]; then
-z?  I think you wanted to use -n here, like at the other places.
Oh, yes, sorry.  This was a mistake.  Thanks for catching it.

Nit: why "if [[ ... ]]"?  FWIW "if [ ... ]" would be enough.
Because you don't need to quote variables with [[ ... ]] --- e.g. [
$lines = 0 ] fails, though in this case this feature does not matter
--- and because [[ ... ]] is faster.
Bash 4.1.5:
time (for (( i = 0; i < 200000; i++ )); do [ -z $foo ]; done)
real    0m3.430s
user    0m3.240s
sys     0m0.180s
time (for (( i = 0; i < 200000; i++ )); do [[ -z $foo ]]; done)
real    0m2.219s
user    0m2.090s
sys     0m0.100s

Zsh 4.3.10:
time (for (( i = 0; i < 2000000; i++ )); do [ -z $foo ]; done)
(; for ((i = 0; i < 2000000; i++ )) do; [ -z $foo ]; done; )  13.56s
user 1.64s system 99% cpu 15.327 total
time (for (( i = 0; i < 2000000; i++ )); do [[ -z $foo ]]; done)
(; for ((i = 0; i < 2000000; i++ )) do; [[ -z $foo ]]; done; )  4.62s
user 0.01s system 99% cpu 4.644 total

Is there a reason to prefer [ ... ] ?

RE: [PATCHv2] completion: make compatible with zsh

From: Peter Kjellerstedt <hidden>
Date: 2016-06-15 22:49:25

quoted hunk
-----Original Message-----
From: git-owner@vger.kernel.org [mailto:git-owner@vger.kernel.org] On
Behalf Of Mark Lodato
Sent: den 31 augusti 2010 02:56
To: SZEDER Gábor
Cc: Shawn O. Pearce; git@vger.kernel.org; avarab@gmail.com; Jonathan
Nieder; Andrew Sayers
Subject: Re: [PATCHv2] completion: make compatible with zsh

2010/8/30 SZEDER Gábor [off-list ref]
quoted
On Thu, Aug 26, 2010 at 10:45:56PM -0400, Mark Lodato wrote:
quoted
@@ -2417,3 +2433,29 @@ if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
 complete -o bashdefault -o default -o nospace -F _git git.exe 2>/dev/null \
      || complete -o default -o nospace -F _git git.exe
 fi
+
+if [[ -z $ZSH_VERSION ]]; then
-z?  I think you wanted to use -n here, like at the other places.
Oh, yes, sorry.  This was a mistake.  Thanks for catching it.

quoted
Nit: why "if [[ ... ]]"?  FWIW "if [ ... ]" would be enough.
Because you don't need to quote variables with [[ ... ]] --- e.g. [
$lines = 0 ] fails, though in this case this feature does not matter
--- and because [[ ... ]] is faster.
Bash 4.1.5:
quoted
time (for (( i = 0; i < 200000; i++ )); do [ -z $foo ]; done)
real    0m3.430s
user    0m3.240s
sys     0m0.180s
quoted
time (for (( i = 0; i < 200000; i++ )); do [[ -z $foo ]]; done)
real    0m2.219s
user    0m2.090s
sys     0m0.100s

Zsh 4.3.10:
quoted
time (for (( i = 0; i < 2000000; i++ )); do [ -z $foo ]; done)
(; for ((i = 0; i < 2000000; i++ )) do; [ -z $foo ]; done; )  13.56s
user 1.64s system 99% cpu 15.327 total
quoted
time (for (( i = 0; i < 2000000; i++ )); do [[ -z $foo ]]; done)
(; for ((i = 0; i < 2000000; i++ )) do; [[ -z $foo ]]; done; )  4.62s
user 0.01s system 99% cpu 4.644 total

Is there a reason to prefer [ ... ] ?
The [ command is defined by POSIX, while [[ is an extension to the 
shell language implemented by bash (and zsh). Thus the [[ construct 
would fail on a stricter POSIX implementation of the shell language, 
e.g., as implemented by dash. Thus to be POSIX compliant (which is
required for shell scripts installed by a multi system supporting 
application like git), the [[ construct cannot be used. 

Of course, this does not really apply to a script explicitly written 
for bash, but that is the common reason to prefer [ over [[ in shell 
scripts.

//Peter
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help