Skip to content

Commit

Permalink
Merge pull request #65 from arijit79/horizontal-scroll
Browse files Browse the repository at this point in the history
Implement horizontal scrolling
  • Loading branch information
AMythicDev authored Feb 15, 2024
2 parents f1daee8 + 9dd0f66 commit b034c3b
Show file tree
Hide file tree
Showing 10 changed files with 267 additions and 47 deletions.
15 changes: 9 additions & 6 deletions src/core/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub enum Command {
SetPrompt(String),

// Screen output configurations
LineWrapping(bool),
SetLineNumbers(LineNumbers),
FollowOutput(bool),

Expand All @@ -53,6 +54,7 @@ impl PartialEq for Command {
| (Self::AppendData(d1), Self::AppendData(d2))
| (Self::SetPrompt(d1), Self::SetPrompt(d2))
| (Self::SendMessage(d1), Self::SendMessage(d2)) => d1 == d2,
(Self::LineWrapping(d1), Self::LineWrapping(d2)) => d1 == d2,
(Self::SetLineNumbers(d1), Self::SetLineNumbers(d2)) => d1 == d2,
(Self::ShowPrompt(d1), Self::ShowPrompt(d2)) => d1 == d2,
(Self::SetExitStrategy(d1), Self::SetExitStrategy(d2)) => d1 == d2,
Expand All @@ -70,12 +72,13 @@ impl PartialEq for Command {
impl Debug for Command {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::SetData(text) => write!(f, "SetData({text:?})"),
Self::AppendData(text) => write!(f, "AppendData({text:?})"),
Self::SetPrompt(text) => write!(f, "SetPrompt({text:?})"),
Self::SendMessage(text) => write!(f, "SendMessage({text:?})"),
Self::SetLineNumbers(ln) => write!(f, "SetLineNumbers({ln:?})"),
Self::SetExitStrategy(es) => write!(f, "SetExitStrategy({es:?})"),
Self::SetData(text) => write!(f, "SetData({:?})", text),
Self::AppendData(text) => write!(f, "AppendData({:?})", text),
Self::SetPrompt(text) => write!(f, "SetPrompt({:?})", text),
Self::SendMessage(text) => write!(f, "SendMessage({:?})", text),
Self::SetLineNumbers(ln) => write!(f, "SetLineNumbers({:?})", ln),
Self::LineWrapping(lw) => write!(f, "LineWrapping({:?})", lw),
Self::SetExitStrategy(es) => write!(f, "SetExitStrategy({:?})", es),
Self::SetInputClassifier(_) => write!(f, "SetInputClassifier"),
Self::ShowPrompt(show) => write!(f, "ShowPrompt({show:?})"),
Self::FormatRedrawPrompt => write!(f, "FormatRedrawPrompt"),
Expand Down
12 changes: 11 additions & 1 deletion src/core/ev_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ pub fn handle_event(
display::draw_for_change(out, p, &mut um)?;
p.upper_mark = um;
}
Command::UserInput(InputEvent::UpdateLeftMark(lm)) if !p.screen.line_wrapping => {
if lm.saturating_add(p.cols) > p.screen.get_max_line_length() && lm > p.left_mark {
return Ok(());
}
p.left_mark = lm;
display::draw_full(out, p)?
}
Command::UserInput(InputEvent::RestorePrompt) => {
// Set the message to None and new messages to false as all messages have been shown
p.message = None;
Expand Down Expand Up @@ -229,7 +236,6 @@ pub fn handle_event(
p.format_lines();
display::draw_full(&mut out, p)?;
}

Command::AppendData(text) => {
let prev_unterminated = p.unterminated;
let prev_fmt_lines_count = p.screen.formatted_lines_count();
Expand Down Expand Up @@ -274,6 +280,10 @@ pub fn handle_event(
display::write_prompt(out, &p.displayed_prompt, p.rows.try_into().unwrap())?;
}
Command::SetExitStrategy(es) => p.exit_strategy = es,
Command::LineWrapping(lw) => {
p.screen.line_wrapping = lw;
p.format_lines();
}
#[cfg(feature = "static_output")]
Command::SetRunNoOverflow(val) => p.run_no_overflow = val,
#[cfg(feature = "search")]
Expand Down
7 changes: 4 additions & 3 deletions src/core/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use std::{
};

#[cfg(feature = "static_output")]
use {super::utils::display::write_lines, crossterm::tty::IsTty};
use {super::utils::display::write_raw_lines, crossterm::tty::IsTty};

#[cfg(feature = "search")]
use parking_lot::Condvar;
Expand Down Expand Up @@ -98,7 +98,7 @@ pub fn init_core(pager: &Pager, rm: RunMode) -> std::result::Result<(), MinusErr
if *RUNMODE.lock() == RunMode::Static {
// If stdout is not a tty, write everything and quit
if !out.is_tty() {
write_lines(&mut out, &[ps.screen.orig_text], None)?;
write_raw_lines(&mut out, &[ps.screen.orig_text], None)?;
let mut rm = RUNMODE.lock();
*rm = RunMode::Uninitialized;
drop(rm);
Expand All @@ -107,7 +107,7 @@ pub fn init_core(pager: &Pager, rm: RunMode) -> std::result::Result<(), MinusErr
// If number of lines of text is less than available rows, write everything and quit
// unless run_no_overflow is set to true
if ps.screen.formatted_lines_count() <= ps.rows && !ps.run_no_overflow {
write_lines(&mut out, &ps.screen.formatted_lines, Some("\r"))?;
write_raw_lines(&mut out, &ps.screen.formatted_lines, Some("\r"))?;
ps.exit();
let mut rm = RUNMODE.lock();
*rm = RunMode::Uninitialized;
Expand Down 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
112 changes: 92 additions & 20 deletions src/core/utils/display/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crossterm::{
use std::{cmp::Ordering, convert::TryInto, io::Write};

use super::{term, text::AppendStyle};
use crate::{error::MinusError, PagerState};
use crate::{error::MinusError, minus_core, LineNumbers, PagerState};

/// Handles drawing of screen based on movement
///
Expand All @@ -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,28 @@ 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.screen.line_wrapping,
ps.left_mark,
ps.line_numbers.is_on(),
ps.screen.get_line_count(),
)?;

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 @@ -243,7 +249,7 @@ pub fn write_text_checked(
term::move_cursor(out, 0, 0, false)?;
term::clear_entire_screen(out, false)?;

write_lines(out, display_lines, Some("\r"))
write_raw_lines(out, display_lines, Some("\r"))
}

pub fn write_from_pagerstate(out: &mut impl Write, ps: &mut PagerState) -> Result<(), MinusError> {
Expand All @@ -267,7 +273,73 @@ pub fn write_from_pagerstate(out: &mut impl Write, ps: &mut PagerState) -> Resul
.screen
.get_formatted_lines_with_bounds(ps.upper_mark, lower_mark);

write_lines(out, display_lines, Some("\r"))
write_lines(
out,
display_lines,
ps.cols,
ps.screen.line_wrapping,
ps.left_mark,
ps.line_numbers.is_on(),
ps.screen.get_line_count(),
)
}

pub fn write_lines(
out: &mut impl Write,
lines: &[String],
cols: usize,
line_wrapping: bool,
left_mark: usize,
line_numbers: bool,
line_count: usize,
) -> crate::Result {
if !line_wrapping {
write_lines_in_horizontal_scroll(out, lines, cols, left_mark, line_numbers, line_count)
} else {
write_raw_lines(out, lines, Some("\r"))
}
}

pub fn write_lines_in_horizontal_scroll(
out: &mut impl Write,
lines: &[String],
cols: usize,
start: usize,
line_numbers: bool,
line_count: usize,
) -> crate::Result {
let line_number_ascii_seq_len = if line_numbers { 8 } else { 0 };
let line_number_padding = if line_numbers {
minus_core::utils::digits(line_count) + LineNumbers::EXTRA_PADDING + 3
} else {
0
};
let shifted_start = if line_numbers {
start + line_number_padding + line_number_ascii_seq_len
} else {
start
};

for line in lines {
let end = shifted_start + cols.min(line.len().saturating_sub(shifted_start))
- line_number_padding;

if start < line.len() {
if line_numbers {
writeln!(
out,
"\r{}{}",
&line[0..line_number_padding + line_number_ascii_seq_len],
&line[shifted_start..end]
)?;
} else {
writeln!(out, "\r{}", &line[shifted_start..end])?;
}
} else {
writeln!(out, "\r")?;
}
}
Ok(())
}

/// Write lines to the the output
Expand All @@ -276,7 +348,7 @@ pub fn write_from_pagerstate(out: &mut impl Write, ps: &mut PagerState) -> Resul
/// `initial` tells any extra text to be inserted before each line. For functions that use this
/// function over terminals, this should be set to `\r` to avoid broken display.
/// The `\r` resets the cursor to the start of the line.
pub fn write_lines(
pub fn write_raw_lines(
out: &mut impl Write,
lines: &[String],
initial: Option<&str>,
Expand Down
Loading

0 comments on commit b034c3b

Please sign in to comment.