Skip to content
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

perf: only process visible lines #135

Merged
merged 3 commits into from
Aug 8, 2024
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
15 changes: 10 additions & 5 deletions src/actors/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use crate::config::{
};
use crate::exec::ExecBuilder;

use super::console::{Output, PanelStatus, RegisterPanel};
use super::console::{Output, OutputKind, PanelStatus, RegisterPanel};
use super::watcher::{IgnorePath, WatchGlob};

#[cfg(not(test))]
Expand Down Expand Up @@ -265,7 +265,8 @@ impl CommandActor {
fn log_info(&self, log: String) {
let job_name = self.operator.name.clone();

self.console.do_send(Output::now(job_name, log, true));
self.console
.do_send(Output::now(job_name, log, OutputKind::Service));
}

fn log_debug(&self, log: String) {
Expand Down Expand Up @@ -353,7 +354,11 @@ impl CommandActor {
colors: task_colors.clone(),
});
}
console.do_send(Output::now(tab_name.to_owned(), line.clone(), false));
console.do_send(Output::now(
tab_name.to_owned(),
line,
OutputKind::Command,
));
}
OutputRedirection::File(path) => {
let path = task_pipe.regex.replace(&line, path);
Expand Down Expand Up @@ -383,11 +388,11 @@ impl CommandActor {

// append new line since strings from the buffer reader don't include it
line.push('\n');
file.write_all(line.clone().as_bytes()).unwrap();
file.write_all(line.as_bytes()).unwrap();
}
}
} else {
console.do_send(Output::now(op_name.clone(), line.clone(), false));
console.do_send(Output::now(op_name.clone(), line, OutputKind::Command));
}
}

Expand Down
132 changes: 90 additions & 42 deletions src/actors/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ impl AppMode {
}

