scrollback-push to sanitize cursor position

I believe it's possible that the cursor position reported by the
terminal does not match fish's cursor.  In that case, overflow. Fix
that since we should not trust the terminal.

Also rename a confusingly named variable.

Mouse-click handling has a similar issue, fix that too.

FWIW, tmux always reports cursor position zero (\x1b[1;1R) when
querying from fish (but not when querying with printf).
Will investigate that next, see the linked issue.

Fixes #10992
This commit is contained in:
Johannes Altmanninger
2025-01-03 07:25:07 +01:00
parent 670541eec8
commit abaeb4af2a

View File

@@ -503,9 +503,22 @@ pub fn move_to_end(&mut self) {
}
pub fn push_to_scrollback(&mut self, cursor_y: usize) {
let mut prompt_y = self.command_line_y_given_cursor_y(cursor_y);
prompt_y -= calc_prompt_lines(&self.actual_left_prompt) - 1;
if prompt_y == 0 {
let prompt_y = self.command_line_y_given_cursor_y(cursor_y);
let trailing_prompt_lines = calc_prompt_lines(&self.actual_left_prompt);
let lines_to_scroll = prompt_y
.checked_sub(trailing_prompt_lines - 1)
.unwrap_or_else(|| {
FLOG!(
error,
"Number of trailing prompt lines prompt lines",
trailing_prompt_lines,
"exceeds prompt's y",
prompt_y,
"inferred from reported cursor position",
);
0
});
if lines_to_scroll == 0 {
return;
}
let zelf = self.scoped_buffer();
@@ -513,12 +526,12 @@ pub fn push_to_scrollback(&mut self, cursor_y: usize) {
return;
};
let mut out = zelf.outp.borrow_mut();
let prompt_y = i32::try_from(prompt_y).unwrap();
let lines_to_scroll = i32::try_from(lines_to_scroll).unwrap();
// Scroll down.
out.tputs_bytes(format!("\x1b[{}S", prompt_y).as_bytes());
out.tputs_bytes(format!("\x1b[{}S", lines_to_scroll).as_bytes());
// Reposition cursor.
if let Some(up) = term.parm_cursor_up.as_ref() {
out.tputs_if_some(&tparm1(up, prompt_y));
out.tputs_if_some(&tparm1(up, lines_to_scroll));
}
}
@@ -542,7 +555,20 @@ pub fn offset_in_cmdline_given_cursor(
viewport_cursor: ViewportPosition,
) -> usize {
let viewport_prompt_y = self.command_line_y_given_cursor_y(viewport_cursor.y);
let y = viewport_position.y - viewport_prompt_y;
let y = viewport_position
.y
.checked_sub(viewport_prompt_y)
.unwrap_or_else(|| {
FLOG!(
error,
"Given y",
viewport_position.y,
"exceeds the prompt's y",
viewport_prompt_y,
"inferred from reported cursor position",
);
0
});
let y = y.min(self.actual.line_count() - 1);
let viewport_prompt_x = viewport_cursor.x - self.actual.cursor.x;
let x = viewport_position.x - viewport_prompt_x;