try_apply_edit_to_autosuggestion: update icase matched codepoint counter

This function mutates the autosuggestion's search_string_range without
updating the number of matched codepoints accordingly, fix that.
Fixes 78f4541116 (reader: fix try_apply_edit_to_autosuggestion false
positive, 2026-01-22).

Fixes #12377
This commit is contained in:
Johannes Altmanninger
2026-01-26 01:29:24 +11:00
parent eda1694581
commit 6a2f531f9b
2 changed files with 27 additions and 11 deletions

View File

@@ -2020,40 +2020,48 @@ fn try_apply_edit_to_autosuggestion(
));
let search_string_range = autosuggestion.search_string_range.clone();
let replacement = &edit.replacement;
let mut matched_codepoints_delta = 0_isize;
// This is a heuristic with false negatives but that seems fine.
if edit.range.start < search_string_range.start
|| edit.range.end != search_string_range.end
|| {
let suggestion = autosuggestion.text.chars();
let replacement = edit.replacement.chars();
let unchanged_prefix = autosuggestion.icase_matched_codepoints.map_or(
search_string_range
.len()
.checked_sub(edit.range.len())
.unwrap(),
|matched_codepoints| {
let unmatched_codepoints =
lowercase(command_line_text[edit.range.clone()].chars()).count();
matched_codepoints_delta -= isize::try_from(unmatched_codepoints).unwrap();
matched_codepoints
.checked_sub(
lowercase(command_line_text[edit.range.clone()].chars()).count(),
)
.checked_sub(unmatched_codepoints)
.unwrap()
},
);
if autosuggestion.icase_matched_codepoints.is_some() {
let replacement_lower = || lowercase(replacement.chars());
matched_codepoints_delta += isize::try_from(replacement_lower().count()).unwrap();
is_prefix(
lowercase(replacement),
replacement_lower(),
lowercase(suggestion).skip(unchanged_prefix),
)
} else {
is_prefix(replacement, suggestion.skip(unchanged_prefix))
is_prefix(replacement.chars(), suggestion.skip(unchanged_prefix))
}
} != Some(IsPrefix::Prefix)
{
return false;
}
autosuggestion.search_string_range.end = search_string_range.end
- edit.range.len().min(search_string_range.end)
+ edit.replacement.len();
autosuggestion.search_string_range.end =
search_string_range.end - edit.range.len().min(search_string_range.end) + replacement.len();
if let Some(matched_codepoints) = &mut autosuggestion.icase_matched_codepoints {
*matched_codepoints = matched_codepoints
.checked_add_signed(matched_codepoints_delta)
.unwrap();
}
true
}
@@ -7431,7 +7439,7 @@ macro_rules! validate {
Some(Autosuggestion {
text: L!("echo hest").to_owned(),
search_string_range: 0..6,
icase_matched_codepoints: Some(4),
icase_matched_codepoints: Some(6),
is_whole_item_from_history: true,
})
);
@@ -7449,7 +7457,7 @@ macro_rules! validate {
Some(Autosuggestion {
text: L!("Echo hest").to_owned(),
search_string_range: 0..3,
icase_matched_codepoints: Some(4),
icase_matched_codepoints: Some(3),
is_whole_item_from_history: true,
})
);

View File

@@ -59,3 +59,11 @@ isolated-tmux send-keys n
tmux-sleep
isolated-tmux capture-pane -p
# CHECK: prompt {{\d+}}> echo in
touch some-file
isolated-tmux send-keys C-u C-l ': some-'
tmux-sleep
isolated-tmux send-keys f Tab
tmux-sleep
isolated-tmux capture-pane -p
# CHECK: prompt {{\d+}}> : some-file