pub struct Panel {
logs: Vec<(String, Style)>,
lines: u16,
logs: Vec<(String, OutputKind)>,
line_offsets: Vec<usize>,
shift: u16,
command: Addr<CommandActor>,
status: Option<ExitStatus>,
Expand All @@ -76,13 +76,22 @@ impl Panel {
pub fn new(command: Addr<CommandActor>, colors: Vec<ColorOption>) -> Self {
Self {
logs: Vec::default(),
lines: 0,
line_offsets: Vec::default(),
shift: 0,
command,
status: None,
colors,
}
}

pub fn sync_lines(&mut self, width: u16) {
self.line_offsets = self
.logs
.iter()
.enumerate()
.flat_map(|(i, l)| vec![i; wrapped_lines(&l.0, width)])
.collect();
}
}

pub struct ConsoleActor {
Expand Down Expand Up @@ -138,7 +147,8 @@ impl ConsoleActor {
if let Some(focused_panel) = self.panels.get_mut(&self.index) {
// maximum_scroll is the number of lines
// overflowing in the current focused panel
let maximum_scroll = focused_panel.lines - min(focused_panel.lines, log_height);
let lines = focused_panel.line_offsets.len() as u16;
let maximum_scroll = lines - min(lines, log_height);

// `focused_panel.shift` goes from 0 until maximum_scroll
focused_panel.shift = min(focused_panel.shift + shift, maximum_scroll);
Expand Down Expand Up @@ -201,23 +211,45 @@ impl ConsoleActor {
.draw(|f| {
let chunks = chunks(&self.mode, &self.layout_direction, f);
let logs = &focused_panel.logs;

let log_height = chunks[0].height;
let maximum_scroll = focused_panel.lines - min(focused_panel.lines, log_height);

let lines: Vec<Line> = logs
.iter()
.flat_map(|(str, base_style)| {
let colorizer = Colorizer::new(&focused_panel.colors, *base_style);
colorizer.patch_text(str)
let shift = focused_panel.shift as usize;
let line_offsets = &focused_panel.line_offsets;
let lines = line_offsets.len();
let log_height = chunks[0].height as usize;

let maximum_scroll = lines - min(lines, log_height);
zifeo marked this conversation as resolved.
Show resolved Hide resolved
let scroll_offset = maximum_scroll - min(maximum_scroll, shift);
let offset_end = min(lines, scroll_offset + log_height).wrapping_sub(1);

let line_start = line_offsets.get(scroll_offset).cloned().unwrap_or(0);
let line_end = line_offsets.get(offset_end).cloned().unwrap_or(0);

let wrap_offset = line_offsets
.get(..scroll_offset)
.map(|offsets| {
offsets
.iter()
.rev()
.take_while(|&line| *line == line_start)
.count()
})
.collect();
.unwrap_or(0);

let lines = logs
.get(line_start..=line_end)
.map(|logs| {
logs.iter()
.flat_map(|(s, kind)| {
Colorizer::new(&focused_panel.colors, kind.style())
.patch_text(s)
})
.collect::<Vec<_>>()
})
.unwrap_or_default();

let paragraph = Paragraph::new(lines).wrap(Wrap { trim: false });
let paragraph = Paragraph::new(lines)
.wrap(Wrap { trim: false })
.scroll((wrap_offset as u16, 0));

// scroll by default until the last line
let paragraph = paragraph
.scroll((maximum_scroll - min(maximum_scroll, focused_panel.shift), 0));
f.render_widget(paragraph, chunks[0]);

//Format titles
Expand Down Expand Up @@ -303,8 +335,18 @@ impl ConsoleActor {
}
}

pub fn resize_panels(&mut self, width: u16) {
for panel in self.panels.values_mut() {
panel.shift = 0;
panel.sync_lines(width)
}
}

pub fn switch_layout(&mut self) {
self.layout_direction = self.layout_direction.get_opposite_orientation();
let f = self.terminal.get_frame();
let chunks = chunks(&self.mode, &self.layout_direction, &f);
self.resize_panels(chunks[0].width);
}
pub fn switch_mode(&mut self) {
self.mode = self.mode.get_opposite_mode();
Expand Down Expand Up @@ -432,16 +474,7 @@ impl Handler<TermEvent> for ConsoleActor {
},
_ => {}
},
Event::Resize(width, _) => {
for panel in self.panels.values_mut() {
panel.shift = 0;
let new_lines = panel
.logs
.iter()
.fold(0, |agg, l| agg + wrapped_lines(&l.0, width));
panel.lines = new_lines;
}
}
Event::Resize(width, _) => self.resize_panels(width),
Event::Mouse(e) => match e.kind {
MouseEventKind::ScrollUp => {
self.up(1);
Expand All @@ -457,29 +490,44 @@ impl Handler<TermEvent> for ConsoleActor {
}
}

#[derive(Debug)]
pub enum OutputKind {
Service,
Command,
}

impl OutputKind {
fn style(&self) -> Style {
match self {
OutputKind::Service => Style::default().bg(Color::DarkGray),
OutputKind::Command => Style::default(),
}
}
}

#[derive(Message)]
#[rtype(result = "()")]
pub struct Output {
panel_name: String,
pub message: String,
service: bool,
kind: OutputKind,
timestamp: DateTime<Local>,
}

impl Output {
pub fn now(panel_name: String, message: String, service: bool) -> Self {
pub fn now(panel_name: String, message: String, kind: OutputKind) -> Self {
Self {
panel_name,
message,
service,
kind,
timestamp: Local::now(),
}
}
}

fn wrapped_lines(message: &String, width: u16) -> u16 {
fn wrapped_lines(message: &String, width: u16) -> usize {
let clean = strip_ansi_escapes::strip(message);
textwrap::wrap(str::from_utf8(&clean).unwrap(), width as usize).len() as u16
textwrap::wrap(str::from_utf8(&clean).unwrap(), width as usize).len()
}

// Replace the character that are max that MAX_CHARS with an ellipse ...
Expand All @@ -502,19 +550,19 @@ impl Handler<Output> for ConsoleActor {
type Result = ();

fn handle(&mut self, msg: Output, _: &mut Context<Self>) -> Self::Result {
let panel = self.panels.get_mut(&msg.panel_name).unwrap();
let style = match msg.service {
true => Style::default().bg(Color::DarkGray),
false => Style::default(),
};

let message = match self.timestamp {
true => format_message(&msg.message, &msg.timestamp),
false => msg.message,
};

let panel = self.panels.get_mut(&msg.panel_name).unwrap();
let width = self.terminal.get_frame().size().width;
panel.lines += wrapped_lines(&message, width);
panel.logs.push((message, style));
let line_count = wrapped_lines(&message, width);
let line_offset = panel.logs.len();

panel.line_offsets.extend(vec![line_offset; line_count]);
panel.logs.push((message, msg.kind));

self.draw();
}
}
Expand Down Expand Up @@ -558,7 +606,7 @@ impl Handler<PanelStatus> for ConsoleActor {

if let Some(message) = msg.status.map(|c| format!("Status: {:?}", c)) {
ctx.address()
.do_send(Output::now(msg.panel_name, message, true));
.do_send(Output::now(msg.panel_name, message, OutputKind::Service));
}

self.draw();
Expand Down
10 changes: 3 additions & 7 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use anyhow::{Ok, Result};
use subprocess::ExitStatus;

use crate::actors::command::{CommandActorsBuilder, WaitStatus};
use crate::actors::console::RegisterPanel;
use crate::actors::console::{OutputKind, RegisterPanel};
use crate::actors::watcher::WatchGlob;
use crate::args::Args;
use crate::config::{ConfigInner, RawConfig};
Expand Down Expand Up @@ -94,19 +94,15 @@ fn hello() {
.send(Output::now(
"test".to_string(),
"message".to_string(),
false,
OutputKind::Command,
))
.await?;

let commands = CommandActorsBuilder::new(config, console, watcher)
.build()
.await?;

let status = commands
.get("test")
.unwrap()
.send(WaitStatus)
.await?;
let status = commands.get("test").unwrap().send(WaitStatus).await?;
println!("status: {:?}", status);

Ok(())
Expand Down