Skip to content

Commit

Permalink
Get horizontal scroll to work properly when line numbers are active
Browse files Browse the repository at this point in the history
  • Loading branch information
AMythicDev committed Feb 9, 2024
1 parent dc9aced commit 7b02d90
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 29 deletions.
1 change: 1 addition & 0 deletions src/core/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ fn start_reactor(

{
let mut p = ps.lock();

draw_full(&mut out_lock, &mut p)?;

if p.follow_output {
Expand Down
80 changes: 55 additions & 25 deletions src/core/utils/display/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,20 @@ use crate::{error::MinusError, PagerState};
/// only that part of the terminal.
pub fn draw_for_change(
out: &mut impl Write,
p: &mut PagerState,
ps: &mut PagerState,
new_upper_mark: &mut usize,
) -> Result<(), MinusError> {
let line_count = p.screen.formatted_lines_count();
let line_count = ps.screen.formatted_lines_count();

// Reduce one row for prompt/messages
//
// NOTE This should be the value of rows that should be used throughout this function.
// Don't use PagerState::rows, it might lead to wrong output
let writable_rows = p.rows.saturating_sub(1);
let writable_rows = ps.rows.saturating_sub(1);

// Calculate the lower_bound for current and new upper marks
// by adding either the rows or line_count depending on the minimality
let lower_bound = p.upper_mark.saturating_add(writable_rows.min(line_count));
let lower_bound = ps.upper_mark.saturating_add(writable_rows.min(line_count));
let new_lower_bound = new_upper_mark.saturating_add(writable_rows.min(line_count));

// If the lower_bound is greater than the available line count, we set it to such a value
Expand All @@ -41,7 +41,7 @@ pub fn draw_for_change(
*new_upper_mark = line_count.saturating_sub(writable_rows);
}

let delta = new_upper_mark.abs_diff(p.upper_mark);
let delta = new_upper_mark.abs_diff(ps.upper_mark);
// Sometimes the value of delta is too large that we can rather use the value of the writable rows to
// achieve the same effect with better performance. This means that we have draw to less lines to the terminal
//
Expand All @@ -56,7 +56,7 @@ pub fn draw_for_change(
// need this value whatever the value of delta be.
let normalized_delta = delta.min(writable_rows);

let lines = match (*new_upper_mark).cmp(&p.upper_mark) {
let lines = match (*new_upper_mark).cmp(&ps.upper_mark) {
Ordering::Greater => {
// Scroll down `normalized_delta` lines, and put the cursor one line above, where the old prompt would present.
// Clear it off and start displaying new dta.
Expand All @@ -67,7 +67,7 @@ pub fn draw_for_change(
term::move_cursor(
out,
0,
p.rows
ps.rows
.saturating_sub(normalized_delta + 1)
.try_into()
.unwrap(),
Expand All @@ -76,10 +76,10 @@ pub fn draw_for_change(
queue!(out, Clear(ClearType::CurrentLine))?;

if delta < writable_rows {
p.screen
ps.screen
.get_formatted_lines_with_bounds(lower_bound, new_lower_bound)
} else {
p.screen.get_formatted_lines_with_bounds(
ps.screen.get_formatted_lines_with_bounds(
*new_upper_mark,
new_upper_mark.saturating_add(normalized_delta),
)
Expand All @@ -92,22 +92,27 @@ pub fn draw_for_change(
)?;
term::move_cursor(out, 0, 0, false)?;

p.screen.get_formatted_lines_with_bounds(
ps.screen.get_formatted_lines_with_bounds(
*new_upper_mark,
new_upper_mark.saturating_add(normalized_delta),
)
}
Ordering::Equal => return Ok(()),
};

for line in lines {
writeln!(out, "\r{line}")?;
}
write_lines(
out,
lines,
ps.cols,
ps.line_wrapping,
ps.left_mark,
ps.line_numbers.is_on(),
)?;

p.upper_mark = *new_upper_mark;
ps.upper_mark = *new_upper_mark;

if p.show_prompt {
super::display::write_prompt(out, &p.displayed_prompt, p.rows.try_into().unwrap())?;
if ps.show_prompt {
super::display::write_prompt(out, &ps.displayed_prompt, ps.rows.try_into().unwrap())?;
}
out.flush()?;

Expand Down Expand Up @@ -266,20 +271,28 @@ pub fn write_from_pagerstate(out: &mut impl Write, ps: &mut PagerState) -> Resul
let display_lines: &[String] = ps
.screen
.get_formatted_lines_with_bounds(ps.upper_mark, lower_mark);
write_lines(out, display_lines, ps.cols, !ps.line_wrapping, ps.left_mark)
let line_count = ps.screen.line_count();
write_lines(
out,
display_lines,
ps.cols,
ps.line_wrapping,
ps.left_mark,
ps.line_numbers.is_on(),
)
}

pub fn write_lines(
out: &mut impl Write,
lines: &[String],
cols: usize,
horizontal_scroll: bool,
line_wrapping: bool,
left_mark: usize,
line_numbers: bool,
) -> crate::Result {
if horizontal_scroll {
let rightmost = lines.iter().max().unwrap().len();
let range = (left_mark, rightmost);
write_lines_in_horizontal_scroll(out, lines, cols, range)
if !line_wrapping {
let range = (left_mark, 0);
write_lines_in_horizontal_scroll(out, lines, cols, range, line_numbers)
} else {
write_raw_lines(out, lines, Some("\r"))
}
Expand All @@ -290,11 +303,28 @@ pub fn write_lines_in_horizontal_scroll(
lines: &[String],
cols: usize,
range: (usize, usize),
line_numbers: bool,
) -> crate::Result {
// let mut line_output = String::with_capacity(range.1);
let (start, end) = range;
let (start, _end) = range;

// When line numbers are active, the bold and reset ascii sequences also appear in the line
// which also gets counted when end = (available no. of columns) and eventually results in
// less text being shown per line.
//
// To counter this, we add the length of these sequences to available no. of columns
// essentially doing this:
//
// crossterm::style::Attribute::Bold.to_string().len()
// + crossterm::style::Attribute::Reset.to_string().len()
//
// which is equal to 8
let line_number_ascii_seq_len = if line_numbers { 8 } else { 0 };

for line in lines {
writeln!(out, "\r{}", &line[start..cols.min(line.len())])?;
let end = cols
.saturating_add(line_number_ascii_seq_len)
.min(line.len());
writeln!(out, "\r{}", &line[start..end])?;
}
Ok(())
}
Expand Down
9 changes: 5 additions & 4 deletions src/core/utils/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,14 +347,14 @@ pub fn formatted_line<'a>(
// NOTE: Only relevant when line numbers are active
// Padding is the space that the actual line text will be shifted to accommodate for
// line numbers. This is equal to:-
// LineNumbers::EXTRA_PADDING + len_line_number + 1 (for '.')
// LineNumbers::EXTRA_PADDING + len_line_number + 1 (for '.') + 1 (for 1 space)
//
// We reduce this from the number of available columns as this space cannot be used for
// actual line display when wrapping the lines
let padding = len_line_number + LineNumbers::EXTRA_PADDING + 1;
let padding = len_line_number + LineNumbers::EXTRA_PADDING + 2;

let cols_avail = if line_numbers {
cols.saturating_sub(padding)
cols.saturating_sub(padding + 2)
} else {
cols
};
Expand Down Expand Up @@ -450,6 +450,7 @@ pub fn make_format_lines(
text: &String,
line_numbers: LineNumbers,
cols: usize,
line_wrapping: bool,
#[cfg(feature = "search")] search_term: &Option<regex::Regex>,
) -> FormatResult {
let format_opts = FormatOpts {
Expand All @@ -462,7 +463,7 @@ pub fn make_format_lines(
cols,
#[cfg(feature = "search")]
search_term,
line_wrapping: true,
line_wrapping,
};

format_text_block(format_opts)
Expand Down
1 change: 1 addition & 0 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ impl PagerState {
&self.screen.orig_text,
self.line_numbers,
self.cols,
self.line_wrapping,
#[cfg(feature = "search")]
&self.search_state.search_term,
);
Expand Down

0 comments on commit 7b02d90

Please sign in to comment.