Patchwork [2,of,2,RFC] completion: use debugpathcomplete in bash_completion

login
register
mail settings
Submitter Bryan O'Sullivan
Date March 13, 2013, 9:42 p.m.
Message ID <27a63d55bcc1300b0775.1363210974@australite.local>
Download mbox | patch
Permalink /patch/1125/
State Superseded
Headers show

Comments

Bryan O'Sullivan - March 13, 2013, 9:42 p.m.
# HG changeset patch
# User Bryan O'Sullivan <bryano@fb.com>
# Date 1363210964 25200
# Node ID 27a63d55bcc1300b077537ef937c1e6fbd61a83e
# Parent  8d53d4c8d0e5b0b59fc8dc1adb51802ec621975f
completion: use debugpathcomplete in bash_completion

The current bash_completion code is very slow in a large working
directory. It uses "hg status" to generate possibly matching files,
which checks the status of every file. We usually don't care about
status when completing, so that cost is very high.

Switching to the new debugpathcomplete command does not check the
status of files, so offers much better performance.

Note that we also change some existing bash_completion behaviour.

For instance, the existing behaviour of completion for "hg remove"
is weird and wrong: we only complete files that have already been
deleted by hand ("hg status -d"), whereas we should complete only
files in the dirstate's 'n' (normal or modified) and 'm' (merge)
states.

There is a tradeoff, however: since we don't check the status of
files, we can now produce *more* completions than necessary for
cases where we'd like to complete only with modified files (e.g.
commit, diff). I'm not sure this is the right tradeoff, but it
seems much better than being terribly slow.
Bryan O'Sullivan - March 13, 2013, 10 p.m.
On Wed, Mar 13, 2013 at 2:42 PM, Bryan O'Sullivan <bos@serpentine.com>wrote:

> There is a tradeoff, however: since we don't check the status of
> files, we can now produce *more* completions than necessary for
> cases where we'd like to complete only with modified files (e.g.
> commit, diff). I'm not sure this is the right tradeoff, but it
> seems much better than being terribly slow.
>

To expand on this:

Suppose you want to commit a changed file from the root of your repo, so
you type "hg commit m<TAB>".

Old behaviour: all modified names under the working directory or its
children that begin with "m" will be completed, even if they're several
levels deep in the hierarchy.

Changed behaviour: all tracked names *only* under the current directory
that begin with "m" will be completed, even if they are files that are not
modified, or directories that contain files that are not modified. You
might have to hit tab a few times to get down several levels deep.

I am not thrilled by this, even though it's basically harmless. On the
other hand, it matches how the shell normally behaves, and the old
behaviour of actually running status is crazytown unacceptably slow in even
a moderately large repo.

Patch

diff --git a/contrib/bash_completion b/contrib/bash_completion
--- a/contrib/bash_completion
+++ b/contrib/bash_completion
@@ -80,6 +80,14 @@  shopt -s extglob
     done
 }
 
+_hg_debugpathcomplete()
+{
+    local files="$(_hg_cmd debugpathcomplete $1 "$cur")"
+    local IFS=$'\n'
+    compopt -o filenames 2>/dev/null
+    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur"))
+}
+
 _hg_status()
 {
     local files="$(_hg_cmd status -n$1 .)"
@@ -252,19 +260,23 @@  shopt -s extglob
 	    _hg_labels
 	;;
 	commit|record)
-	    _hg_status "mar"
+	    # it is harmless to complete unmodified files
+	    _hg_debugpathcomplete
 	;;
 	remove)
-	    _hg_status "d"
+	    _hg_debugpathcomplete --normal
 	;;
 	forget)
-	    _hg_status "a"
+	    _hg_debugpathcomplete --added
 	;;
 	diff)
-	    _hg_status "mar"
+	    # it is harmless to complete unmodified files
+	    _hg_debugpathcomplete
 	;;
 	revert)
-	    _hg_status "mard"
+	    # we must complete all files in case we are asked to
+	    # revert to a non-parent rev
+	    _hg_debugpathcomplete
 	;;
 	clone)
 	    local count=$(_hg_count_non_option)