mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-06-02 05:41:16 -03:00
Render overflown commandline in entirety just before executing
As of 04c913427 (Limit command line rendering to $LINES lines,
2024-10-25), we only render a part of the command line. This removes
valuable information from scrollback.
The reasons for the limit were
1. to enable redrawing the commandline (can't do that if part of it
is off-screen).
2. if the cursor is at the beginning of the command-line, we can't
really render the off-screen suffix (unless we can tell the terminal
to scroll back after doing that).
Fortunately these don't matter for the very last rendering of a
command line. Let's render the entire command just before executing,
fixing the scrollback for executed commands.
In future, we should fix it also for pre-execution renderings. This
needs a terminal command to clear part of the scrollback. Can't find
anything on https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
There is "Erase Saved Lines" but that deletes the entire scrollback.
See the discussion in #10827
This commit is contained in:
@@ -1456,12 +1456,17 @@ fn make_layout_data(&self) -> LayoutData {
|
|||||||
/// If `mcolors` has a value, then apply it; otherwise extend existing colors.
|
/// If `mcolors` has a value, then apply it; otherwise extend existing colors.
|
||||||
fn layout_and_repaint(&mut self, reason: &wstr) {
|
fn layout_and_repaint(&mut self, reason: &wstr) {
|
||||||
self.rendered_layout = self.make_layout_data();
|
self.rendered_layout = self.make_layout_data();
|
||||||
self.paint_layout(reason);
|
self.paint_layout(reason, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout_and_repaint_before_execution(&mut self) {
|
||||||
|
self.rendered_layout = self.make_layout_data();
|
||||||
|
self.paint_layout(L!("prepare to execute"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Paint the last rendered layout.
|
/// Paint the last rendered layout.
|
||||||
/// `reason` is used in FLOG to explain why.
|
/// `reason` is used in FLOG to explain why.
|
||||||
fn paint_layout(&mut self, reason: &wstr) {
|
fn paint_layout(&mut self, reason: &wstr, is_final_rendering: bool) {
|
||||||
FLOGF!(reader_render, "Repainting from %ls", reason);
|
FLOGF!(reader_render, "Repainting from %ls", reason);
|
||||||
let data = &self.data.rendered_layout;
|
let data = &self.data.rendered_layout;
|
||||||
let cmd_line = &self.data.command_line;
|
let cmd_line = &self.data.command_line;
|
||||||
@@ -1526,6 +1531,7 @@ fn paint_layout(&mut self, reason: &wstr) {
|
|||||||
self.parser.vars(),
|
self.parser.vars(),
|
||||||
pager,
|
pager,
|
||||||
current_page_rendering,
|
current_page_rendering,
|
||||||
|
is_final_rendering,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1949,8 +1955,9 @@ fn readline(&mut self, nchars: Option<NonZeroUsize>) -> Option<WString> {
|
|||||||
|
|
||||||
// Redraw the command line. This is what ensures the autosuggestion is hidden, etc. after the
|
// Redraw the command line. This is what ensures the autosuggestion is hidden, etc. after the
|
||||||
// user presses enter.
|
// user presses enter.
|
||||||
if zelf.is_repaint_needed(None) || zelf.conf.inputfd != STDIN_FILENO {
|
if zelf.is_repaint_needed(None) || zelf.screen.scrolled || zelf.conf.inputfd != STDIN_FILENO
|
||||||
zelf.layout_and_repaint(L!("prepare to execute"));
|
{
|
||||||
|
zelf.layout_and_repaint_before_execution();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finish syntax highlighting (but do not wait forever).
|
// Finish syntax highlighting (but do not wait forever).
|
||||||
@@ -3635,7 +3642,7 @@ fn flash(&mut self) {
|
|||||||
data.colors[i].background = HighlightRole::search_match;
|
data.colors[i].background = HighlightRole::search_match;
|
||||||
}
|
}
|
||||||
self.rendered_layout = data.clone(); // need to copy the data since we will use it again.
|
self.rendered_layout = data.clone(); // need to copy the data since we will use it again.
|
||||||
self.paint_layout(L!("flash"));
|
self.paint_layout(L!("flash"), false);
|
||||||
|
|
||||||
let _old_data = std::mem::take(&mut self.rendered_layout);
|
let _old_data = std::mem::take(&mut self.rendered_layout);
|
||||||
|
|
||||||
@@ -3644,7 +3651,7 @@ fn flash(&mut self) {
|
|||||||
// Re-render with our saved data.
|
// Re-render with our saved data.
|
||||||
data.colors = saved_colors;
|
data.colors = saved_colors;
|
||||||
self.rendered_layout = data;
|
self.rendered_layout = data;
|
||||||
self.paint_layout(L!("unflash"));
|
self.paint_layout(L!("unflash"), false);
|
||||||
|
|
||||||
// Save the time we stopped flashing as the time of the most recent flash. We can't just
|
// Save the time we stopped flashing as the time of the most recent flash. We can't just
|
||||||
// increment the old `now` value because the sleep is non-deterministic.
|
// increment the old `now` value because the sleep is non-deterministic.
|
||||||
|
|||||||
@@ -177,6 +177,8 @@ pub fn is_empty(&self) -> bool {
|
|||||||
pub struct Screen {
|
pub struct Screen {
|
||||||
/// Whether the last-drawn autosuggestion (if any) is truncated, or hidden entirely.
|
/// Whether the last-drawn autosuggestion (if any) is truncated, or hidden entirely.
|
||||||
pub autosuggestion_is_truncated: bool,
|
pub autosuggestion_is_truncated: bool,
|
||||||
|
/// True if the last rendering was so large we could only display part of the command line.
|
||||||
|
pub scrolled: bool,
|
||||||
|
|
||||||
/// Receiver for our output.
|
/// Receiver for our output.
|
||||||
outp: &'static RefCell<Outputter>,
|
outp: &'static RefCell<Outputter>,
|
||||||
@@ -213,6 +215,7 @@ pub fn new() -> Self {
|
|||||||
Self {
|
Self {
|
||||||
outp: Outputter::stdoutput(),
|
outp: Outputter::stdoutput(),
|
||||||
autosuggestion_is_truncated: Default::default(),
|
autosuggestion_is_truncated: Default::default(),
|
||||||
|
scrolled: Default::default(),
|
||||||
desired: Default::default(),
|
desired: Default::default(),
|
||||||
actual: Default::default(),
|
actual: Default::default(),
|
||||||
actual_left_prompt: Default::default(),
|
actual_left_prompt: Default::default(),
|
||||||
@@ -250,6 +253,7 @@ pub fn write(
|
|||||||
vars: &dyn Environment,
|
vars: &dyn Environment,
|
||||||
pager: &mut Pager,
|
pager: &mut Pager,
|
||||||
page_rendering: &mut PageRendering,
|
page_rendering: &mut PageRendering,
|
||||||
|
is_final_rendering: bool,
|
||||||
) {
|
) {
|
||||||
let curr_termsize = termsize_last();
|
let curr_termsize = termsize_last();
|
||||||
let screen_width = curr_termsize.width;
|
let screen_width = curr_termsize.width;
|
||||||
@@ -360,15 +364,19 @@ struct ScrolledCursor {
|
|||||||
break scrolled_cursor.unwrap();
|
break scrolled_cursor.unwrap();
|
||||||
}
|
}
|
||||||
if !self.desired_append_char(
|
if !self.desired_append_char(
|
||||||
scrolled_cursor
|
if is_final_rendering {
|
||||||
.map(|sc| {
|
usize::MAX
|
||||||
if sc.scroll_amount != 0 {
|
} else {
|
||||||
sc.cursor.y
|
scrolled_cursor
|
||||||
} else {
|
.map(|sc| {
|
||||||
screen_height - 1
|
if sc.scroll_amount != 0 {
|
||||||
}
|
sc.cursor.y
|
||||||
})
|
} else {
|
||||||
.unwrap_or(usize::MAX),
|
screen_height - 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or(usize::MAX)
|
||||||
|
},
|
||||||
effective_commandline.as_char_slice()[i],
|
effective_commandline.as_char_slice()[i],
|
||||||
colors[i],
|
colors[i],
|
||||||
usize::try_from(indent[i]).unwrap(),
|
usize::try_from(indent[i]).unwrap(),
|
||||||
@@ -405,7 +413,9 @@ struct ScrolledCursor {
|
|||||||
scroll_amount,
|
scroll_amount,
|
||||||
} = scrolled_cursor;
|
} = scrolled_cursor;
|
||||||
if scroll_amount != 0 {
|
if scroll_amount != 0 {
|
||||||
self.desired.line_datas = self.desired.line_datas.split_off(scroll_amount);
|
if !is_final_rendering {
|
||||||
|
self.desired.line_datas = self.desired.line_datas.split_off(scroll_amount);
|
||||||
|
}
|
||||||
cursor.y -= scroll_amount;
|
cursor.y -= scroll_amount;
|
||||||
}
|
}
|
||||||
cursor
|
cursor
|
||||||
@@ -423,11 +433,13 @@ struct ScrolledCursor {
|
|||||||
// Append pager_data (none if empty).
|
// Append pager_data (none if empty).
|
||||||
self.desired.append_lines(&page_rendering.screen_data);
|
self.desired.append_lines(&page_rendering.screen_data);
|
||||||
|
|
||||||
|
self.scrolled = scrolled_cursor.scroll_amount != 0;
|
||||||
|
|
||||||
self.update(
|
self.update(
|
||||||
vars,
|
vars,
|
||||||
&layout.left_prompt,
|
&layout.left_prompt,
|
||||||
&layout.right_prompt,
|
&layout.right_prompt,
|
||||||
scrolled_cursor.scroll_amount != 0,
|
is_final_rendering,
|
||||||
);
|
);
|
||||||
self.save_status();
|
self.save_status();
|
||||||
}
|
}
|
||||||
@@ -852,7 +864,7 @@ fn update(
|
|||||||
vars: &dyn Environment,
|
vars: &dyn Environment,
|
||||||
left_prompt: &wstr,
|
left_prompt: &wstr,
|
||||||
right_prompt: &wstr,
|
right_prompt: &wstr,
|
||||||
scrolled: bool,
|
is_final_rendering: bool,
|
||||||
) {
|
) {
|
||||||
// Helper function to set a resolved color, using the caching resolver.
|
// Helper function to set a resolved color, using the caching resolver.
|
||||||
let mut color_resolver = HighlightColorResolver::new();
|
let mut color_resolver = HighlightColorResolver::new();
|
||||||
@@ -909,14 +921,14 @@ fn update(
|
|||||||
let term = term.as_ref();
|
let term = term.as_ref();
|
||||||
|
|
||||||
// Output the left prompt if it has changed.
|
// Output the left prompt if it has changed.
|
||||||
if scrolled {
|
if zelf.scrolled && !is_final_rendering {
|
||||||
zelf.r#move(0, 0);
|
zelf.r#move(0, 0);
|
||||||
zelf.outp
|
zelf.outp
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.tputs_if_some(&term.and_then(|term| term.clr_eol.as_ref()));
|
.tputs_if_some(&term.and_then(|term| term.clr_eol.as_ref()));
|
||||||
zelf.actual_left_prompt.clear();
|
zelf.actual_left_prompt.clear();
|
||||||
zelf.actual.cursor.x = 0;
|
zelf.actual.cursor.x = 0;
|
||||||
} else if left_prompt != zelf.actual_left_prompt {
|
} else if left_prompt != zelf.actual_left_prompt || (zelf.scrolled && is_final_rendering) {
|
||||||
zelf.r#move(0, 0);
|
zelf.r#move(0, 0);
|
||||||
let mut start = 0;
|
let mut start = 0;
|
||||||
let osc_133_prompt_start =
|
let osc_133_prompt_start =
|
||||||
@@ -966,7 +978,7 @@ fn s_line(zelf: &Screen, i: usize) -> &Line {
|
|||||||
// Note that skip_remaining is a width, not a character count.
|
// Note that skip_remaining is a width, not a character count.
|
||||||
let mut skip_remaining = start_pos;
|
let mut skip_remaining = start_pos;
|
||||||
|
|
||||||
let shared_prefix = if scrolled {
|
let shared_prefix = if zelf.scrolled {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
line_shared_prefix(o_line(&zelf, i), s_line(&zelf, i))
|
line_shared_prefix(o_line(&zelf, i), s_line(&zelf, i))
|
||||||
|
|||||||
Reference in New Issue
Block a user