Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions src/core/ev_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub fn handle_event(
Event::UserInput(InputEvent::RestorePrompt) => {
// Set the message to None and new messages to false as all messages have been shown
p.message = None;
p.format_prompt();
p.update_displayed_prompt();
}
Event::UserInput(InputEvent::UpdateTermArea(c, r)) => {
p.rows = r;
Expand Down Expand Up @@ -94,7 +94,7 @@ pub fn handle_event(
let compiled_regex = regex::Regex::new(&search_result.string).ok();
if compiled_regex.is_none() {
p.message = Some("Invalid regular expression. Press Enter".to_owned());
p.format_prompt();
p.update_displayed_prompt();
}
compiled_regex
} else {
Expand All @@ -112,7 +112,7 @@ pub fn handle_event(
p.upper_mark = *p.search_idx.iter().nth(p.search_mark).unwrap();
}

p.format_prompt();
p.update_displayed_prompt();
display::draw_full(&mut out, p)?;
}
#[cfg(feature = "search")]
Expand All @@ -126,7 +126,7 @@ pub fn handle_event(
p.upper_mark = *p.search_idx.iter().nth(p.search_mark).unwrap();
}

p.format_prompt();
p.update_displayed_prompt();
}
#[cfg(feature = "search")]
Event::UserInput(InputEvent::PrevMatch | InputEvent::MoveToPrevMatch(1))
Expand All @@ -142,7 +142,7 @@ pub fn handle_event(
// If the index is less than or equal to the upper_mark, then set y to the new upper_mark
if *y < p.upper_mark {
p.upper_mark = *y;
p.format_prompt();
p.update_displayed_prompt();
}
}
}
Expand All @@ -163,7 +163,7 @@ pub fn handle_event(
p.upper_mark = *p.search_idx.iter().nth(p.search_mark).unwrap();
}
}
p.format_prompt();
p.update_displayed_prompt();
}
#[cfg(feature = "search")]
Event::UserInput(InputEvent::MoveToPrevMatch(n)) if p.search_term.is_some() => {
Expand All @@ -177,7 +177,7 @@ pub fn handle_event(
// If the index is less than or equal to the upper_mark, then set y to the new upper_mark
if *y < p.upper_mark {
p.upper_mark = *y;
p.format_prompt();
p.update_displayed_prompt();
}
}
}
Expand All @@ -204,7 +204,7 @@ pub fn handle_event(
} else {
p.message = Some(text.to_string());
}
p.format_prompt();
p.update_displayed_prompt();
term::move_cursor(&mut out, 0, p.rows.try_into().unwrap(), false)?;
if !p.running.lock().is_uninitialized() {
super::utils::display::write_prompt(
Expand Down
6 changes: 3 additions & 3 deletions src/core/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,17 +348,17 @@ fn event_reader(
if let Some(iev) = input {
if let InputEvent::Number(n) = iev {
guard.prefix_num.push(n);
guard.format_prompt();
guard.update_displayed_prompt();
} else if !guard.prefix_num.is_empty() {
guard.prefix_num.clear();
guard.format_prompt();
guard.update_displayed_prompt();
}
if let Err(TrySendError::Disconnected(_)) = evtx.try_send(Event::UserInput(iev)) {
break;
}
} else if !guard.prefix_num.is_empty() {
guard.prefix_num.clear();
guard.format_prompt();
guard.update_displayed_prompt();
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod events;
pub mod init;
pub mod utils;
pub static RUNMODE: parking_lot::Mutex<RunMode> = parking_lot::const_mutex(RunMode::Uninitialized);
pub mod screen_line;

/// Define the modes in which minus can run
#[derive(Copy, Clone, PartialEq, Eq)]
Expand Down
127 changes: 127 additions & 0 deletions src/core/screen_line.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use crate::minus_core::utils::text::wrap_str;
use crate::LineNumbers;

pub struct ScreenLine {
fmt_lines: Vec<String>,
orig_text: String,
line_number: usize,
fmt_lines_count: usize,
terminated: bool,
}

struct ScreenLineCreationOpts {
text: String,
cols: usize,
line_number: usize,
line_numbers: LineNumbers,
len_line_numbers: u16,
// prev_fmt_lines_count: usize,
}

impl ScreenLine {
fn new(fmt_lines: Vec<String>, orig_text: String, line_number: usize) -> Self {
let fmt_lines_count = fmt_lines.len();
let terminated = orig_text.ends_with('\n');
Self {
fmt_lines,
orig_text,
line_number,
fmt_lines_count,
terminated,
}
}

fn new_from_string(
text: String,
cols: u16,
line_number: usize,
line_numbers: LineNumbers,
len_line_number: u16,
) -> Self {
let fmt_lines = formatted_line(&text, len_line_number, line_number, line_numbers, cols);
Self::new(fmt_lines, text, line_number)
}
}

pub fn formatted_line(
line: &str,
len_line_number: u16,
line_number: usize,
line_numbers: LineNumbers,
cols: u16,
) -> Vec<String> {
assert!(
!line.contains('\n'),
"Newlines found in appending line {:?}",
line
);
// Whether line numbers are active
let line_numbers = matches!(line_numbers, LineNumbers::Enabled | LineNumbers::AlwaysOn);

// 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 '.')
//
// 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;

// Wrap the line and return an iterator over all the rows
let mut enumerated_rows = if line_numbers {
wrap_str(line, cols.saturating_sub(padding + 2).into()).into_iter()
} else {
wrap_str(line, cols.into()).into_iter()
};

if line_numbers {
let mut formatted_rows = Vec::with_capacity(256);

// Formatter for only when line numbers are active
// * If minus is run under test, ascii codes for making the numbers bol is not inserted because they add
// extra difficulty while writing tests
// * Line number is added only to the first row of a line. This makes a better UI overall
let formatter = |row: String, is_first_row: bool, idx: usize| {
format!(
"{bold}{number: >len$}{reset} {row}",
bold = if cfg!(not(test)) && is_first_row {
crossterm::style::Attribute::Bold.to_string()
} else {
String::new()
},
number = if is_first_row {
(idx + 1).to_string() + "."
} else {
String::new()
},
len = padding.into(),
reset = if cfg!(not(test)) && is_first_row {
crossterm::style::Attribute::Reset.to_string()
} else {
String::new()
},
row = row
)
};

// First format the first row separate from other rows, then the subsequent rows and finally join them
// This is because only the first row contains the line number and not the subsequent rows
let first_row = {
#[cfg_attr(not(feature = "search"), allow(unused_mut))]
let mut row = enumerated_rows.next().unwrap();
formatter(row, true, line_number)
};
formatted_rows.push(first_row);

#[cfg_attr(not(feature = "search"), allow(unused_mut))]
#[cfg_attr(not(feature = "search"), allow(unused_variables))]
let mut rows_left = enumerated_rows
.map(|mut row| formatter(row, false, 0))
.collect::<Vec<String>>();
formatted_rows.append(&mut rows_left);

formatted_rows
} else {
enumerated_rows.collect()
}
}
4 changes: 2 additions & 2 deletions src/core/utils/display/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ fn draw_help_message() {
let mut pager = PagerState::new().unwrap();
pager.lines = lines.to_string();
pager.line_numbers = LineNumbers::AlwaysOff;
pager.format_prompt();
pager.update_displayed_prompt();

draw_full(&mut out, &mut pager).expect("Should have written");

Expand Down Expand Up @@ -468,7 +468,7 @@ mod draw_for_change_tests {
ps.upper_mark = 0;
ps.lines = lines;
ps.format_lines();
ps.format_prompt();
ps.update_displayed_prompt();
ps
}

Expand Down
76 changes: 75 additions & 1 deletion src/core/utils/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ pub fn formatted_line(
//
// 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 + (<u16 as Into<usize>>::into(LineNumbers::EXTRA_PADDING)) + 1;

// Wrap the line and return an iterator over all the rows
let mut enumerated_rows = if line_numbers {
Expand Down Expand Up @@ -656,6 +656,80 @@ third line\n",
}
}

/// Reformat the inputted prompt to how it should be displayed
pub(crate) fn format_prompt(
prompt: String,
cols: usize,
prefix_num: &str,
message: Option<String>,
#[cfg(feature = "search")] search_idx: BTreeSet<usize>,
#[cfg(feature = "search")] search_mark: usize,
) -> String {
const SEARCH_BG: &str = "\x1b[34m";
const INPUT_BG: &str = "\x1b[33m";

// Allocate the string. Add extra space in case for the
// ANSI escape things if we do have characters typed and search showing
let mut format_string = String::with_capacity(cols + (SEARCH_BG.len() * 2) + 4);

// Get the string that will contain the search index/match indicator
#[cfg(feature = "search")]
let mut search_str = String::new();
#[cfg(feature = "search")]
if !search_idx.is_empty() {
search_str.push(' ');
search_str.push_str(&(search_mark + 1).to_string());
search_str.push('/');
search_str.push_str(&search_idx.len().to_string());
search_str.push(' ');
}

// And get the string that will contain the prefix_num
let mut prefix_str = String::new();
if !prefix_num.is_empty() {
prefix_str.push(' ');
prefix_str.push_str(&prefix_num);
prefix_str.push(' ');
}

// And lastly, the string that contains the prompt or msg
let prompt_str = message.as_ref().unwrap_or(&prompt);

#[cfg(feature = "search")]
let search_len = search_str.len();
#[cfg(not(feature = "search"))]
let search_len = 0;

// Calculate how much extra padding in the middle we need between
// the prompt/message and the indicators on the right
let prefix_len = prefix_str.len();
let extra_space = cols.saturating_sub(search_len + prefix_len + prompt_str.len());
let dsp_prompt: &str = if extra_space == 0 {
&prompt_str[..cols - search_len - prefix_len]
} else {
prompt_str
};

// push the prompt/msg
format_string.push_str(dsp_prompt);
format_string.push_str(&" ".repeat(extra_space));

// add the prefix_num if it exists
if prefix_len > 0 {
format_string.push_str(INPUT_BG);
format_string.push_str(&prefix_str);
}

// and add the search indicator stuff if it exists
#[cfg(feature = "search")]
if search_len > 0 {
format_string.push_str(SEARCH_BG);
format_string.push_str(&search_str);
}

format_string
}

mod wrapping {
// Test wrapping functions
#[test]
Expand Down
13 changes: 6 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ pub mod input;
#[path = "core/mod.rs"]
mod minus_core;
mod pager;
pub mod screen;
#[cfg(feature = "search")]
pub mod search;
mod state;
Expand All @@ -246,16 +247,14 @@ mod static_pager;

#[cfg(feature = "dynamic_output")]
pub use dynamic_pager::dynamic_paging;
#[cfg(feature = "static_output")]
pub use static_pager::page_all;

pub use error::MinusError;
pub use minus_core::RunMode;
pub use pager::Pager;
#[cfg(feature = "search")]
pub use search::SearchMode;

pub use error::MinusError;
pub use pager::Pager;
pub use state::PagerState;
#[cfg(feature = "static_output")]
pub use static_pager::page_all;

/// A convenient type for `Vec<Box<dyn FnMut() + Send + Sync + 'static>>`
pub type ExitCallbacks = Vec<Box<dyn FnMut() + Send + Sync + 'static>>;
Expand Down Expand Up @@ -305,7 +304,7 @@ pub enum LineNumbers {
}

impl LineNumbers {
const EXTRA_PADDING: usize = 5;
const EXTRA_PADDING: u16 = 5;

/// Returns `true` if `self` can be inverted (i.e, `!self != self`), see
/// the documentation for the variants to know if they are invertible or
Expand Down
Loading