Truncate autosuggestion lines only at screen edge

From each logical line in the autosuggestion, we show all or nothing.
This means that we may truncate too early -- specifically 1  + the
number of soft-wrappings in this line. Fix that.

See also #12153
This commit is contained in:
Johannes Altmanninger
2025-12-16 08:17:03 +01:00
parent 32cee9caec
commit c3a9edceac
2 changed files with 56 additions and 33 deletions

View File

@@ -2049,6 +2049,10 @@ fn compute_layout(
.split(|&c| c == '\n')
.enumerate()
{
if available_vertical_space == 0 {
truncated_vertically = true;
break;
}
let autosuggestion_line = wstr::from_char_slice(autosuggestion_line);
// Calculate space available for autosuggestion.
@@ -2063,36 +2067,56 @@ fn compute_layout(
} else {
indent_width(suggestion_start - "\n".len())
};
available_vertical_space = available_vertical_space.saturating_sub(width / screen_width);
if available_vertical_space == 0 {
truncated_vertically = true;
break;
}
let column = width % screen_width;
let suggestion_line_height = {
let mut column = column;
fn consumed_lines_or_truncated_suggestion(
screen_width: usize,
available_vertical_space: usize,
mut column: usize,
autosuggestion_line: &wstr,
) -> Result<usize, &wstr> {
let mut lines = 1;
for ch in autosuggestion_line.chars() {
for (i, ch) in autosuggestion_line.char_indices() {
let ch_width = wcwidth_rendered_min_0(ch);
let new_column = column + ch_width;
if new_column > screen_width {
if new_column >= screen_width {
column = 0;
}
if column == 0 && ch_width != 0 {
lines += 1;
}
column = if new_column == screen_width {
0
} else {
column + ch_width
};
let barely_softwrapped = new_column == screen_width;
if !barely_softwrapped {
column += ch_width;
}
if lines > available_vertical_space {
return Err(autosuggestion_line.slice_to(i));
}
}
lines
};
match available_vertical_space.checked_sub(suggestion_line_height) {
Some(lines) => available_vertical_space = lines,
None => {
truncated_vertically = true;
break;
}
};
suggestion_lines.push(autosuggestion_line);
Ok(lines)
}
suggestion_lines.push(
match consumed_lines_or_truncated_suggestion(
screen_width,
available_vertical_space,
column,
autosuggestion_line,
) {
Ok(lines) => {
available_vertical_space -= lines;
autosuggestion_line
}
Err(truncated) => {
truncated_vertically = true;
truncated
}
},
);
if truncated_vertically {
break;
}
suggestion_start += autosuggestion_line.len() + "\n".len();
}

View File

@@ -49,22 +49,21 @@ isolated-tmux capture-pane -p | sed s/^/^/
# CHECK: ^ echo 000000000000000000000000000000000000000000000000000000000000000
# CHECK: ^0000000000000000000000000000000000000
# Currently, we take either all or nothing from soft-wrapped suggestion-lines.
# The ellipsis means that we'll get more lines.
isolated-tmux resize-window -y 3
tmux-sleep
isolated-tmux capture-pane -p | sed s/^/^/
# CHECK: ^prompt> if true
# CHECK: ^ echo 00000000000000000000000000000000000000000000000000
# CHECK: ^
# CHECK: ^ echo 00000000000000000000000000000000000000000000000000
# CHECK: ^ echo 00000000000000000000000000000000000000000000000000000000000000
# Test that truncation also works after the resize.
isolated-tmux send-keys C-u if
tmux-sleep
isolated-tmux capture-pane -p | sed s/^/^/
# CHECK: ^prompt> if true
# CHECK: ^ echo 00000000000000000000000000000000000000000000000000
# CHECK: ^
# CHECK: ^ echo 00000000000000000000000000000000000000000000000000
# CHECK: ^ echo 00000000000000000000000000000000000000000000000000000000000000
# Test that we truncate such that the prompt is never pushed up.
isolated-tmux resize-window -y 5 \; send-keys C-u Enter if
@@ -83,8 +82,8 @@ isolated-tmux capture-pane -p | sed s/^/^/
# CHECK: ^prompt>
# CHECK: ^prompt>
# CHECK: ^prompt> if true
# CHECK: ^ echo 00000000000000000000000000000000000000000000000000
# CHECK: ^
# CHECK: ^ echo 00000000000000000000000000000000000000000000000000
# CHECK: ^ echo 00000000000000000000000000000000000000000000000000000000000000
# Now try with a multiline prompt.
isolated-tmux send-keys C-u 'function fish_prompt; printf "prompt-line%d/2> \n" 1 2; end' Enter C-l Enter if
@@ -103,8 +102,8 @@ isolated-tmux capture-pane -p | sed s/^/^/
# CHECK: ^prompt-line2/2>
# CHECK: ^prompt-line1/2>
# CHECK: ^prompt-line2/2> if true
# CHECK: ^ echo 00000000000000000000000000000000000000000000000000
# CHECK: ^
# CHECK: ^ echo 00000000000000000000000000000000000000000000000000
# CHECK: ^ echo 000000000000000000000000000000000000000000000000000000
isolated-tmux send-keys C-u \; resize-window -y 7 \; send-keys if
tmux-sleep
@@ -132,7 +131,7 @@ isolated-tmux capture-pane -p | sed s/^/^/
# CHECK: ^prompt-line1
# CHECK: ^> begin
# CHECK: ^ : 000000000000000000000000000000000000000000000000000000000000000000000000
# CHECK: ^
# CHECK: ^
# Autosuggestions on a soft-wrapped commandline don't push the prompt.
isolated-tmux resize-window -x 6 -y 4 \; send-keys C-u \
@@ -159,4 +158,4 @@ isolated-tmux capture-pane -p | sed s/^/^/
# CHECK: ^>
# CHECK: ^>
# CHECK: ^> echo
# CHECK: ^
# CHECK: ^ wrap…