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
22 changes: 7 additions & 15 deletions src/core/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,16 @@ use crate::{
ExitStrategy, LineNumbers, OutputSink,
hooks::{Hook, HookCallback},
input::{InputClassifier, InputEvent},
minus_core::utils::display::AppendStyle,
source::DataSource,
};

#[cfg(feature = "search")]
use crate::search::SearchOpts;

#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum IoCommand {
RedrawPrompt,
RedrawDisplay,
/// Append text to the screen
///
/// First item corresponds to the value of unterminated lines before the text is formatted while
/// the second value corresponds to the total number of rows before formatting.
DrawAppendedText(usize, usize, AppendStyle),
SetUpperMark(usize),
#[cfg(feature = "search")]
FetchSearchQuery,
Expand All @@ -37,8 +32,7 @@ pub enum Command {
UserInput(InputEvent),

// Data related
AppendData(String),
SetData(String),
SetDataSource(Box<dyn DataSource>),

// Prompt related
SendMessage(String),
Expand Down Expand Up @@ -68,9 +62,7 @@ pub enum Command {
impl PartialEq for Command {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::SetData(d1), Self::SetData(d2))
| (Self::AppendData(d1), Self::AppendData(d2))
| (Self::SetPrompt(d1), Self::SetPrompt(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,
Expand All @@ -81,7 +73,8 @@ impl PartialEq for Command {
(Self::SetInputClassifier(_), Self::SetInputClassifier(_))
| (Self::AddExitCallback(_), Self::AddExitCallback(_))
| (Self::AddHook(..), Self::AddHook(..))
| (Self::SetOutputSink(_), Self::SetOutputSink(_)) => true,
| (Self::SetOutputSink(_), Self::SetOutputSink(_))
| (Self::SetDataSource(_), Self::SetDataSource(_)) => true,
(Self::RemoveHook(h1, id1), Self::RemoveHook(h2, id2)) => h1 == h2 && id1 == id2,
#[cfg(feature = "search")]
(Self::IncrementalSearchCondition(_), Self::IncrementalSearchCondition(_)) => true,
Expand All @@ -94,8 +87,7 @@ 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::SetDataSource(_) => write!(f, "SetDataSource"),
Self::SetPrompt(text) => write!(f, "SetPrompt({text:?})"),
Self::SendMessage(text) => write!(f, "SendMessage({text:?})"),
Self::SetLineNumbers(ln) => write!(f, "SetLineNumbers({ln:?})"),
Expand Down
119 changes: 36 additions & 83 deletions src/core/ev_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use parking_lot::{Condvar, Mutex};

use super::CommandQueue;
use super::commands::{Command, IoCommand};
use super::utils::display::{self, AppendStyle};
use super::utils::display::{self};
use crate::ExitStrategy;
#[cfg(feature = "search")]
use crate::search;
Expand All @@ -32,14 +32,14 @@ pub fn handle_event(
is_exited: &Arc<AtomicBool>,
) {
match ev {
Command::SetData(text) => {
if let Some(ref mut hs) = p.help_state {
hs.screen.orig_text = text;
hs.screen.line_count = hs.screen.orig_text.lines().count();
Command::SetDataSource(source) => {
if let Some(hs) = &mut p.help_state {
// The help screen owns the data source while it is active; remember the new
// source so it takes effect when help exits
hs.data_source = source;
return;
}
p.screen.orig_text = text;
p.screen.line_count = p.screen.orig_text.lines().count();
p.data_source = source;
p.reformat_display();
command_queue.push_back(Command::Io(IoCommand::RedrawDisplay));
}
Expand Down Expand Up @@ -305,34 +305,6 @@ pub fn handle_event(
command_queue.push_back(Command::Io(IoCommand::RedrawDisplay));
}

Command::AppendData(text) => {
if let Some(ref mut hs) = p.help_state {
hs.screen.orig_text.push_str(&text);
hs.screen.line_count = hs.screen.orig_text.lines().count();
return;
}
let prev_unterminated = p.screen.unterminated;
let prev_fmt_lines_count = p.screen.formatted_lines_count();
let append_style = p.append_str(text.as_str());

if append_style == AppendStyle::FullRedraw {
command_queue.push_back(Command::Io(IoCommand::RedrawDisplay));
return;
}

command_queue.push_back(Command::Io(IoCommand::DrawAppendedText(
prev_unterminated,
prev_fmt_lines_count,
append_style,
)));

if p.follow_output {
command_queue.push_back(Command::Io(IoCommand::SetUpperMark(
p.screen.formatted_lines_count(),
)));
}
}

Command::SetPrompt(ref text) | Command::SendMessage(ref text) => {
if let Command::SetPrompt(_) = ev {
p.prompt = text.clone();
Expand Down Expand Up @@ -432,19 +404,6 @@ pub fn handle_io_command(
}
p.upper_mark = um;
}
IoCommand::DrawAppendedText(prev_unterminated, prev_fmt_lines_count, append_style) => {
let AppendStyle::PartialUpdate(bounds) = append_style else {
unreachable!();
};
let fmt_lines = p.render_rows_for_display(bounds.0, bounds.1);
display::draw_append_text(
out,
p.rows,
prev_unterminated,
prev_fmt_lines_count,
&fmt_lines,
)?;
}
#[cfg(feature = "search")]
IoCommand::FetchSearchQuery => {
// Pause the main user input thread, read search query and then restart the main input thread
Expand Down Expand Up @@ -495,7 +454,7 @@ pub fn handle_io_command(
mod tests {
use super::super::commands::{Command, IoCommand};
use super::handle_event;
use crate::{PagerState, input::InputEvent, minus_core::CommandQueue, state::Selection};
use crate::{PagerState, input::InputEvent, minus_core::CommandQueue, source::InMemorySource, state::Selection};
use std::fmt::Write;
use std::sync::{Arc, atomic::AtomicBool};

Expand All @@ -504,9 +463,9 @@ mod tests {
// Tests for event emitting functions of Pager
#[test]
#[cfg(any(feature = "dynamic_output", feature = "static_output"))]
fn set_data() {
fn set_data_source() {
let mut ps = PagerState::new().unwrap();
let ev = Command::SetData(TEST_STR.to_string());
let ev = Command::SetDataSource(Box::new(InMemorySource::from(TEST_STR)));
let mut command_queue = CommandQueue::new_zero();

handle_event(
Expand All @@ -520,24 +479,14 @@ mod tests {
}

#[test]
fn append_str() {
fn appended_lines_are_served_from_source() {
let mut ps = PagerState::new().unwrap();
let ev1 = Command::AppendData(format!("{TEST_STR}\n"));
let ev2 = Command::AppendData(TEST_STR.to_string());
let mut command_queue = CommandQueue::new_zero();
let mut source = InMemorySource::new();
source.append(&format!("{TEST_STR}\n"));
source.append(TEST_STR);
ps.data_source = Box::new(source);
ps.reformat_display();

handle_event(
ev1,
&mut ps,
&mut command_queue,
&Arc::new(AtomicBool::new(false)),
);
handle_event(
ev2,
&mut ps,
&mut command_queue,
&Arc::new(AtomicBool::new(false)),
);
assert_eq!(
ps.screen.formatted_lines,
vec![TEST_STR.to_string(), TEST_STR.to_string()]
Expand Down Expand Up @@ -579,7 +528,7 @@ mod tests {
#[test]
fn show_help() {
let mut ps = PagerState::new().unwrap();
ps.screen.orig_text = "original text\n".to_string();
ps.data_source = Box::new(InMemorySource::from("original text\n"));
ps.reformat_display();
ps.upper_mark = 0;

Expand All @@ -594,7 +543,11 @@ mod tests {
&Arc::new(AtomicBool::new(false)),
);
assert!(ps.help_state.is_some());
assert!(ps.screen.orig_text.contains("COMMAND SUMMARY"));
assert!(ps
.screen
.formatted_lines
.iter()
.any(|row| row.contains("COMMAND SUMMARY")));
assert!(ps.prompt.contains("HELP"));

// Pressing help again toggles it off and restores original text
Expand All @@ -606,7 +559,7 @@ mod tests {
&Arc::new(AtomicBool::new(false)),
);
assert!(ps.help_state.is_none());
assert_eq!(ps.screen.orig_text, "original text\n");
assert_eq!(ps.screen.formatted_lines, vec!["original text".to_string()]);

// Showing help then exiting with Exit returns to pager
handle_event(
Expand All @@ -625,7 +578,7 @@ mod tests {
);
assert!(ps.help_state.is_none());
assert!(!is_exited.load(std::sync::atomic::Ordering::SeqCst));
assert_eq!(ps.screen.orig_text, "original text\n");
assert_eq!(ps.screen.formatted_lines, vec!["original text".to_string()]);
}

#[test]
Expand Down Expand Up @@ -663,10 +616,10 @@ mod tests {
fn update_selection_scrolls_up_at_top_edge() {
let mut ps = PagerState::new().unwrap();
ps.rows = 5;
ps.screen.orig_text = (0..10).fold(String::new(), |mut t, idx| {
let _ = writeln!(t, "line {idx}");
t
});
ps.data_source = Box::new((0..10).fold(InMemorySource::new(), |mut s, idx| {
let _ = writeln!(s, "line {idx}");
s
}));
ps.reformat_display();
ps.upper_mark = 3;
ps.selection_anchor = Some(Selection {
Expand Down Expand Up @@ -701,10 +654,10 @@ mod tests {
fn update_selection_scrolls_down_at_bottom_edge() {
let mut ps = PagerState::new().unwrap();
ps.rows = 5;
ps.screen.orig_text = (0..10).fold(String::new(), |mut t, idx| {
let _ = writeln!(t, "line {idx}");
t
});
ps.data_source = Box::new((0..10).fold(InMemorySource::new(), |mut s, idx| {
let _ = writeln!(s, "line {idx}");
s
}));
ps.reformat_display();
ps.upper_mark = 3;
ps.selection_anchor = Some(Selection {
Expand Down Expand Up @@ -739,10 +692,10 @@ mod tests {
fn update_selection_clamps_scroll_at_bottom_bound() {
let mut ps = PagerState::new().unwrap();
ps.rows = 5;
ps.screen.orig_text = (0..6).fold(String::new(), |mut t, idx| {
let _ = writeln!(t, "line {idx}");
t
});
ps.data_source = Box::new((0..6).fold(InMemorySource::new(), |mut s, idx| {
let _ = writeln!(s, "line {idx}");
s
}));
ps.reformat_display();
ps.upper_mark = 2;
ps.selection_anchor = Some(Selection {
Expand Down
61 changes: 43 additions & 18 deletions src/core/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::{
input::InputEvent,
minus_core::{
RunMode,
commands::Command,
commands::{Command, IoCommand},
ev_handler::handle_event,
utils::{display::draw_full, term},
},
Expand Down Expand Up @@ -96,7 +96,11 @@ pub fn init_core(pager: &Pager, rm: RunMode) -> std::result::Result<(), MinusErr
let mut out = output_sink.lock();
// If output sink is not a tty, write everything and quit
if !out.is_tty() {
write_raw_lines(&mut *out, &[ps.screen.orig_text], None)?;
let lines: Vec<String> = (0..ps.data_source.line_count())
.filter_map(|idx| ps.data_source.line(idx))
.map(std::borrow::Cow::into_owned)
.collect();
write_raw_lines(&mut *out, &lines, None)?;
drop(out);
*RUNMODE.lock() = RunMode::Uninitialized;
return Ok(());
Expand Down Expand Up @@ -244,25 +248,46 @@ fn start_reactor(
}

let next_command = if command_queue.is_empty() {
rx.recv()
// A timeout instead of a blocking receive so that changes in the data source
// are polled while waiting for commands
rx.recv_timeout(std::time::Duration::from_millis(100)).ok()
} else {
Ok(command_queue.pop_front().unwrap())
Some(command_queue.pop_front().unwrap())
};

let mut p = ps.lock();
if let Ok(Command::Io(ic)) = next_command {
use crate::minus_core::ev_handler::handle_io_command;

handle_io_command(
ic,
&mut *output_sink.lock(),
&mut p,
&mut command_queue,
#[cfg(feature = "search")]
input_thread_running,
)?;
} else if let Ok(command) = next_command {
handle_event(command, &mut p, &mut command_queue, is_exited);
let should_reformat = {
let mut p = ps.lock();
if let Some(Command::Io(ic)) = next_command {
use crate::minus_core::ev_handler::handle_io_command;

handle_io_command(
ic,
&mut *output_sink.lock(),
&mut p,
&mut command_queue,
#[cfg(feature = "search")]
input_thread_running,
)?;
} else if let Some(command) = next_command {
handle_event(command, &mut p, &mut command_queue, is_exited);
}

// Poll the data source for changes and reformat when it differs from what
// minus has already formatted
command_queue.is_empty() && p.source_changed(p.data_source.as_ref())
};

if should_reformat {
let (follow, fmt_count) = {
let mut p = ps.lock();
p.reformat_display();
(p.follow_output, p.screen.formatted_lines_count())
};
if follow {
command_queue.push_back(Command::Io(IoCommand::SetUpperMark(fmt_count)));
} else {
command_queue.push_back(Command::Io(IoCommand::RedrawDisplay));
}
}
},
#[cfg(feature = "static_output")]
Expand Down
Loading
Loading