completions/git: Add fast path for untracked files

It's super easy to get a lot of these and they'll otherwise slow down
the completions a lot.

This makes `git add <TAB>` ~5-6x faster with about 4000 untracked
files (a copy of the fish build directory). It goes from 1.5 seconds to
250ms.

This is just for the git >= 2.11 path, but the other one would require
more checking and since git 2.11 is almost 7 years old now that's not
worth it.
This commit is contained in:
Fabian Boehm 2023-07-13 16:43:50 +02:00
parent 1f1975689e
commit 7f76f75966

View file

@ -183,9 +183,12 @@ function __fish_git_files
# We explicitly enable globs so we can use that to match the current token. # We explicitly enable globs so we can use that to match the current token.
set -l git_opt -c status.relativePaths -c core.quotePath= set -l git_opt -c status.relativePaths -c core.quotePath=
# If the token starts with `./`, we need to prepend that
string match -q './*' -- (commandline -ct) string match -q './*' -- (commandline -ct)
and set -l rel ./ and set -l rel ./
or set -l rel or set -l rel
# If the token starts with `:`, it's from the repo root
string match -q ':*' -- (commandline -ct) string match -q ':*' -- (commandline -ct)
and set -l colon 1 and set -l colon 1
or set -l colon or set -l colon
@ -195,8 +198,20 @@ function __fish_git_files
set -l ver (__fish_git --version | string replace -rf 'git version (\d+)\.(\d+)\.?.*' '$1\n$2') set -l ver (__fish_git --version | string replace -rf 'git version (\d+)\.(\d+)\.?.*' '$1\n$2')
# Version >= 2.11.* has the v2 format. # Version >= 2.11.* has the v2 format.
if test "$ver[1]" -gt 2 2>/dev/null; or test "$ver[1]" -eq 2 -a "$ver[2]" -ge 11 2>/dev/null if test "$ver[1]" -gt 2 2>/dev/null; or test "$ver[1]" -eq 2 -a "$ver[2]" -ge 11 2>/dev/null
__fish_git $git_opt status --porcelain=2 $status_opt \ set -l stats (__fish_git $git_opt status --porcelain=2 $status_opt)
| while read -la -d ' ' line if set -ql untracked
# Fast path for untracked files - it is extremely easy to get a lot of these,
# so we handle them first
set -l files (string match -rg '^\? (.*)' -- $stats | string trim -c \")
set stats (string match -rv '^\? ' -- $stats)
printf "$rel%s\n" $files\t$untracked_desc
if set -ql colon[1]
or set files (string match '../*' -- $files)
set files (path resolve -- $files | string replace -- "$root/" ":/:")
and printf '%s\n' $files\t$untracked_desc
end
end
printf %s\n $stats | while read -la -d ' ' line
set -l file set -l file
set -l desc set -l desc
# The basic status format is "XY", where X is "our" state (meaning the staging area), # The basic status format is "XY", where X is "our" state (meaning the staging area),
@ -316,12 +331,6 @@ function __fish_git_files
set -ql deleted_staged set -ql deleted_staged
and set file "$line[9..-1]" and set file "$line[9..-1]"
and set desc $staged_deleted_desc and set desc $staged_deleted_desc
case "$q"' *'
# Untracked
# "? <path>" - print from element 2 on.
set -ql untracked
and set file "$line[2..-1]"
and set desc $untracked_desc
case '! *' case '! *'
# Ignored # Ignored
# "! <path>" - print from element 2 on. # "! <path>" - print from element 2 on.
@ -338,7 +347,7 @@ function __fish_git_files
# If this contains newlines or tabs, # If this contains newlines or tabs,
# there is nothing we can do, but that's a general issue with scripted completions. # there is nothing we can do, but that's a general issue with scripted completions.
set file (string trim -c \" -- $file) set file (string trim -c \" -- $file)
# The relative filename. # The (possibly relative) filename.
printf "$rel%s\n" "$file"\t$desc printf "$rel%s\n" "$file"\t$desc
# Now from repo root. # Now from repo root.
# Only do this if the filename isn't a simple child, # Only do this if the filename isn't a simple child,