Fix repeated tab causing repeated smartcase completion insertion

Doing

	firefox --pro TAB TAB TAB

results in

	firefox --profile --ProfileManager

git-bisect points to 3546ffa3ef (reader
handle_completions(): remove dead filtering code, 2026-01-02)
but that regression has already been fixed by 85e76ba356
(Fix option substr completions not being filtered out, 2026-04-16).

However in between those two commits, the above case has also been
broken by 2f6b1eaaf9 (reader handle_completions(): don't consider
odd replacing completions for common prefix, 2026-01-02)

The first TAB inserts "--profile ", including the trailing space.
However it also shows the completion pager, which means that subsequent
TABs will insert after the space.

The trailing space does not make sense unless we navigate the pager.
Remove it in all cases, to fix this smartcase scenario.

Alternatively, we could start navigating the pager in this case
(and keep the trailing space), but that's probably too inconsistent.
This commit is contained in:
Johannes Altmanninger
2026-05-14 15:11:48 +08:00
parent 5876ff66ff
commit 1f18b9715f
3 changed files with 18 additions and 10 deletions

View File

@@ -13,6 +13,7 @@ Interactive improvements
Regression fixes:
-----------------
- (from 4.4) Vi mode ``x`` in :doc:`builtin read <cmds/read>` (:issue:`12724`).
- (from 4.3.3) Repeated tab would sometimes insert smartcase completions redundantly.
fish 4.7.1 (released May 08, 2026)
==================================

View File

@@ -7091,7 +7091,6 @@ fn handle_completions(&mut self, token_range: Range<usize>, mut comp: Vec<Comple
if all_matches_exact_or_prefix {
// Try to find a common prefix to insert among the surviving completions.
let mut flags = CompleteFlags::empty();
let mut prefix_is_partial_completion = false;
let mut first = true;
for c in &comp {
if c.flags.contains(CompleteFlags::SUPPRESS_PAGER_PREFIX) {
@@ -7115,7 +7114,6 @@ fn handle_completions(&mut self, token_range: Range<usize>, mut comp: Vec<Comple
// idx is now the length of the new common prefix.
common_prefix = common_prefix.slice_to(idx);
prefix_is_partial_completion = true;
// Early out if we decide there's no common prefix.
if idx == 0 {
@@ -7132,11 +7130,8 @@ fn handle_completions(&mut self, token_range: Range<usize>, mut comp: Vec<Comple
assert!(!use_prefix || !common_prefix.is_empty());
if use_prefix {
// We got something. If more than one completion contributed, then it means we have
// a prefix; don't insert a space after it.
if prefix_is_partial_completion {
flags |= CompleteFlags::NO_SPACE;
}
// More than one completion contributed, so don't insert a space after it.
flags |= CompleteFlags::NO_SPACE;
self.completion_insert(
common_prefix,
token_range.end,

View File

@@ -3,9 +3,11 @@
#REQUIRES: uname -r | grep -qv Microsoft
isolated-tmux-start -C '
set -g fish_autosuggestion_enabled 0
complete : -s c -l clip
complete : -s q -l qrcode
set -g fish_autosuggestion_enabled 0
complete true -l profile
complete true -l ProfileManager
'
touch somefile1
touch somefile2
@@ -20,11 +22,21 @@ end
tab
# CHECK: [prompt 0> : -cq]
tab
# CHECK: [prompt 0> : -cq somefile]
# CHECK: [somefile1 somefile2]
tab
# CHECK: [prompt 0> : -cq somefile1]
# CHECK: [somefile1 somefile2]
isolated-tmux send-keys C-u C-l 'true --pro'
tab
# CHECK: [prompt 0> true --profile]
# CHECK: [--profile --ProfileManager]
tab
# CHECK: [prompt 0> true --profile]
# CHECK: [--profile --ProfileManager]
isolated-tmux send-keys foo
tmux-sleep
isolated-tmux capture-pane -p | awk '/./ { print "[" $0 "]" }'
# CHECK: [prompt 0> true --profile foo]