Soft-wrapped autosuggestion to hide right prompt for now

Prior to f417cbc981 (Show soft-wrapped portions in autosuggestions,
2025-12-11), we'd truncate autosuggestions before the right prompt.
We no longer do that for autosuggestions that soft-wrap, which means
we try to draw both right prompt and suggestion in the same space.

Make suggestion paint over right prompt for now, since this seems to
be a simple and robust solution.  We can revisit this later.

Fixes #12255
This commit is contained in:
Johannes Altmanninger
2026-01-02 17:57:40 +01:00
parent 725cf33f1a
commit 0a23a78523
3 changed files with 66 additions and 35 deletions

View File

@@ -4,6 +4,7 @@ fish ?.?.? (released ???)
This release fixes the following problems identified in fish 4.3.0: This release fixes the following problems identified in fish 4.3.0:
- Selecting a completion could insert only part of the token (:issue:`12249`). - Selecting a completion could insert only part of the token (:issue:`12249`).
- Glitch with soft-wrapped autosuggestions and :doc:`cmds/fish_right_prompt` (:issue:`12255`).
- The sample prompts and themes are correctly installed (:issue:`12241`). - The sample prompts and themes are correctly installed (:issue:`12241`).
- Last line of command output could be hidden when missing newline (:issue:`12246`). - Last line of command output could be hidden when missing newline (:issue:`12246`).

View File

@@ -1980,7 +1980,7 @@ fn compute_layout(
); );
let left_prompt_width = left_prompt_layout.last_line_width; let left_prompt_width = left_prompt_layout.last_line_width;
let mut right_prompt_width = right_prompt_layout.last_line_width; let right_prompt_width = right_prompt_layout.last_line_width;
// Get the width of the first line, and if there is more than one line. // Get the width of the first line, and if there is more than one line.
let first_command_line_width: usize = line_at_cursor(commandline_before_suggestion, 0) let first_command_line_width: usize = line_at_cursor(commandline_before_suggestion, 0)
@@ -2016,24 +2016,14 @@ fn compute_layout(
..Default::default() ..Default::default()
}; };
// Hide the right prompt if it doesn't fit on the first line.
if left_prompt_width + first_command_line_width + right_prompt_width < screen_width {
result.right_prompt = right_prompt;
} else {
right_prompt_width = 0;
}
// Now we should definitely fit.
assert!(left_prompt_width + right_prompt_width <= screen_width);
// Track each logical line from the autosuggestion so we can determine how much of it fits // Track each logical line from the autosuggestion so we can determine how much of it fits
// on screen. We allow the lines to soft wrap naturally and we only truncate vertically if // on screen. We allow the lines to soft wrap naturally and we only truncate vertically if
// we would exceed the screen height. // we would exceed the screen height.
let cursor_y = left_prompt_layout.line_starts.len() - 1 let commandline_before_suggestion_lines = commandline_before_suggestion
+ commandline_before_suggestion .chars()
.chars() .filter(|&c| c == '\n')
.filter(|&c| c == '\n') .count();
.count(); let cursor_y = left_prompt_layout.line_starts.len() - 1 + commandline_before_suggestion_lines;
let mut suggestion_lines = vec![]; let mut suggestion_lines = vec![];
@@ -2062,7 +2052,8 @@ fn compute_layout(
+ commandline_before_suggestion + commandline_before_suggestion
.chars() .chars()
.rposition(|c| c == '\n') .rposition(|c| c == '\n')
.map_or(right_prompt_width, indent_width) .map(indent_width)
.unwrap_or_default()
} else { } else {
indent_width(suggestion_start - "\n".len()) indent_width(suggestion_start - "\n".len())
}; };
@@ -2122,6 +2113,24 @@ fn consumed_lines_or_truncated_suggestion(
let mut autosuggestion = WString::new(); let mut autosuggestion = WString::new();
let mut displayed_len = 0; let mut displayed_len = 0;
{
// Hide the right prompt if it doesn't fit on the first line.
let first_command_line_suggestion_width = if commandline_before_suggestion_lines == 0 {
suggestion_lines.first().map_or(0, |line| {
line.chars().map(wcwidth_rendered_min_0).sum::<usize>()
})
} else {
0
};
if left_prompt_width
+ first_command_line_width
+ first_command_line_suggestion_width
+ right_prompt_width
<= screen_width
{
result.right_prompt = right_prompt;
}
}
for (line_idx, autosuggestion_line) in suggestion_lines.iter().enumerate() { for (line_idx, autosuggestion_line) in suggestion_lines.iter().enumerate() {
if line_idx != 0 { if line_idx != 0 {
autosuggestion.push('\n'); autosuggestion.push('\n');
@@ -2421,20 +2430,19 @@ fn test_prompt_truncation() {
fn test_compute_layout() { fn test_compute_layout() {
macro_rules! validate { macro_rules! validate {
( (
( (
$screen_width:expr, $screen_width:expr,
$left_untrunc_prompt:literal, $left_untrunc_prompt:literal,
$right_untrunc_prompt:literal, $right_untrunc_prompt:literal,
$commandline_before_suggestion:literal, $commandline_before_suggestion:literal,
$autosuggestion_str:literal, $autosuggestion_str:literal,
$commandline_after_suggestion:literal $commandline_after_suggestion:literal
) ) -> (
-> ( $left_prompt:literal,
$left_prompt:literal, $left_prompt_space:expr,
$left_prompt_space:expr, $right_prompt:literal,
$right_prompt:literal, $autosuggestion:literal $(,)?
$autosuggestion:literal $(,)? )
)
) => {{ ) => {{
let full_commandline = L!($commandline_before_suggestion).to_owned() let full_commandline = L!($commandline_before_suggestion).to_owned()
+ L!($autosuggestion_str) + L!($autosuggestion_str)
@@ -2482,7 +2490,7 @@ macro_rules! validate {
) -> ( ) -> (
"left>", "left>",
5, 5,
"<right", "",
" autosuggesTION", " autosuggesTION",
) )
); );
@@ -2604,7 +2612,7 @@ macro_rules! validate {
) -> ( ) -> (
"left>", "left>",
5, 5,
"", "<RIGHT",
"and AUTOSUGGESTION", "and AUTOSUGGESTION",
) )
); );
@@ -2614,7 +2622,7 @@ macro_rules! validate {
) -> ( ) -> (
"left>", "left>",
5, 5,
"", "<RIGHT",
"AUTOSUGGESTION", "AUTOSUGGESTION",
) )
); );
@@ -2624,7 +2632,7 @@ macro_rules! validate {
) -> ( ) -> (
"left>", "left>",
5, 5,
"", "<RIGHT",
"utosuggestion sofT WRAP", "utosuggestion sofT WRAP",
) )
); );

View File

@@ -0,0 +1,22 @@
#RUN: %fish %s
#REQUIRES: command -v tmux
isolated-tmux-start -C '
function fish_right_prompt
echo RP
end
history append "echo $(string repeat 100 a)"
'
isolated-tmux send-keys Enter e
tmux-sleep
isolated-tmux capture-pane -p
# CHECK: prompt 0> {{ *}} RP
# CHECK: prompt 0> echo {{(a{65,})}}
# CHECK: {{(a{35,})}}
isolated-tmux send-keys C-h C-l "echo line1" M-Enter "e"
tmux-sleep
isolated-tmux capture-pane -p
# CHECK: prompt 0> echo line1 {{ *}} RP
# CHECK: echo {{(a{65,})}}
# CHECK: {{(a{35,})}}