Also show case-insensitive prefix matches in completion pager

[ja: made some changes and added commit message]

Fixes #7944
Closes #11910
This commit is contained in:
Asuka Minato
2025-11-23 22:38:49 +09:00
committed by Johannes Altmanninger
parent fceb600be5
commit 656b39a0b3
5 changed files with 71 additions and 29 deletions

View File

@@ -11,6 +11,7 @@ Interactive improvements
------------------------ ------------------------
- When typing immediately after starting fish, the first prompt is now rendered correctly. - When typing immediately after starting fish, the first prompt is now rendered correctly.
- Completion accuracy was improved for file paths containing ``=`` or ``:`` (:issue:`5363`). - Completion accuracy was improved for file paths containing ``=`` or ``:`` (:issue:`5363`).
- Prefix-matching completions are now shown even if they don't have the case typed by the user (:issue:`7944`).
Improved terminal support Improved terminal support
------------------------- -------------------------

View File

@@ -110,6 +110,8 @@ pub struct CompleteFlags: u16 {
const KEEP_VARIABLE_OVERRIDE_PREFIX = 1 << 8; const KEEP_VARIABLE_OVERRIDE_PREFIX = 1 << 8;
/// This is a variable name. /// This is a variable name.
const VARIABLE_NAME = 1 << 9; const VARIABLE_NAME = 1 << 9;
/// Suppress showing the pager prefix for this completion.
const SUPPRESS_PAGER_PREFIX = 1 << 10;
} }
} }

View File

