diff --git a/src/pager.rs b/src/pager.rs index 8a4d836fd..b5326e0de 100644 --- a/src/pager.rs +++ b/src/pager.rs @@ -71,7 +71,7 @@ pub enum SelectionMotion { const PAGER_MIN_WIDTH: usize = 16; /// Minimum height to show completions -const PAGER_MIN_HEIGHT: usize = 4; +pub const PAGER_MIN_HEIGHT: usize = 4; /// The maximum number of columns of completion to attempt to fit onto the screen. const PAGER_MAX_COLS: usize = 6; diff --git a/src/reader.rs b/src/reader.rs index 5f5b67088..5a8d7e344 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -427,8 +427,8 @@ struct LayoutData { /// Position of the cursor in the command line. position: usize, - /// Whether the cursor is focused on the pager or not. - focused_on_pager: bool, + /// The cursor position in the pager search field. + pager_search_field_position: Option, /// Visual selection of the command line, or none if none. selection: Option, @@ -1386,6 +1386,7 @@ fn is_repaint_needed(&self, mcolors: Option<&[HighlightSpec]>) -> bool { }; let focused_on_pager = self.active_edit_line_tag() == EditableLineTag::SearchField; + let pager_search_field_position = focused_on_pager.then_some(self.pager.cursor_position()); let last = &self.rendered_layout; check(self.force_exec_prompt_and_repaint, "forced") || check(self.command_line.text() != last.text, "text") @@ -1394,8 +1395,11 @@ fn is_repaint_needed(&self, mcolors: Option<&[HighlightSpec]>) -> bool { "highlight", ) || check(self.selection != last.selection, "selection") - || check(focused_on_pager != last.focused_on_pager, "focus") || check(self.command_line.position() != last.position, "position") + || check( + pager_search_field_position != last.pager_search_field_position, + "pager_search_field_position", + ) || check( self.history_search.search_range_if_active() != last.history_search_range, "history search", @@ -1431,13 +1435,10 @@ fn make_layout_data(&self) -> LayoutData { result.text = self.command_line.text().to_owned(); result.colors = self.command_line.colors().to_vec(); assert!(result.text.len() == result.colors.len()); - result.position = if focused_on_pager { - self.pager.cursor_position() - } else { - self.command_line.position() - }; + result.position = self.command_line.position(); + result.pager_search_field_position = + focused_on_pager.then_some(self.pager.cursor_position()); result.selection = self.selection; - result.focused_on_pager = focused_on_pager; result.history_search_range = self.history_search.search_range_if_active(); result.autosuggestion = self.autosuggestion.text.clone(); result.left_prompt_buff = self.left_prompt_buff.clone(); @@ -1516,10 +1517,10 @@ fn paint_layout(&mut self, reason: &wstr) { &colors, &indents, data.position, + data.pager_search_field_position, self.parser.vars(), pager, current_page_rendering, - data.focused_on_pager, ); } } diff --git a/src/screen.rs b/src/screen.rs index df2612ccf..71ece27a3 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -7,7 +7,7 @@ //! The current implementation is less smart than ncurses allows and can not for example move blocks //! of text around to handle text insertion. -use crate::pager::{PageRendering, Pager}; +use crate::pager::{PageRendering, Pager, PAGER_MIN_HEIGHT}; use std::cell::RefCell; use std::collections::LinkedList; use std::ffi::{CStr, CString}; @@ -237,7 +237,6 @@ pub fn new() -> Self { /// of the command line \param colors the colors to use for the commanad line \param indent the /// indent to use for the command line \param cursor_pos where the cursor is \param pager the /// pager to render below the command line \param page_rendering to cache the current pager view - /// \param cursor_is_within_pager whether the position is within the pager line (first line) pub fn write( &mut self, left_prompt: &wstr, @@ -247,10 +246,10 @@ pub fn write( colors: &[HighlightSpec], indent: &[i32], cursor_pos: usize, + pager_search_field_position: Option, vars: &dyn Environment, pager: &mut Pager, page_rendering: &mut PageRendering, - cursor_is_within_pager: bool, ) { let curr_termsize = termsize_last(); let screen_width = curr_termsize.width; @@ -337,7 +336,7 @@ struct ScrolledCursor { let mut i = 0; loop { // Grab the current cursor's x,y position if this character matches the cursor's offset. - if !cursor_is_within_pager && i == cursor_pos { + if i == cursor_pos { cursor_arr = Some(ScrolledCursor { cursor: self.desired.cursor, scroll_amount: (self.desired.line_count() @@ -379,39 +378,44 @@ struct ScrolledCursor { } i += 1; } - cursor_arr.as_mut().map( - |ScrolledCursor { - ref mut cursor, - scroll_amount, - }| { - if *scroll_amount != 0 { - self.desired.line_datas = self.desired.line_datas.split_off(*scroll_amount); - cursor.y -= *scroll_amount; - } - }, - ); let full_line_count = self.desired.cursor.y + 1; + let pager_available_height = std::cmp::max( + 1, + curr_termsize + .height + .saturating_sub_unsigned(full_line_count), + ); // Now that we've output everything, set the cursor to the position that we saved in the loop // above. - self.desired.cursor = cursor_arr.as_ref().map(|sc| sc.cursor).unwrap_or_default(); - - if cursor_is_within_pager { - self.desired.cursor.x = cursor_pos; - self.desired.cursor.y = self.desired.line_count(); - } + self.desired.cursor = match pager_search_field_position { + Some(pager_cursor_pos) + if pager_available_height >= isize::try_from(PAGER_MIN_HEIGHT).unwrap() => + { + Cursor { + x: pager_cursor_pos, + y: self.desired.line_count(), + } + } + _ => { + let ScrolledCursor { + mut cursor, + scroll_amount, + } = cursor_arr.unwrap(); + if scroll_amount != 0 { + self.desired.line_datas = self.desired.line_datas.split_off(scroll_amount); + cursor.y -= scroll_amount; + } + cursor + } + }; // Re-render our completions page if necessary. Limit the term size of the pager to the true // term size, minus the number of lines consumed by our string. pager.set_term_size(&Termsize::new( std::cmp::max(1, curr_termsize.width), - std::cmp::max( - 1, - curr_termsize - .height - .saturating_sub_unsigned(full_line_count), - ), + pager_available_height, )); pager.update_rendering(page_rendering);