use std::io::Write;
use super::term::cleanup;
#[cfg(feature = "search")]
use crate::search;
use crate::{error::MinusError, input::InputEvent, PagerState};
use crate::{events::Event, wrap_str};
#[cfg(feature = "search")]
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
pub(crate) fn handle_event(
ev: Event,
mut out: &mut impl Write,
p: &mut PagerState,
is_exitted: &mut bool,
#[cfg(feature = "search")] event_thread_running: &Arc<AtomicBool>,
) -> Result<(), MinusError> {
match ev {
Event::SetData(text) => {
p.lines = text;
p.format_lines();
}
Event::UserInput(InputEvent::Exit) => {
p.exit();
*is_exitted = true;
cleanup(&mut out, &p.exit_strategy, true)?;
}
Event::UserInput(InputEvent::UpdateUpperMark(um)) => p.upper_mark = um,
Event::UserInput(InputEvent::RestorePrompt) => {
p.message.0 = None;
p.message.1 = false;
p.format_lines();
}
Event::UserInput(InputEvent::UpdateTermArea(c, r)) => {
p.rows = r;
p.cols = c;
p.format_lines();
}
Event::UserInput(InputEvent::UpdateLineNumber(l)) => {
p.line_numbers = l;
p.format_lines();
}
#[cfg(feature = "search")]
Event::UserInput(InputEvent::Search(m)) => {
p.search_mode = m;
event_thread_running.swap(false, Ordering::SeqCst);
let string = search::fetch_input(&mut out, p.search_mode, p.rows)?;
event_thread_running.swap(true, Ordering::SeqCst);
if !string.is_empty() {
let regex = regex::Regex::new(&string);
if let Ok(r) = regex {
p.search_term = Some(r);
search::set_match_indices(p);
search::next_match(p);
} else {
p.message.0 = Some(crate::wrap_str(
"Invalid regular expression. Press Enter",
p.cols,
));
p.message.1 = true;
}
}
p.format_lines();
}
#[cfg(feature = "search")]
Event::UserInput(InputEvent::NextMatch) if p.search_term.is_some() => {
if p.search_mark < p.search_idx.len().saturating_sub(1)
&& p.upper_mark + p.rows < p.num_lines()
{
p.search_mark += 1;
}
search::next_match(p);
}
#[cfg(feature = "search")]
Event::UserInput(InputEvent::PrevMatch) if p.search_term.is_some() => {
if p.search_idx.is_empty() {
return Ok(());
}
p.search_mark = p.search_mark.saturating_sub(1);
let y = p.search_idx[p.search_mark];
if y < p.upper_mark {
p.upper_mark = y;
}
}
Event::AppendData(text) => p.append_str(&text),
Event::SetPrompt(prompt) => p.prompt = wrap_str(&prompt, p.cols),
Event::SendMessage(message) => {
p.message.0 = Some(wrap_str(&message, p.cols));
p.message.1 = true;
}
Event::SetLineNumbers(ln) => {
p.line_numbers = ln;
p.format_lines();
}
Event::SetExitStrategy(es) => p.exit_strategy = es,
#[cfg(feature = "static_output")]
Event::SetRunNoOverflow(val) => p.run_no_overflow = val,
Event::SetInputClassifier(clf) => p.input_classifier = clf,
Event::AddExitCallback(cb) => p.exit_callbacks.push(cb),
Event::UserInput(_) => {}
}
Ok(())
}
#[cfg(test)]
mod tests {
#[cfg(feature = "search")]
use std::sync::{atomic::AtomicBool, Arc};
use crate::{events::Event, ExitStrategy, PagerState};
use super::handle_event;
const TEST_STR: &str = "This is some sample text";
#[test]
fn set_data() {
let mut ps = PagerState::new().unwrap();
let ev = Event::SetData(TEST_STR.to_string());
let mut out = Vec::new();
#[cfg(feature = "search")]
let etr = Arc::new(AtomicBool::new(true));
handle_event(
ev,
&mut out,
&mut ps,
&mut false,
#[cfg(feature = "search")]
&etr,
)
.unwrap();
assert_eq!(ps.formatted_lines, vec![TEST_STR.to_string()]);
}
#[test]
fn append_str() {
let mut ps = PagerState::new().unwrap();
let ev1 = Event::AppendData(format!("{}\n", TEST_STR));
let ev2 = Event::AppendData(TEST_STR.to_string());
let mut out = Vec::new();
#[cfg(feature = "search")]
let etr = Arc::new(AtomicBool::new(true));
handle_event(
ev1,
&mut out,
&mut ps,
&mut false,
#[cfg(feature = "search")]
&etr,
)
.unwrap();
handle_event(
ev2,
&mut out,
&mut ps,
&mut false,
#[cfg(feature = "search")]
&etr,
)
.unwrap();
assert_eq!(
ps.formatted_lines,
vec![TEST_STR.to_string(), TEST_STR.to_string()]
);
}
#[test]
fn set_prompt() {
let mut ps = PagerState::new().unwrap();
let ev = Event::SetPrompt(TEST_STR.to_string());
let mut out = Vec::new();
#[cfg(feature = "search")]
let etr = Arc::new(AtomicBool::new(true));
handle_event(
ev,
&mut out,
&mut ps,
&mut false,
#[cfg(feature = "search")]
&etr,
)
.unwrap();
assert_eq!(ps.prompt, vec![TEST_STR.to_string()]);
}
#[test]
fn send_message() {
let mut ps = PagerState::new().unwrap();
let ev = Event::SendMessage(TEST_STR.to_string());
let mut out = Vec::new();
#[cfg(feature = "search")]
let etr = Arc::new(AtomicBool::new(true));
handle_event(
ev,
&mut out,
&mut ps,
&mut false,
#[cfg(feature = "search")]
&etr,
)
.unwrap();
assert_eq!(ps.message.0.unwrap(), vec![TEST_STR.to_string()]);
assert!(ps.message.1);
}
#[test]
#[cfg(feature = "static_output")]
fn set_run_no_overflow() {
let mut ps = PagerState::new().unwrap();
let ev = Event::SetRunNoOverflow(false);
let mut out = Vec::new();
#[cfg(feature = "search")]
let etr = Arc::new(AtomicBool::new(true));
handle_event(
ev,
&mut out,
&mut ps,
&mut false,
#[cfg(feature = "search")]
&etr,
)
.unwrap();
assert!(!ps.run_no_overflow);
}
#[test]
fn set_exit_strategy() {
let mut ps = PagerState::new().unwrap();
let ev = Event::SetExitStrategy(ExitStrategy::PagerQuit);
let mut out = Vec::new();
#[cfg(feature = "search")]
let etr = Arc::new(AtomicBool::new(true));
handle_event(
ev,
&mut out,
&mut ps,
&mut false,
#[cfg(feature = "search")]
&etr,
)
.unwrap();
assert_eq!(ps.exit_strategy, ExitStrategy::PagerQuit);
}
#[test]
fn add_exit_callback() {
let mut ps = PagerState::new().unwrap();
let ev = Event::AddExitCallback(Box::new(|| println!("Hello World")));
let mut out = Vec::new();
#[cfg(feature = "search")]
let etr = Arc::new(AtomicBool::new(true));
handle_event(
ev,
&mut out,
&mut ps,
&mut false,
#[cfg(feature = "search")]
&etr,
)
.unwrap();
assert_eq!(ps.exit_callbacks.len(), 1);
}
}