git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Peter van der Does <peter@avirtualhome.com>
To: "SZEDER Gábor" <szeder@ira.uka.de>
Cc: Jonathan Nieder <jrnieder@gmail.com>,
	"Shawn O. Pearce" <spearce@spearce.org>,
	git@vger.kernel.org, Marc Branchaud <marcnarc@xiplink.com>,
	Brian Gernhardt <brian@gernhardtsoftware.com>,
	Kevin Ballard <kevin@sb.org>,
	Mathias Lafeldt <misfire@debugon.org>
Subject: Re: [completion] Request: Include remote heads as push targets
Date: Sat, 23 Oct 2010 20:07:39 -0400	[thread overview]
Message-ID: <20101023200739.28b6eb1e@montecarlo.grandprix.int> (raw)
In-Reply-To: <20101023130434.GA29386@neumann>

On Sat, 23 Oct 2010 15:04:34 +0200
SZEDER Gábor <szeder@ira.uka.de> wrote:


> Here is a proof of concept patch to use that function instead of
> ${COMP_WORDS[COMP_CWORD]} in two places.  The second hunk fixes the
> completion of pretty aliases for 'git log --pretty='.  The first hunk
> seems to fix Marc's issue with the completion of remotes after 'git
> push origin HEAD:', but I haven't thought this one through (there's a
> lot going on with scanning the previous words on the command line and
> such, so it might actually break something else).  Both fixes seem to
> work under bash 4 and 3.2.
> 
> diff --git a/contrib/completion/git-completion.bash
> b/contrib/completion/git-completion.bash index f83f019..5608e9b 100755
> --- a/contrib/completion/git-completion.bash
> +++ b/contrib/completion/git-completion.bash
> @@ -551,7 +551,8 @@ __git_complete_revlist ()
>  __git_complete_remote_or_refspec ()
>  {
>  	local cmd="${COMP_WORDS[1]}"
> -	local cur="${COMP_WORDS[COMP_CWORD]}"
> +	local cur
> +	_get_comp_words_by_ref -n ':' cur
>  	local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0
>  	while [ $c -lt $COMP_CWORD ]; do
>  		i="${COMP_WORDS[c]}"
> @@ -1360,7 +1361,8 @@ _git_log ()
>  {
>  	__git_has_doubledash && return
>  
> -	local cur="${COMP_WORDS[COMP_CWORD]}"
> +	local cur
> +	_get_comp_words_by_ref -n '=' cur
>  	local g="$(git rev-parse --git-dir 2>/dev/null)"
>  	local merge=""
>  	if [ -f "$g/MERGE_HEAD" ]; then
> 
> This patch assumes that you use fairly recent bash-completion, because
> _get_comp_words_by_ref() was first included in bash-completion v1.2,
> which was released just this summer.
> 
> However, git completion is currently a standalone completion script,
> i.e. to use it you need only bash, git-completion.bash, and nothing
> else.  If we start to use _get_comp_words_by_ref() directly, as in the
> PoC patch above, then git completion will inherently depend on
> bash-completion, too.  This could be considered as a regression.
> 
> Alternatively, we could just copy the necessary functions from
> bash-completion to git-completion.bash (with the name changed, of
> course, e.g. to __git_get_comp_words_by_ref()), keeping git completion
> standalone but still getting the benefits of this function, and
> getting these bash 4 vs. 3 issues fixed.
> 
> Thoughts?
> 

Instead of using [code]_get_comp_words_by_ref -n '=' cur[/code] you can
use [code]local cur=`_get_cword "="`[/code].

To keep git completion standalone we need to, like Gábor mentioned, add
the necessary functions, but we don't have to rename them. There is
an option to check if a function exists. I've changed the entire git
completion script and hopefully covered all options. From my tests it
works on bash 4.

To give an idea of what the change is, here's part of the entire diff.

diff --git a/contrib/completion/git-completion.bash
b/contrib/completion/git-completion.bash index f83f019..a2c0589 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -71,12 +71,159 @@
 #
 #       git@vger.kernel.org
 #
+# Updated for Bash 4.0
 
 case "$COMP_WORDBREAKS" in
 *:*) : great ;;
 *)   COMP_WORDBREAKS="$COMP_WORDBREAKS:"
 esac
 
