Account for fuzzy completions.

Introduced in Fish 3.1
cf. https://github.com/fish-shell/fish-shell/issues/5467

Needed to make tests less strict.
Instead of checking the exact list of matches,
we require the expected ones and exclude some others.
This commit is contained in:
Raphael Reitzig 2020-05-26 00:00:04 +02:00
parent 0c15f199cd
commit 479fa1e541
4 changed files with 154 additions and 115 deletions

View file

@ -88,14 +88,14 @@ complete -c sdk -f -n '__fish_sdkman_no_command' \
-d 'Install new version'
complete -c sdk -f -n '__fish_sdkman_using_command i install' \
-a "(__fish_sdkman_candidates)"
# TODO complete available versions --> issue #4
complete -c sdk -f -n '__fish_sdkman_specifying_candidate i install' \
# TODO complete available versions --> #4
-a 'a.b.c' \
-d "version list unavailable"
complete -c sdk -f -n '__fish_sdkman_specifying_candidate i install' \
-a 'x.y.z' \
-d "Add your own; specify path!"
# Implicit: complete files as fourth parameter
-d "Specify path to install custom version."
# Implicit: complete files as fourth parameter
complete -c sdk -f -n '__fish_sdkman_command_has_enough_parameters 3 i install'
# block

View file

@ -14,166 +14,179 @@ Feature: Shell Completion
Scenario Outline: Commands complete
When the user enters "<cmd>" into the prompt
Then completion should propose "<completions>"
But completion should not propose <exclusions>
Examples:
| cmd | completions |
| b | b, broadcast |
| c | c, current |
| d | d, default |
| f | flush |
| h | h, help |
| i | i, install |
| in | install |
| l | list, ls |
| o | offline |
| r | rm |
| s | selfupdate |
| u | u, ug, uninstall, update, upgrade, use |
| un | uninstall |
| up | update, upgrade |
| us | use |
| v | v, version |
# Currently uncovered; include to catch new upstream commands:
| a | |
| e | |
| g | |
| j | |
| k | |
| m | |
| n | |
| p | |
| q | |
| t | |
| w | |
| x | |
| y | |
| z | |
| cmd | completions | exclusions |
| b | b, broadcast | /^[^b]+$/ |
| c | c, current | /^[^c]+$/ |
| d | d, default | /^[^d]+$/ |
| f | flush | /^[^f]+$/ |
| h | h, help | /^[^h]+$/ |
| i | i, install | /^[^i]+$/ |
| in | install | |
| l | list, ls | /^[^l]+$/ |
| o | offline | /^[^o]+$/ |
| r | rm | /^[^r]+$/ |
| s | selfupdate | /^[^s]+$/ |
| u | u, ug, uninstall, update, upgrade, use | /^[^u]+$/ |
| un | uninstall | |
| up | update, upgrade | |
| us | use | |
| v | v, version | /^[^v]+$/ |
# Currently uncovered (except by fuzzy matches);
# include negatives to prevent accidents:
| a | | /^a/ |
| e | | /^e/ |
| g | | /^g/ |
| j | | /^j/ |
| k | | /^k/ |
| m | | /^m/ |
| n | | /^n/ |
| p | | /^p/ |
| q | | /^q/ |
| t | | /^t/ |
| w | | /^w/ |
| x | | /^x/ |
| y | | /^y/ |
| z | | /^z/ |
Scenario Outline: Completion for 'install'
When the user enters "install <cmd>" into the prompt
Then completion should propose "<completions>"
But completion should not propose <exclusions>
Examples:
| cmd | completions |
| an | ant |
| xyz | |
| 1. | |
| gra | gradle, grails |
| grad | gradle |
| gradk | |
# | ant 1.10. | 1.10.0, 1.10.1 | # TODO: list installable versions --> issue #4
| ant 1.10.2-mine /tm | /tmp/ |
| 'ant 1.10.2-mine /tmp ' | |
| cmd | completions | exclusions |
| an | ant | gradle |
| xyz | | /.*/ |
| 1. | | /.*/ |
| gra | gradle, grails | ant |
| grad | gradle | ant, grails |
| gradk | | /.*/ |
# | ant 1.10. | 1.10.0, 1.10.1 | | # TODO: list installable versions --> issue #4
| ant 1.10.2-mine /tm | /tmp/ | /bin |
| 'ant 1.10.2-mine /tmp ' | | /.*/ |
# NB: Excluding wildcard pattern /.*/ expresses "do not offer any completions"
Scenario Outline: Completion for 'uninstall'
When the user enters "uninstall <cmd>" into the prompt
Then completion should propose "<completions>"
But completion should not propose <exclusions>
Examples:
| cmd | completions |
| | ant, crash |
| a | ant |
| j | |
| 1. | |
| an | ant | # installed
| gr | | # none installed
| xyz | | # no such candidate
| 'an ' | | # no such candidate installed
| 'ant 1' | 1.10.1, 1.9.9 |
| 'ant 1.10.' | 1.10.1 |
| 'ant 2' | |
| 'ant 1.10.1 ' | |
| cmd | completions | exclusions |
| | ant, crash | gradle |
| a | ant | gradle |
| j | | /.*/ |
| 1. | | /.*/ |
| an | ant | gradle, crash | # some installed
| gr | | /.*/ | # none installed
| xyz | | /.*/ | # no such candidate
| 'an ' | | /.*/ | # no such candidate installed
| 'ant 1' | 1.10.1, 1.9.9 | /^\w+$/ |
| 'ant 1.10.' | 1.10.1 | 1.9.9 |
| 'ant 2' | | /.*/ |
| 'ant 1.10.1 ' | | /.*/ | # only one version at a time
Scenario Outline: Completion for 'list'
When the user enters "list <cmd>" into the prompt
Then completion should propose "<completions>"
But completion should not propose <exclusions>
Examples:
| cmd | completions |
| an | ant |
| xyz | |
| 1. | |
| 'ant ' | |
| cmd | completions | exclusions |
| an | ant | crash |
| xyz | | /.*/ |
| 1. | | /.*/ |
| 'ant ' | | /.*/ |
Scenario Outline: Completion for 'use'
When the user enters "use <cmd>" into the prompt
Then completion should propose "<completions>"
But completion should not propose <exclusions>
Examples:
| cmd | completions |
| | ant, crash |
| an | ant |
| j | |
| 1. | |
| 'ant ' | 1.10.1, 1.9.9 |
| 'ant 1.10.1 ' | |
| cmd | completions | exclusions |
| | ant, crash | gradle |
| an | ant | crash, gradle |
| j | | /.*/ |
| 1. | | /.*/ |
| 'ant ' | 1.10.1, 1.9.9 | /^\w+$/ |
| 'ant 1.10.1 ' | | /.*/ |
Scenario Outline: Completion for 'default'
When the user enters "default <cmd>" into the prompt
Then completion should propose "<completions>"
But completion should not propose <exclusions>
Examples:
| cmd | completions |
| | ant, crash |
| an | ant |
| j | |
| 1. | |
| 'ant ' | 1.10.1, 1.9.9 |
| 'ant 1.10.1 ' | |
| cmd | completions | exclusions |
| | ant, crash | gradle |
| an | ant | crash, gradle |
| j | | /.*/ |
| 1. | | /.*/ |
| 'ant ' | 1.10.1, 1.9.9 | /^\w+$/ |
| 'ant 1.10.1 ' | | /.*/ |
Scenario Outline: Completion for 'current'
When the user enters "current <cmd>" into the prompt
Then completion should propose "<completions>"
But completion should not propose <exclusions>
Examples:
| cmd | completions |
| an | ant | # --> installed version
| ja | java | # --> not installed
| xyz | |
| 1. | |
| 'ant ' | |
| cmd | completions | exclusions |
| an | ant | gradle | # --> installed version
| gr | gradle | ant | # --> not installed
| xyz | | /.*/ |
| 1. | | /.*/ |
| 'ant ' | | /.*/ |
Scenario Outline: Completion for 'upgrade'
When the user enters "upgrade <cmd>" into the prompt
Then completion should propose "<completions>"
But completion should not propose <exclusions>
Examples:
| cmd | completions |
| | ant, crash |
| an | ant |
| j | |
| 1. | |
| 'ant ' | |
| cmd | completions | exclusions |
| | ant, crash | gradle |
| an | ant | crash, gradle |
| j | | /.*/ |
| 1. | | /.*/ |
| 'ant ' | | /^\w+$/ |
Scenario Outline: Completion for 'offline'
When the user enters "offline <cmd>" into the prompt
Then completion should propose "<completions>"
But completion should not propose <exclusions>
Examples:
| cmd | completions |
| | disable, enable |
| e | enable |
| d | disable |
| a | |
| 'enable ' | |
| cmd | completions | exclusions |
| | disable, enable | /^(?!disable\|enable).*$/ | # NB: \| escaped to get it past Gherkin's parser
| en | enable | /^(?!enable).*$/ |
| di | disable | /^(?!disable).*$/ |
| an | | /.*/ |
| 'enable ' | | /.*/ |
Scenario Outline: Completion for 'selfupdate'
When the user enters "selfupdate <cmd>" into the prompt
Then completion should propose "<completions>"
But completion should not propose <exclusions>
Examples:
| cmd | completions |
| | force |
| f | force |
| a | |
| 'force ' | |
| cmd | completions | exclusions |
| | force | /^(?!force).*$/ |
| f | force | /^(?!force).*$/ |
| a | | /.*/ |
| 'force ' | | /.*/ |
Scenario Outline: Completion for 'flush'
When the user enters "flush <cmd>" into the prompt
Then completion should propose "<completions>"
But completion should not propose <exclusions>
Examples:
| cmd | completions |
| | archives, broadcast, temp |
| b | broadcast |
| a | archives |
| t | temp |
| x | |
| 'temp ' | |
| cmd | completions | exclusions |
| | archives, broadcast, temp | /^(?!archives\|broadcast\|temp).*$/ |
| b | broadcast | /^(?!broadcast).*$/ |
| a | archives | /^(?!archives\|broadcast).*$/ |
| t | temp | /^(?!temp\|broadcast).*$/ |
| x | | /.*/ |
| 'temp ' | | /.*/ |
Scenario Outline: Completion for commands without parameters
When the user enters "<cmd>" into the prompt
Then completion should propose ""
Then completion should not propose /.*/
Examples:
| cmd |
| 'version ' |

