Skip to content
Merged
35 changes: 26 additions & 9 deletions src/menu/columnar_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ pub struct ColumnarMenu {
col_pos: u16,
/// row position in the menu. Starts from 0
row_pos: u16,
/// Number of values that are skipped when printing,
/// depending on selected value and terminal height
skip_values: u16,
/// Event sent to the menu
event: Option<MenuEvent>,
/// Longest suggestion found in the values
Expand All @@ -82,6 +85,7 @@ impl Default for ColumnarMenu {
values: Vec::new(),
col_pos: 0,
row_pos: 0,
skip_values: 0,
event: None,
longest_suggestion: 0,
input: None,
Expand Down Expand Up @@ -619,6 +623,26 @@ impl Menu for ColumnarMenu {
self.working_details.columns = possible_cols;
}
}

let mut available_lines = painter.remaining_lines_real();
// Handle the case where a prompt uses the entire screen.
// Drawing the menu has priority over the drawing the prompt.
if available_lines == 0 {
available_lines = painter.remaining_lines().min(self.min_rows());
}

let first_visible_row = self.skip_values / self.get_cols();

self.skip_values = if self.row_pos <= first_visible_row {
// Selection is above the visible area, scroll up
self.row_pos * self.get_cols()
} else if self.row_pos >= first_visible_row + available_lines {
// Selection is below the visible area, scroll down
(self.row_pos.saturating_sub(available_lines) + 1) * self.get_cols()
} else {
// Selection is within the visible area
self.skip_values
};
}
}

Expand All @@ -645,19 +669,12 @@ impl Menu for ColumnarMenu {
if self.get_values().is_empty() {
self.no_records_msg(use_ansi_coloring)
} else {
// The skip values represent the number of lines that should be skipped
// while printing the menu
let skip_values = if self.row_pos >= available_lines {
let skip_lines = self.row_pos.saturating_sub(available_lines) + 1;
(skip_lines * self.get_cols()) as usize
} else {
0
};

// It seems that crossterm prefers to have a complete string ready to be printed
// rather than looping through the values and printing multiple things
// This reduces the flickering when printing the menu
let available_values = (available_lines * self.get_cols()) as usize;
let skip_values = self.skip_values as usize;

self.get_values()
.iter()
.skip(skip_values)
Expand Down
39 changes: 28 additions & 11 deletions src/menu/ide_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ pub struct IdeMenu {
values: Vec<Suggestion>,
/// Selected value. Starts at 0
selected: u16,
/// Number of values that are skipped when printing,
/// depending on selected value and terminal height
skip_values: u16,
/// Event sent to the menu
event: Option<MenuEvent>,
/// Longest suggestion found in the values
Expand All @@ -161,6 +164,7 @@ impl Default for IdeMenu {
working_details: IdeMenuDetails::default(),
values: Vec::new(),
selected: 0,
skip_values: 0,
event: None,
longest_suggestion: 0,
input: None,
Expand Down Expand Up @@ -807,6 +811,29 @@ impl Menu for IdeMenu {

self.working_details.space_left = space_left;
self.working_details.space_right = space_right;

let mut available_lines = painter
.remaining_lines_real()
.min(self.default_details.max_completion_height);

// Handle the case where a prompt uses the entire screen.
// Drawing the menu has priority over the drawing the prompt.
if available_lines == 0 {
available_lines = painter.remaining_lines().min(self.min_rows());
}

let visible_items = available_lines.saturating_sub(border_width);

self.skip_values = if self.selected <= self.skip_values {
// Selection is above the visible area
self.selected
} else if self.selected >= self.skip_values + visible_items {
// Selection is below the visible area
self.selected.saturating_sub(visible_items) + 1
} else {
// Selection is within the visible area
self.skip_values
}
}
}

Expand Down Expand Up @@ -840,17 +867,7 @@ impl Menu for IdeMenu {
};

let available_lines = available_lines.min(self.default_details.max_completion_height);
// The skip values represent the number of lines that should be skipped
// while printing the menu
let skip_values = if self.selected >= available_lines.saturating_sub(border_width) {
let skip_lines = self
.selected
.saturating_sub(available_lines.saturating_sub(border_width))
+ 1;
skip_lines as usize
} else {
0
};
let skip_values = self.skip_values as usize;

let available_values = available_lines.saturating_sub(border_width) as usize;

Expand Down
19 changes: 18 additions & 1 deletion src/painting/painter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ pub struct Painter {
// Stdout
stdout: W,
prompt_start_row: u16,
// The number of lines that the prompt takes up
prompt_height: u16,
terminal_size: (u16, u16),
last_required_lines: u16,
large_buffer: bool,
Expand All @@ -103,6 +105,7 @@ impl Painter {
Painter {
stdout,
prompt_start_row: 0,
prompt_height: 0,
terminal_size: (0, 0),
last_required_lines: 0,
large_buffer: false,
Expand All @@ -121,7 +124,18 @@ impl Painter {
self.terminal_size.0
}

/// Returns the available lines from the prompt down
/// Returns the empty lines from the prompt down.
pub fn remaining_lines_real(&self) -> u16 {
self.screen_height()
.saturating_sub(self.prompt_start_row)
.saturating_sub(self.prompt_height)
}

/// Returns the number of lines that are available or can be made available by
/// stripping the prompt.
///
/// If you want the number of empty lines below the prompt,
/// use [`Painter::remaining_lines_real`] instead.
pub fn remaining_lines(&self) -> u16 {
self.screen_height().saturating_sub(self.prompt_start_row)
}
Expand Down Expand Up @@ -199,6 +213,9 @@ impl Painter {
let screen_width = self.screen_width();
let screen_height = self.screen_height();

// We add one here as [`PromptLines::prompt_lines_with_wrap`] intentionally subtracts 1 from the real value.
self.prompt_height = lines.prompt_lines_with_wrap(screen_width) + 1;

// Handle resize for multi line prompt
if self.just_resized {
self.prompt_start_row = self.prompt_start_row.saturating_sub(
Expand Down