Skip to content
Merged
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
49 changes: 24 additions & 25 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ repository = "https://github.com/ReagentX/Logria"
version = "0.0.0"

[dependencies]
clap = { version = "=4.5.58", features = ["cargo"] }
clap = { version = "=4.5.60", features = ["cargo"] }
crossterm = "=0.29.0"
dirs = "=6.0.0"
is_executable = "=1.0.5"
Expand Down
22 changes: 13 additions & 9 deletions src/communication/handlers/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,18 +146,10 @@ impl ParserHandler {

match message_parts {
Ok(message_parts) => {
// If we got this far, allocate the return value
let mut aggregated_data = vec![];
for (idx, part) in message_parts.iter().enumerate() {
if let Some(item) = parser.order.get(idx).cloned() {
if let Some(aggregator) = parser.aggregator_map.get_mut(&item) {
aggregator.update(part)?;
if render {
// Name of aggregated part
aggregated_data.push(item);
// Messages generated for that aggregator
aggregated_data.extend(aggregator.messages(num_to_get));
}
} else {
return Err(LogriaError::InvalidParserState(format!(
"aggregator missing for {item}!"
Expand All @@ -170,7 +162,19 @@ impl ParserHandler {
));
}
}
Ok(aggregated_data)
// Only generate display messages on the final iteration
if render {
let mut aggregated_data = vec![];
for item in &parser.order {
if let Some(aggregator) = parser.aggregator_map.get(item) {
aggregated_data.push(item.clone());
aggregated_data.extend(aggregator.messages(num_to_get));
}
}
Ok(aggregated_data)
} else {
Ok(vec![])
}
}
Err(why) => Err(why),
}
Expand Down
9 changes: 2 additions & 7 deletions src/communication/handlers/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use regex::bytes::Regex;
use super::{handler::Handler, processor::update_progress, user_input::UserInputHandler};
use crate::{
communication::{input::InputType::Normal, reader::MainWindow},
constants::cli::{cli_chars::NORMAL_STR, patterns::ANSI_COLOR_PATTERN},
constants::cli::{cli_chars::NORMAL_STR, patterns::ANSI_COLOR_REGEX},
};

/// Shared state and logic for regex filtering and highlight searching.
Expand All @@ -14,26 +14,21 @@ use crate::{
/// to avoid duplicating pattern matching, match processing, and
/// cleanup logic.
pub struct SearchState {
color_pattern: Regex,
pub current_pattern: Option<Regex>,
pub input_handler: UserInputHandler,
}

impl SearchState {
pub fn new() -> Self {
SearchState {
color_pattern: Regex::new(ANSI_COLOR_PATTERN).unwrap(),
current_pattern: None,
input_handler: UserInputHandler::new(),
}
}

/// Test a message to see if it matches the pattern while also escaping the color code
pub fn test(&self, message: &str) -> bool {
// TODO: Possibly without the extra allocation here?
let clean_message = self
.color_pattern
.replace_all(message.as_bytes(), "".as_bytes());
let clean_message = ANSI_COLOR_REGEX.replace_all(message.as_bytes(), "".as_bytes());
match &self.current_pattern {
Some(pattern) => pattern.is_match(&clean_message),
None => panic!("Match called with no pattern!"),
Expand Down
10 changes: 8 additions & 2 deletions src/communication/handlers/startup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,15 @@ mod startup_tests {
let mut handler = StartupHandler::new();
handler.initialize();

// Find the correct index for the "ls -la" session
let sessions = Session::list_full();
let idx = sessions
.iter()
.position(|s| s.ends_with("/ls -la"))
.expect("ls -la session not found");

// Tests
assert!(handler.process_command(&mut window, "0").is_ok());
assert!(handler.process_command(&mut window, &idx.to_string()).is_ok());
assert!(matches!(window.input_type, InputType::Normal));
assert!(matches!(window.config.stream_type, StreamType::StdErr));
}
Expand Down Expand Up @@ -227,6 +234,5 @@ mod startup_tests {
);
assert!(matches!(window.input_type, InputType::Startup));
assert!(matches!(window.config.stream_type, StreamType::Auxiliary));
Session::del(&[Session::list_full().len() - 1]).unwrap();
}
}
3 changes: 2 additions & 1 deletion src/communication/handlers/user_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ impl UserInputHandler {
/// Remove char 1 to the left of the cursor
fn backspace(&mut self, window: &mut MainWindow) -> Result<()> {
if self.last_write >= 1 && !self.content.is_empty() {
self.content.remove(self.position_as_index() - 1);
self.content
.remove(self.position_as_index().saturating_sub(1));
self.move_left()?;
self.write(window)?;
}
Expand Down
42 changes: 11 additions & 31 deletions src/communication/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use std::{
process::{Child, Command, Stdio},
result::Result,
sync::{
Arc, Mutex,
Arc,
atomic::{AtomicBool, Ordering},
mpsc::{Receiver, channel},
},
Expand Down Expand Up @@ -139,10 +139,6 @@ impl Input for CommandInput {
let should_die = Arc::new(AtomicBool::new(false));
let should_die_clone = Arc::clone(&should_die);

// Handle poll rate for each stream
let poll_rate_stdout = Arc::new(Mutex::new(RollingMean::new(5)));
let poll_rate_stderr = Arc::new(Mutex::new(RollingMean::new(5)));

// Start reading from the queues
let handle = thread::Builder::new()
.name(format!("CommandInput: {name}"))
Expand All @@ -153,13 +149,9 @@ impl Input for CommandInput {

// Create threads to read stdout and stderr independently
let die_clone = Arc::clone(&should_die_clone);
let poll_stdout = poll_rate_stdout.clone();
let stdout_handle = thread::spawn(move || {
let mut poll_rate = RollingMean::new(5);
loop {
thread::sleep(time::Duration::from_millis(
poll_stdout.lock().unwrap().mean(),
));

// Exit if the process is requested to die
if die_clone.load(Ordering::Relaxed) {
break;
Expand All @@ -170,32 +162,24 @@ impl Input for CommandInput {
stdout_reader.read_line(&mut buf_stdout).unwrap();

if buf_stdout.is_empty() {
poll_stdout
.lock()
.unwrap()
.update(ms_per_message(timestamp.elapsed(), 0));
poll_rate.update(ms_per_message(timestamp.elapsed(), 0));
// Back off when no data is available (EOF/empty read)
thread::sleep(time::Duration::from_millis(poll_rate.mean()));
continue;
}

if out_tx.send(buf_stdout).is_err() {
break;
}

poll_stdout
.lock()
.unwrap()
.update(ms_per_message(timestamp.elapsed(), 1));
poll_rate.update(ms_per_message(timestamp.elapsed(), 1));
}
});

let die_clone = Arc::clone(&should_die_clone);
let poll_stderr = poll_rate_stderr.clone();
let stderr_handle = thread::spawn(move || {
let mut poll_rate = RollingMean::new(5);
loop {
thread::sleep(time::Duration::from_millis(
poll_stderr.lock().unwrap().mean(),
));

// Exit if the process is requested to die
if die_clone.load(Ordering::Relaxed) {
break;
Expand All @@ -206,21 +190,17 @@ impl Input for CommandInput {
stderr_reader.read_line(&mut buf_stderr).unwrap();

if buf_stderr.is_empty() {
poll_stderr
.lock()
.unwrap()
.update(ms_per_message(timestamp.elapsed(), 0));
poll_rate.update(ms_per_message(timestamp.elapsed(), 0));
// Back off when no data is available (EOF/empty read)
thread::sleep(time::Duration::from_millis(poll_rate.mean()));
continue;
}

if err_tx.send(buf_stderr).is_err() {
break;
}

poll_stderr
.lock()
.unwrap()
.update(ms_per_message(timestamp.elapsed(), 1));
poll_rate.update(ms_per_message(timestamp.elapsed(), 1));
}
});

Expand Down
Loading
Loading