Allow complete to have multiple conditions

This makes it so `complete -c foo -n test1 -n test2` registers *both*
conditions, and when it comes time to check the candidate, tries both,
in that order. If any fails it stops, if all succeed the completion is offered.

The reason for this is that it helps with caching - we have a
condition cache, but conditions like

```fish
test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] length

test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] sub
```

defeats it pretty easily, because the cache only looks at the entire
script as a string - it can't tell that the first `test` is the same
in both.

So this means we separate it into

```fish
complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] length" -s V -l visible -d "Use the visible width, excluding escape sequences"
+complete -f -c string -n "test (count (commandline -opc)) -ge 2" -n "contains -- (commandline -opc)[2] length" -s V -l visible -d "Use the visible width, excluding escape sequences"
```

which allows the `test` to be cached.

In tests, this improves performance for the string completions by 30%
by reducing all the redundant `test` calls.

The `git` completions can also greatly benefit from this.
This commit is contained in:
Fabian Homborg
2022-05-20 19:02:42 +02:00
committed by Fabian Boehm
parent 5a610f60d7
commit 64b34c8cda
6 changed files with 60 additions and 17 deletions

View File

@@ -466,3 +466,36 @@ complete -C"a=1 b=2 cmd_with_fancy_completion 1 "
complete -c thing -x -F
# CHECKERR: complete: invalid option combination, '--exclusive' and '--force-files'
# Multiple conditions
complete -f -c shot
complete -fc shot -n 'test (count (commandline -opc) -eq 1' -n 'test (commandline -opc)[-1] = shot' -a 'through'
# CHECKERR: complete: Condition 'test (count (commandline -opc) -eq 1' contained a syntax error
# CHECKERR: complete: Unexpected end of string, expecting ')'
# CHECKERR: test (count (commandline -opc) -eq 1
# CHECKERR: ^
complete -fc shot -n 'test (count (commandline -opc)) -eq 1' -n 'test (commandline -opc)[-1] = shot' -a 'through'
complete -fc shot -n 'test (count (commandline -opc)) -eq 2' -n 'test (commandline -opc)[-1] = through' -a 'the'
complete -fc shot -n 'test (count (commandline -opc)) -eq 3' -n 'test (commandline -opc)[-1] = the' -a 'heart'
complete -fc shot -n 'test (count (commandline -opc)) -eq 4' -n 'test (commandline -opc)[-1] = heart' -a 'and'
complete -fc shot -n 'test (count (commandline -opc)) -eq 5' -n 'test (commandline -opc)[-1] = and' -a "you\'re"
complete -fc shot -n 'test (count (commandline -opc)) -eq 6' -n 'test (commandline -opc)[-1] = "you\'re"' -a 'to'
complete -fc shot -n 'test (count (commandline -opc)) -eq 7' -n 'test (commandline -opc)[-1] = to' -a 'blame'
complete -C"shot "
# CHECK: through
complete -C"shot through "
# CHECK: the
# See that conditions after a failing one aren't executed.
set -g oops 0
complete -fc oooops
complete -fc oooops -n true -n true -n true -n 'false' -n 'set -g oops 1' -a oops
complete -C'oooops '
echo $oops
# CHECK: 0
complete -fc oooops -n 'true' -n 'set -g oops 1' -a oops
complete -C'oooops '
# CHECK: oops
echo $oops
# CHECK: 1