@@ -6,7 +6,7 @@
use crate::common::{ use crate::common::{
EscapeFlags, EscapeStringStyle, escape_string, get_ellipsis_char, get_ellipsis_str, EscapeFlags, EscapeStringStyle, escape_string, get_ellipsis_char, get_ellipsis_str,
}; };
use crate::complete::Completion; use crate::complete::{CompleteFlags, Completion};
use crate::editable_line::EditableLine; use crate::editable_line::EditableLine;
use crate::highlight::{HighlightRole, HighlightSpec, highlight_shell}; use crate::highlight::{HighlightRole, HighlightSpec, highlight_shell};
use crate::operation_context::OperationContext; use crate::operation_context::OperationContext;
@@ -349,6 +349,11 @@ fn measure_completion_infos(&mut self) {
for comp in &mut self.unfiltered_completion_infos { for comp in &mut self.unfiltered_completion_infos {
let comp_strings = &mut comp.comp; let comp_strings = &mut comp.comp;
let show_prefix = !comp
.representative
.flags
.contains(CompleteFlags::SUPPRESS_PAGER_PREFIX);
for (j, comp_string) in comp_strings.iter().enumerate() { for (j, comp_string) in comp_strings.iter().enumerate() {
// If there's more than one, append the length of ', '. // If there's more than one, append the length of ', '.
if j >= 1 { if j >= 1 {
@@ -357,7 +362,9 @@ fn measure_completion_infos(&mut self) {
// This can return -1 if it can't calculate the width. So be cautious. // This can return -1 if it can't calculate the width. So be cautious.
let comp_width = wcswidth_rendered(comp_string); let comp_width = wcswidth_rendered(comp_string);
comp.comp_width += usize::try_from(prefix_len).unwrap_or_default(); if show_prefix {
comp.comp_width += usize::try_from(prefix_len).unwrap_or_default();
}
comp.comp_width += usize::try_from(comp_width).unwrap_or_default(); comp.comp_width += usize::try_from(comp_width).unwrap_or_default();
} }
@@ -432,9 +439,13 @@ fn completion_print(
let is_selected = Some(idx) == effective_selected_idx; let is_selected = Some(idx) == effective_selected_idx;
// Print this completion on its own "line". // Print this completion on its own "line".
let show_prefix = !el
.representative
.flags
.contains(CompleteFlags::SUPPRESS_PAGER_PREFIX);
let mut line = self.completion_print_item( let mut line = self.completion_print_item(
CharOffset::Pager(idx), CharOffset::Pager(idx),
prefix, show_prefix.then_some(prefix),
el, el,
col_width, col_width,
row % 2 != 0, row % 2 != 0,
@@ -459,7 +470,7 @@ fn completion_print(
fn completion_print_item( fn completion_print_item(
&self, &self,
offset_in_cmdline: CharOffset, offset_in_cmdline: CharOffset,
prefix: &wstr, prefix: Option<&wstr>,
c: &PagerComp, c: &PagerComp,
width: usize, width: usize,
secondary: bool, secondary: bool,
@@ -531,14 +542,16 @@ fn completion_print_item(
); );
} }
comp_remaining -= print_max( if let Some(prefix) = prefix {
offset_in_cmdline, comp_remaining -= print_max(
prefix, offset_in_cmdline,
prefix_col, prefix,
comp_remaining, prefix_col,
!comp.is_empty(), comp_remaining,
&mut line_data, !comp.is_empty(),
); &mut line_data,
);
}
comp_remaining -= print_max_impl( comp_remaining -= print_max_impl(
offset_in_cmdline, offset_in_cmdline,
comp, comp,

View File

@@ -6646,8 +6646,16 @@ fn handle_completions(&mut self, token_range: Range<usize>) -> bool {
// Only use completions that match replace_token. // Only use completions that match replace_token.
let completion_replaces_token = c.flags.contains(CompleteFlags::REPLACES_TOKEN); let completion_replaces_token = c.flags.contains(CompleteFlags::REPLACES_TOKEN);
let replaces_only_due_to_case_mismatch = {
c.flags.contains(CompleteFlags::REPLACES_TOKEN)
&& c.r#match.is_exact_or_prefix()
&& !matches!(c.r#match.case_fold, CaseSensitivity::Sensitive)
};
if completion_replaces_token != will_replace_token { if completion_replaces_token != will_replace_token {
continue; // Keep smart/samecase results even if we prefer not to replace the token.
if will_replace_token || !replaces_only_due_to_case_mismatch {
continue;
}
} }
// Don't use completions that want to replace, if we cannot replace them. // Don't use completions that want to replace, if we cannot replace them.
@@ -6655,10 +6663,13 @@ fn handle_completions(&mut self, token_range: Range<usize>) -> bool {
continue; continue;
} }
// This completion survived. all_matches_exact_or_prefix &= c.r#match.is_exact_or_prefix();
surviving_completions.push(c.clone());
all_matches_exact_or_prefix = let mut completion = c.clone();
all_matches_exact_or_prefix && c.r#match.is_exact_or_prefix(); if replaces_only_due_to_case_mismatch && !will_replace_token {
completion.flags |= CompleteFlags::SUPPRESS_PAGER_PREFIX;
}
surviving_completions.push(completion);
} }
if surviving_completions.len() == 1 { if surviving_completions.len() == 1 {
@@ -6735,6 +6746,11 @@ fn handle_completions(&mut self, token_range: Range<usize>) -> bool {
if use_prefix { if use_prefix {
for c in &mut surviving_completions { for c in &mut surviving_completions {
if c.flags.contains(CompleteFlags::SUPPRESS_PAGER_PREFIX) {
// Keep replacement semantics and the original prefix so these completions can
// fix casing when selected.
continue;
}
c.flags &= !CompleteFlags::REPLACES_TOKEN; c.flags &= !CompleteFlags::REPLACES_TOKEN;
c.completion.replace_range(0..common_prefix.len(), L!("")); c.completion.replace_range(0..common_prefix.len(), L!(""));
} }

View File

@@ -13,7 +13,7 @@ isolated-tmux send-keys 'HOME=$PWD ls ~/' Tab
tmux-sleep tmux-sleep
isolated-tmux capture-pane -p isolated-tmux capture-pane -p
# Note the contents may or may not have the autosuggestion appended - it is a race. # Note the contents may or may not have the autosuggestion appended - it is a race.
# CHECK: prompt 0> HOME=$PWD ls ~/file-{{1?}} # CHECK: prompt {{\d+}}> HOME=$PWD ls ~/file-{{1?}}
# CHECK: ~/file-1 ~/file-2 # CHECK: ~/file-1 ~/file-2
# No pager on single smartcase completion (#7738). # No pager on single smartcase completion (#7738).
@@ -21,7 +21,17 @@ isolated-tmux send-keys C-u C-l 'mkdir cmake CMakeFiles' Enter C-l \
'cat cmake' Tab 'cat cmake' Tab
tmux-sleep tmux-sleep
isolated-tmux capture-pane -p isolated-tmux capture-pane -p
# CHECK: prompt 1> cat cmake/ # CHECK: prompt {{\d+}}> cat cmake/
# CHECK: cmake/ CMakeFiles/
# Keep mixed-case completions visible when typing lowercase and ignore non-matching prefixes (#7944).
isolated-tmux send-keys C-u C-l 'rm -rf dog Dodo Voodo' Enter \
'mkdir dog Dodo Voodo docker doc_internal doc_src' Enter C-l \
'cd do' Tab
tmux-sleep
isolated-tmux capture-pane -p
# CHECK: prompt {{\d+}}> cd do{{(cker/)?}}
# CHECK: docker/ doc_internal/ doc_src/ Dodo/ dog/
# Correct case in pager when prefixes differ in case (#7743). # Correct case in pager when prefixes differ in case (#7743).
isolated-tmux send-keys C-u C-l 'complete -c foo2 -a "aabc aaBd" -f' Enter C-l \ isolated-tmux send-keys C-u C-l 'complete -c foo2 -a "aabc aaBd" -f' Enter C-l \
@@ -29,7 +39,7 @@ isolated-tmux send-keys C-u C-l 'complete -c foo2 -a "aabc aaBd" -f' Enter C-l \
tmux-sleep tmux-sleep
isolated-tmux capture-pane -p isolated-tmux capture-pane -p
# The "bc" part is the autosuggestion - we could use "capture-pane -e" to check colors. # The "bc" part is the autosuggestion - we could use "capture-pane -e" to check colors.
# CHECK: prompt 2> foo2 aabc # CHECK: prompt {{\d+}}> foo2 aabc
# CHECK: aabc aaBd # CHECK: aabc aaBd
# Check that a larger-than-screen completion list does not stomp a multiline commandline (#8509). # Check that a larger-than-screen completion list does not stomp a multiline commandline (#8509).
@@ -39,7 +49,7 @@ isolated-tmux send-keys C-u 'complete -c foo3 -fa "(seq $LINES)\t(string repeat
tmux-sleep tmux-sleep
isolated-tmux capture-pane -p | sed -n '1p;$p' isolated-tmux capture-pane -p | sed -n '1p;$p'
# Assert that we didn't change the command line. # Assert that we didn't change the command line.
# CHECK: prompt 3> begin # CHECK: prompt {{\d+}}> begin
# Also ensure that the pager is actually fully disclosed. # Also ensure that the pager is actually fully disclosed.
# CHECK: rows 1 to {{\d+}} of {{\d+}} # CHECK: rows 1 to {{\d+}} of {{\d+}}
@@ -50,7 +60,7 @@ tmux-sleep
isolated-tmux send-keys C-l foo2 Space BTab b BSpace b Escape isolated-tmux send-keys C-l foo2 Space BTab b BSpace b Escape
tmux-sleep tmux-sleep
isolated-tmux capture-pane -p isolated-tmux capture-pane -p
# CHECK: prompt 3> foo2 aa # CHECK: prompt {{\d+}}> foo2 aa
# Check that down-or-search works even when the pager is not selected. # Check that down-or-search works even when the pager is not selected.
isolated-tmux send-keys C-u foo2 Space Tab isolated-tmux send-keys C-u foo2 Space Tab
@@ -59,7 +69,7 @@ isolated-tmux send-keys Down
tmux-sleep tmux-sleep
isolated-tmux capture-pane -p isolated-tmux capture-pane -p
# Also check that we show an autosuggestion. # Also check that we show an autosuggestion.
# CHECK: prompt 3> foo2 aabc aabc # CHECK: prompt {{\d+}}> foo2 aabc aabc
# CHECK: aabc{{ *}}aaBd # CHECK: aabc{{ *}}aaBd
# Check that a larger-than-screen completion does not break down-or-search. # Check that a larger-than-screen completion does not break down-or-search.
@@ -76,14 +86,14 @@ isolated-tmux capture-pane -p | head -1
isolated-tmux send-keys C-u echo Space old-arg Enter C-l foo2 Space Tab Tab M-. isolated-tmux send-keys C-u echo Space old-arg Enter C-l foo2 Space Tab Tab M-.
tmux-sleep tmux-sleep
isolated-tmux capture-pane -p isolated-tmux capture-pane -p
# CHECK: prompt 5> foo2 aabc old-arg # CHECK: prompt {{\d+}}> foo2 aabc old-arg
isolated-tmux send-keys C-u 'echo suggest this' Enter C-l isolated-tmux send-keys C-u 'echo suggest this' Enter C-l
tmux-sleep tmux-sleep
isolated-tmux send-keys 'echo sug' C-w C-z isolated-tmux send-keys 'echo sug' C-w C-z
tmux-sleep tmux-sleep
isolated-tmux capture-pane -p isolated-tmux capture-pane -p
# CHECK: prompt 6> echo suggest this # CHECK: prompt {{\d+}}> echo suggest this
isolated-tmux send-keys C-u 'bind ctrl-s forward-single-char' Enter C-l isolated-tmux send-keys C-u 'bind ctrl-s forward-single-char' Enter C-l
isolated-tmux send-keys 'echo suggest thi' isolated-tmux send-keys 'echo suggest thi'
@@ -93,7 +103,7 @@ tmux-sleep
isolated-tmux send-keys C-s isolated-tmux send-keys C-s
tmux-sleep tmux-sleep
isolated-tmux capture-pane -p isolated-tmux capture-pane -p
# CHECK: prompt 7> echo suggest this # CHECK: prompt {{\d+}}> echo suggest this
isolated-tmux send-keys C-u isolated-tmux send-keys C-u
isolated-tmux send-keys 'echo sugg' C-a isolated-tmux send-keys 'echo sugg' C-a
@@ -101,7 +111,7 @@ tmux-sleep
isolated-tmux send-keys C-e M-f Space nothing isolated-tmux send-keys C-e M-f Space nothing
tmux-sleep tmux-sleep
isolated-tmux capture-pane -p isolated-tmux capture-pane -p
# CHECK: prompt 7> echo suggest nothing # CHECK: prompt {{\d+}}> echo suggest nothing
isolated-tmux send-keys C-u 'bind \cs forward-char-passive' Enter C-l isolated-tmux send-keys C-u 'bind \cs forward-char-passive' Enter C-l
isolated-tmux send-keys C-u 'bind \cb backward-char-passive' Enter C-l isolated-tmux send-keys C-u 'bind \cb backward-char-passive' Enter C-l
@@ -111,8 +121,8 @@ isolated-tmux send-keys 'echo do not accept thi' C-b C-b DC C-b C-s 'h'
tmux-sleep tmux-sleep
isolated-tmux send-keys C-s C-s C-s 'x' isolated-tmux send-keys C-s C-s C-s 'x'
isolated-tmux capture-pane -p isolated-tmux capture-pane -p
# CHECK: prompt 10> echo do not accept thix # CHECK: prompt {{\d+}}> echo do not accept thix
isolated-tmux send-keys C-u C-l ': {*,' Tab Tab Space , isolated-tmux send-keys C-u C-l ': {*,' Tab Tab Space ,
tmux-sleep tmux-sleep
isolated-tmux capture-pane -p isolated-tmux capture-pane -p
# CHECK: prompt 10> : {*,cmake/ ,{{.*}} # CHECK: prompt {{\d+}}> : {*,cmake/ ,{{.*}}