8000 Implement the DataSource trait by AMythicDev · Pull Request #139 · AMythicDev/minus · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Implement the DataSource trait #139

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
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
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ regex = { version = "^1", optional = true }
crossbeam-channel = "^0.5"
parking_lot = "0.12.1"
once_cell = { version = "^1.18", features = ["parking_lot"] }
bytecount = { version = "0.6.7", features = ["runtime-dispatch-simd"] }
smol_str = "0.2.1"
arrayvec = "0.7.4"

[features]
search = [ "regex" ]
Expand Down Expand Up @@ -70,3 +73,8 @@ required-features = ["static_output"]
name = "msg-tokio"
path = "examples/msg-tokio.rs"
required-features = ["dynamic_output"]


[profile.profiling]
inherits = "release"
debug = 1
3 changes: 2 additions & 1 deletion src/core/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use crate::{

use crossbeam_channel::{Receiver, Sender, TrySendError};
use crossterm::event;
use smol_str::ToSmolStr;
use std::{
io::{stdout, Stdout},
panic,
Expand Down Expand Up @@ -98,7 +99,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_raw_lines(&mut out, &[ps.screen.orig_text], None)?;
write_raw_lines(&mut out, &[ps.screen.orig_text.to_smolstr()], None)?;
let mut rm = RUNMODE.lock();
*rm = RunMode::Uninitialized;
drop(rm);
Expand Down
12 changes: 6 additions & 6 deletions src/core/utils/display/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ pub fn draw_append_text(
#[allow(clippy::too_many_arguments)]
pub fn write_text_checked(
out: &mut impl Write,
lines: &[String],
lines: &[Row],
mut upper_mark: usize,
rows: usize,
cols: usize,
Expand All @@ -257,7 +257,7 @@ pub fn write_text_checked(
}

// Add \r to ensure cursor is placed at the beginning of each row
let display_lines: &[String] = &lines[upper_mark..lower_mark];
let display_lines = &lines[upper_mark..lower_mark];

term::move_cursor(out, 0, 0, false)?;
term::clear_entire_screen(out, false)?;
Expand Down Expand Up @@ -287,7 +287,7 @@ pub fn write_from_pagerstate(out: &mut impl Write, ps: &mut PagerState) -> Resul
}

// Add \r to ensure cursor is placed at the beginning of each row
let display_lines: &[String] = ps
let display_lines = ps
.screen
.get_formatted_lines_with_bounds(ps.upper_mark, lower_mark);

Expand All @@ -304,7 +304,7 @@ pub fn write_from_pagerstate(out: &mut impl Write, ps: &mut PagerState) -> Resul

pub fn write_lines(
out: &mut impl Write,
lines: &[String],
lines: &[Row],
cols: usize,
line_wrapping: bool,
left_mark: usize,
Expand All @@ -320,7 +320,7 @@ pub fn write_lines(

pub fn write_lines_in_horizontal_scroll(
out: &mut impl Write,
lines: &[String],
lines: &[Row],
cols: usize,
start: usize,
line_numbers: bool,
Expand Down Expand Up @@ -368,7 +368,7 @@ pub fn write_lines_in_horizontal_scroll(
/// The `\r` resets the cursor to the start of the line.
pub fn write_raw_lines(
out: &mut impl Write,
lines: &[String],
lines: &[Row],
initial: Option<&str>,
) -> Result<(), MinusError> {
for line in lines {
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#![warn(clippy::nursery)]
#![allow(clippy::doc_markdown)]
#![cfg_attr(doctest, doc = include_str!("../README.md"))]
#![feature(test)]

//! `minus`: A library for asynchronous terminal [paging], written in Rust.
//!
Expand Down
39 changes: 39 additions & 0 deletions src/screen/data_source.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use arrayvec::ArrayString;

type InsertionBuffer = ArrayString<240>;

pub trait DataSource {
fn reads_forward(&mut self, buffer: &mut InsertionBuffer) -> bool;
fn reads_backward(&mut self, buffer: &mut InsertionBuffer) -> bool;
fn readr_line(&mut self, line: &mut String, pb: &mut InsertionBuffer, fb: &mut InsertionBuffer);
}

pub struct StringSource {
text: String,
cursor: usize,
}

impl DataSource for StringSource {
fn reads_forward(&mut self, buffer: &mut InsertionBuffer) -> bool {
let max_read = (self.text.len() - self.cursor).min(buffer.capacity());
buffer.push_str(&self.text[self.cursor..self.cursor + max_read]);
self.cursor += max_read + 1;

self.cursor < self.text.len()
}
fn reads_backward(&mut self, buffer: &mut InsertionBuffer) -> bool {
let max_read = self.text.len().min(buffer.capacity());
buffer.push_str(&self.text[self.cursor..max_read]);
self.cursor += max_read + 1;

self.cursor != 0
}

fn readr_line(
&mut self,
line: &mut String,
pb: &mut InsertionBuffer,
fb: &mut InsertionBuffer,
) {
}
}
124 changes: 52 additions & 72 deletions src/screen/mod.rs
9E7A
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,19 @@ use crate::{
};
#[cfg(feature = "search")]
use regex::Regex;

use smol_str::{SmolStr, ToSmolStr};
use std::borrow::Cow;

mod data_source;

#[cfg(feature = "search")]
use {crate::search, std::collections::BTreeSet};

// |||||||||||||||||||||||||||||||||||||||||||||||||||||||
// TYPES TO BETTER DESCRIBE THE PURPOSE OF STRINGS
// |||||||||||||||||||||||||||||||||||||||||||||||||||||||
pub type Row = String;
pub type Rows = Vec<String>;
pub type Row = SmolStr;
pub type Rows = Vec<SmolStr>;
pub type Line<'a> = &'a str;
pub type TextBlock<'a> = &'a str;
pub type OwnedTextBlock = String;
Expand Down Expand Up @@ -149,7 +151,7 @@ impl Default for Screen {
Self {
line_wrapping: true,
orig_text: String::with_capacity(100 * 1024),
formatted_lines: Vec::with_capacity(500 * 1024),
formatted_lines: Vec::with_capacity(10_000),
line_count: 0,
max_line_length: 0,
unterminated: 0,
Expand Down Expand Up @@ -359,12 +361,8 @@ where
to_format = opts.text.to_string();
}

let lines = to_format
.lines()
.enumerate()
.collect::<Vec<(usize, &str)>>();

let to_format_size = lines.len();
let lines = to_format.lines().enumerate();
let to_format_size = calculate_format_sizr(&to_format);

let mut fr = FormatResult {
lines_formatted: to_format_size,
Expand All @@ -380,71 +378,47 @@ where
let line_number_digits = minus_core::utils::digits(opts.lines_count + to_format_size);

// Return if we have nothing to format
if lines.is_empty() {
if to_format_size == 0 {
return fr;
}

// Number of rows that have been formatted so far
// Whenever a line is formatted, this will be incremented to te number of rows that the formatted line has occupied
let mut formatted_row_count = opts.formatted_lines_count;

{
let line_numbers = opts.line_numbers;
let cols = opts.cols;
let lines_count = opts.lines_count;
let line_wrapping = opts.line_wrapping;
#[cfg(feature = "search")]
let search_term = opts.search_term;

let rest_lines =
lines
.iter()
.take(lines.len().saturating_sub(1))
.flat_map(|(idx, line)| {< F438 /td>
let fmt_line = formatted_line(
line,
line_number_digits,
lines_count + idx,
line_numbers,
cols,
line_wrapping,
#[cfg(feature = "search")]
formatted_row_count,
#[cfg(feature = "search")]
&mut fr.append_search_idx,
#[cfg(feature = "search")]
search_term,
);
fr.lines_to_row_map.insert(formatted_row_count, true);
formatted_row_count += fmt_line.len();
if lines.len() > fr.max_line_length {
fr.max_line_length = line.len();
}

fmt_line
});
opts.buffer.extend_buffer(rest_lines);
};
let line_numbers = opts.line_numbers;
let cols = opts.cols;
let lines_count = opts.lines_count;
let line_wrapping = opts.line_wrapping;
let mut last_line_row_span = 0;
#[cfg(feature = "search")]
let search_term = opts.search_term;

let formatted_lines = lines.flat_map(|(idx, line)| {
let fmt_line = formatted_line(
line,
line_number_digits,
lines_count + idx,
line_numbers,
cols,
line_wrapping,
#[cfg(feature = "search")]
formatted_row_count,
#[cfg(feature = "search")]
&mut fr.append_search_idx,
#[cfg(feature = "search")]
search_term,
);
fr.lines_to_row_map.insert(formatted_row_count, true);
formatted_row_count += fmt_line.len();
last_line_row_span = fmt_line.len();
if line.len() > fr.max_line_length {
fr.max_line_length = line.len();
}

let mut last_line = formatted_line(
lines.last().unwrap().1,
line_number_digits,
opts.lines_count + to_format_size - 1,
opts.line_numbers,
opts.cols,
opts.line_wrapping,
#[cfg(feature = "search")]
formatted_row_count,
#[cfg(feature = "search")]
&mut fr.append_search_idx,
#[cfg(feature = "search")]
opts.search_term,
);
fr.lines_to_row_map.insert(formatted_row_count, true);
formatted_row_count += last_line.len();
if lines.last().unwrap().1.len() > fr.max_line_length {
fr.max_line_length = lines.last().unwrap().1.len();
}
fmt_line
});
opts.buffer.extend_buffer(formatted_lines);

#[cfg(feature = "search")]
{
Expand Down Expand Up @@ -478,14 +452,20 @@ where
// If the last line ends with \n, then the line is complete so nothing is left as unterminated
0
} else {
last_line.len()
last_line_row_span
};
opts.buffer.append_to_buffer(&mut last_line);
fr.rows_formatted = formatted_row_count - opts.formatted_lines_count;

fr
}

fn calculate_format_sizr(text: &str) -> usize {
if text.is_empty() {
return 0;
}
bytecount::count(text.as_bytes(), b'\n') + usize::from(!text.ends_with('\n'))
}

/// Formats the given `line`
///
/// - `line`: The line to format
Expand Down Expand Up @@ -568,7 +548,7 @@ pub(crate) fn formatted_line<'a>(
// 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: Cow<'_, str>, is_first_row: bool, idx: usize| {
format!(
smol_str::format_smolstr!(
"{bold}{number: >len$}{reset} {row}",
bold = if cfg!(not(test)) && is_first_row {
crossterm::style::Attribute::Bold.to_string()
Expand Down Expand Up @@ -615,9 +595,9 @@ pub(crate) fn formatted_line<'a>(
enumerated_rows
.map(|(wrap_idx, mut row)| {
handle_search(&mut row, wrap_idx);
row.to_string()
row.to_smolstr()
})
.collect::<Vec<String>>()
.collect::<Vec<SmolStr>>()
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@

#![allow(unused_imports)]
use crate::minus_core::utils::{display, term};
use crate::screen::Screen;
use crate::screen::{Row, Screen};
use crate::{error::MinusError, input::HashedEventRegister, screen};
use crate::{LineNumbers, PagerState};
use crossterm::{
Expand Down Expand Up @@ -242,7 +242,7 @@ impl FetchInputResult {
/// A cache for storing all the new data obtained by running incremental search
pub(crate) struct IncrementalSearchCache {
/// Lines to be displayed with highlighted search matches
pub(crate) formatted_lines: Vec<String>,
pub(crate) formatted_lines: Vec<Row>,
/// Index from `search_idx` where a search match after current upper mark may be found
/// NOTE: There is no guarantee that this will stay within the bounds of `search_idx`
pub(crate) search_mark: usize,
Expand Down
38 changes: 38 additions & 0 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,3 +398,41 @@ impl PagerState {
AppendStyle::PartialUpdate(fmt_lines)
}
}

#[cfg(test)]
mod bench {
extern crate test;
use super::PagerState;
use test::Bencher;

#[bench]
fn bench_append_str_chunks(b: &mut Bencher) {
let mut buffer = "This is a line\n".repeat(20);
// Remove the last \n from the text block
buffer.pop();

b.iter(|| {
for _ in 0..4_400_000 {
let mut ps = PagerState::new().unwrap();
ps.append_str(&buffer);
}
});
}

#[bench]
fn bench_append_str_big(b: &mut Bencher) {
let mut buffer = String::with_capacity(20 * 15 * 4_400_000);
let mut line = "This is a line\n".repeat(20);
// Remove the last \n from the text block
line.pop();

for _ in 0..4_400_000 {
buffer.push_str(&line);
}

b.iter(|| {
let mut ps = PagerState::new().unwrap();
ps.append_str(&buffer);
});
}
}
Loading
0