View file

@ -7,12 +7,8 @@ module CompletionHelper
completions = run_fish_command("complete -C\"sdk #{cmd}\"")
completions.split("\n") \
.map { |line| line.split(/\s+/)[0].strip } \
.sort \
.uniq \
.join(', ')
.map { |line| line.split(/\s+/)[0].strip }
# TODO: Why do we get duplicates in the Docker container?
# --> remove uniq
end
end
World CompletionHelper
@ -21,6 +17,15 @@ When('the user enters {string} into the prompt') do |cmd|
@response = complete(cmd.gsub(/["']/, ''))
end
Then(/^completion should propose "(.*)"$/) do |completions|
expect(@response).to eq(completions)
Then('completion should propose {string}') do |completions|
completions = completions.split(',').map(&:strip)
expect(@response).to include(*completions)
end
Then('completion should not propose {patterns}') do |exclusions_patterns|
exclusions_patterns.each do |p|
@response.each do |r|
expect(r).not_to match(p)
end
end
end

View file

@ -0,0 +1,21 @@
# frozen_string_literal: true
ParameterType(
name: 'patterns',
regexp: %r{([^\s]*|'[^']*'|/[^/]*/)(,\s*([^\s]*|'[^']*'|/[^/]*/))*},
type: Array,
transformer: lambda do |*patterns|
patterns \
.map(&:strip) \
.map do |s|
s = if %r{^/(.*)/$} =~ s
Regexp.last_match(1)
elsif %r{^'(.*)'$} =~ s
"^#{Regexp.escape(Regexp.last_match(1))}$"
else
"^#{Regexp.escape(s)}$"
end
Regexp.compile(s)
end
end
)