+# If the function _get_cword does not exists, we can assume the
+# bash_completion script isn't loaded and therefor we're defining the
+# necessary functions ourselves.
+if ! type _get_cword &> /dev/null ; then
+	# features supported by bash 4.0 and higher
+	if [ ${BASH_VERSINFO[0]} -gt 3 ]; then
+	    declare -r git_bash4=$BASH_VERSION 2>/dev/null || :
+	fi
+
+	# Get the word to complete.
+	# This is nicer than ${COMP_WORDS[$COMP_CWORD]}, since it
handles cases
+	# where the user is completing in the middle of a word.
+	# (For example, if the line is "ls foobar",
+	# and the cursor is here -------->   ^
+	# it will complete just "foo", not "foobar", which is what the
user wants.)
+	# @param $1 string  (optional) Characters out of
$COMP_WORDBREAKS which should
+	#     NOT be considered word breaks. This is useful for things
like scp where
+	#     we want to return host:path and not only path.
+	#     NOTE: This parameter only applies to bash-4.
+	_get_cword()
+	{
+    	if [ -n "$git_bash4" ] ; then
+        	__get_cword4 "$@"
+    	else
+        	__get_cword3
+    	fi
+	} # _get_cword()
+
+
+	# Get the word to complete on bash-3, where words are not
broken by
+	# COMP_WORDBREAKS characters and the COMP_CWORD variables look
like this, for
+	# example:
+	#
+	#     $ a b:c<TAB>
+	#     COMP_CWORD: 1
+	#     COMP_CWORDS:
+	#     0: a
+	#     1: b:c
+	#
+	# See also:
+	# _get_cword, main routine
+	# __get_cword4, bash-4 variant
+	#
+	__get_cword3()
+	{
+	    if [[ "${#COMP_WORDS[COMP_CWORD]}" -eq 0 ]] ||
[[ "$COMP_POINT" == "${#COMP_LINE}" ]]; then
+        	printf "%s" "${COMP_WORDS[COMP_CWORD]}"
+    	else
+	        local i
+        	local cur="$COMP_LINE"
+        	local index="$COMP_POINT"
+        	for (( i = 0; i <= COMP_CWORD; ++i )); do
+	            while [[
+                	# Current COMP_WORD fits in $cur?
+                	"${#cur}" -ge ${#COMP_WORDS[i]} &&
+                	# $cur doesn't match COMP_WORD?
+                	"${cur:0:${#COMP_WORDS[i]}}" !=
"${COMP_WORDS[i]}"
+                	]]; do
+                	# Strip first character
+                	cur="${cur:1}"
+                	# Decrease cursor position
+                	index="$(( index - 1 ))"
+            	done
+	
+            	# Does found COMP_WORD matches COMP_CWORD?
+            	if [[ "$i" -lt "$COMP_CWORD" ]]; then
+	                # No, COMP_CWORD lies further;
+                	local old_size="${#cur}"
+                	cur="${cur#${COMP_WORDS[i]}}"
+                	local new_size="${#cur}"
+                	index="$(( index - old_size + new_size ))"
+            	fi
+        	done
+
+	        if [[ "${COMP_WORDS[COMP_CWORD]:0:${#cur}}" !=
"$cur" ]]; then
+            	# We messed up! At least return the whole word so
things
+            	# keep working
+            	printf "%s" "${COMP_WORDS[COMP_CWORD]}"
+        	else
+	            printf "%s" "${cur:0:$index}"
+        	fi
+    	fi
+	} # __get_cword3()
+
+
+	# Get the word to complete on bash-4, where words are splitted
by
+	# COMP_WORDBREAKS characters (default is " \t\n\"'><=;|&(:")
and the COMP_CWORD
+	# variables look like this, for example:
+	#
+	#     $ a b:c<TAB>
+	#     COMP_CWORD: 3
+	#     COMP_CWORDS:
+	#     0: a
+	#     1: b
+	#     2: :
+	#     3: c
+	#
+	# @oaram $1 string
+	# $1 string  (optional) Characters out of $COMP_WORDBREAKS
which should
+	#     NOT be considered word breaks. This is useful for things
like scp where
+	#     we want to return host:path and not only path.
+	# See also:
+	# _get_cword, main routine
+	# __get_cword3, bash-3 variant
+	#
+	__get_cword4()
+	{
+	    local i
+	    local LC_CTYPE=C
+	    local WORDBREAKS=$COMP_WORDBREAKS
+	    # Strip single quote (') and double quote (") from
WORDBREAKS to
+	    # workaround a bug in bash-4.0, where quoted words are
split
+	    # unintended, see:
+	    #
http://www.mail-archive.com/bug-bash@gnu.org/msg06095.html
+	    # This fixes simple quoting (e.g. $ a "b<TAB> returns "b
instead of b)
+	    # but still fails quoted spaces (e.g. $ a "b c<TAB>
returns c instead
+	    # of "b c).
+	    WORDBREAKS=${WORDBREAKS//\"/}
+	    WORDBREAKS=${WORDBREAKS//\'/}
+	    if [ -n "$1" ]; then
+        	for (( i=0; i<${#1}; ++i )); do
+	            local char=${1:$i:1}
+            	WORDBREAKS=${WORDBREAKS//$char/}
+        	done
+    	fi
+    	local cur=${COMP_LINE:0:$COMP_POINT}
+    	local tmp=$cur
+    	local word_start=`expr "$tmp" : '.*['"$WORDBREAKS"']'`
+    	while [ "$word_start" -ge 2 ]; do
+	        # Get character before $word_start
+        	local char=${cur:$(( $word_start - 2 )):1}
+        	# If the WORDBREAK character isn't escaped, exit loop
+        	if [ "$char" != "\\" ]; then
+	            break
+        	fi
+        	# The WORDBREAK character is escaped;
+        	# Recalculate $word_start
+        	tmp=${COMP_LINE:0:$(( $word_start - 2 ))}
+        	word_start=`expr "$tmp" : '.*['"$WORDBREAKS"']'`
+    	done
+
+	    cur=${cur:$word_start}
+	    printf "%s" "$cur"
+	} # __get_cword4()
+fi
+
@@ -551,7 +698,7 @@ __git_complete_revlist ()
 __git_complete_remote_or_refspec ()
 {
 	local cmd="${COMP_WORDS[1]}"
-	local cur="${COMP_WORDS[COMP_CWORD]}"
+	local cur=`_get_cword ":"`
 	local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0
 	while [ $c -lt $COMP_CWORD ]; do
 		i="${COMP_WORDS[c]}"
@@ -1360,7 +1508,7 @@ _git_log ()
 {
 	__git_has_doubledash && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
+	local cur=`_get_cword "="`
 	local g="$(git rev-parse --git-dir 2>/dev/null)"
 	local merge=""
 	if [ -f "$g/MERGE_HEAD" ]; then
@@ -1419,7 +1567,7 @@ _git_merge ()
 {
 	__git_complete_strategy && return
 
-	local cur="${COMP_WORDS[COMP_CWORD]}"
+	local cur=`_get_cword`
 	case "$cur" in
 	--*)
 		__gitcomp "$__git_merge_options"


I just need someone to test the new script on Bash 3. If somebody is
willing to test, drop me an private email and I can send the new script.

Peter

-- 
GPG key: E77E8E98

IRC: Ganseki on irc.freenode.net
Twitter: @petervanderdoes

WordPress Plugin Developer
Blog: http://blog.avirtualhome.com
Forums: http://forums.avirtualhome.com
Twitter: @avhsoftware

  reply	other threads:[~2010-10-24  0:08 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-10-21 15:37 [completion] Request: Include remote heads as push targets Marc Branchaud
2010-10-21 16:03 ` Marc Branchaud
2010-10-21 19:10   ` Jonathan Nieder
2010-10-22  1:08     ` Peter van der Does
2010-10-22  1:11       ` Kevin Ballard
2010-10-22 14:50       ` Marc Branchaud
2010-10-23 13:04       ` SZEDER Gábor
2010-10-24  0:07         ` Peter van der Does [this message]
2010-10-24 11:23           ` SZEDER Gábor
2010-10-24 16:28             ` Peter van der Does

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20101023200739.28b6eb1e@montecarlo.grandprix.int \
    --to=peter@avirtualhome.com \
    --cc=brian@gernhardtsoftware.com \
    --cc=git@vger.kernel.org \
    --cc=jrnieder@gmail.com \
    --cc=kevin@sb.org \
    --cc=marcnarc@xiplink.com \
    --cc=misfire@debugon.org \
    --cc=spearce@spearce.org \
    --cc=szeder@ira.uka.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).