From 4086fc1f83673554f9b9695e853c44dd895add23 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Tue, 11 Jan 2022 00:10:56 -0500 Subject: highlight the specific pipeline that is currently running --- src/parse/ast.rs | 7 +++++ src/runner/mod.rs | 3 ++ src/shell/event.rs | 9 ++++++ src/shell/history/entry.rs | 70 ++++++++++++++++++++++++++++++++++++---------- src/shell/history/mod.rs | 7 +++++ src/shell/mod.rs | 3 ++ 6 files changed, 85 insertions(+), 14 deletions(-) diff --git a/src/parse/ast.rs b/src/parse/ast.rs index a3239ce..6a3ab8e 100644 --- a/src/parse/ast.rs +++ b/src/parse/ast.rs @@ -82,6 +82,7 @@ impl Command { #[derive(Debug, Clone, PartialEq, Eq)] pub struct Pipeline { exes: Vec, + span: (usize, usize), } impl Pipeline { @@ -95,10 +96,16 @@ impl Pipeline { }) } + pub fn span(&self) -> (usize, usize) { + self.span + } + fn build_ast(pipeline: pest::iterators::Pair) -> Self { assert!(matches!(pipeline.as_rule(), Rule::pipeline)); + let span = (pipeline.as_span().start(), pipeline.as_span().end()); Self { exes: pipeline.into_inner().map(Exe::build_ast).collect(), + span, } } } diff --git a/src/runner/mod.rs b/src/runner/mod.rs index 86fb1b4..3e32498 100644 --- a/src/runner/mod.rs +++ b/src/runner/mod.rs @@ -9,6 +9,7 @@ const PID0: nix::unistd::Pid = nix::unistd::Pid::from_raw(0); #[derive(Debug, serde::Serialize, serde::Deserialize)] pub enum Event { + RunPipeline(usize, (usize, usize)), Suspend(usize), Exit(Env), } @@ -195,6 +196,8 @@ async fn run_pipeline( env: &mut Env, shell_write: &async_std::fs::File, ) -> anyhow::Result<()> { + write_event(&shell_write, Event::RunPipeline(env.idx(), pipeline.span())) + .await?; // Safety: pipelines are run serially, so only one copy of these will ever // exist at once. note that reusing a single copy of these at the top // level would not be safe, because in the case of a command line like diff --git a/src/shell/event.rs b/src/shell/event.rs index 52b7acf..9885821 100644 --- a/src/shell/event.rs +++ b/src/shell/event.rs @@ -4,6 +4,7 @@ pub enum Event { Resize((u16, u16)), PtyOutput, PtyClose, + ChildRunPipeline(usize, (usize, usize)), ChildSuspend(usize), ClockTimer, } @@ -56,6 +57,7 @@ struct Pending { size: Option<(u16, u16)>, pty_output: bool, pty_close: bool, + child_run_pipeline: std::collections::VecDeque<(usize, (usize, usize))>, child_suspend: std::collections::VecDeque, clock_timer: bool, done: bool, @@ -72,6 +74,7 @@ impl Pending { || self.size.is_some() || self.pty_output || self.pty_close + || !self.child_run_pipeline.is_empty() || !self.child_suspend.is_empty() || self.clock_timer } @@ -90,6 +93,9 @@ impl Pending { self.pty_close = false; return Some(Event::PtyClose); } + if let Some((idx, span)) = self.child_run_pipeline.pop_front() { + return Some(Event::ChildRunPipeline(idx, span)); + } if let Some(idx) = self.child_suspend.pop_front() { return Some(Event::ChildSuspend(idx)); } @@ -113,6 +119,9 @@ impl Pending { Some(Event::Resize(size)) => self.size = Some(size), Some(Event::PtyOutput) => self.pty_output = true, Some(Event::PtyClose) => self.pty_close = true, + Some(Event::ChildRunPipeline(idx, span)) => { + self.child_run_pipeline.push_back((idx, span)); + } Some(Event::ChildSuspend(idx)) => { self.child_suspend.push_back(idx); } diff --git a/src/shell/history/entry.rs b/src/shell/history/entry.rs index c9112e8..f2a7d08 100644 --- a/src/shell/history/entry.rs +++ b/src/shell/history/entry.rs @@ -1,8 +1,14 @@ use crate::shell::prelude::*; +enum State { + Running((usize, usize)), + Exited(ExitInfo), +} + pub struct Entry { cmdline: String, env: Env, + state: State, vt: vt100::Parser, audible_bell_state: usize, visual_bell_state: usize, @@ -11,7 +17,6 @@ pub struct Entry { resize: async_std::channel::Sender<(u16, u16)>, start_time: time::OffsetDateTime, start_instant: std::time::Instant, - exit_info: Option, } impl Entry { @@ -22,9 +27,11 @@ impl Entry { input: async_std::channel::Sender>, resize: async_std::channel::Sender<(u16, u16)>, ) -> Self { + let span = (0, cmdline.len()); Self { cmdline, env, + state: State::Running(span), vt: vt100::Parser::new(size.0, size.1, 0), audible_bell_state: 0, visual_bell_state: 0, @@ -33,7 +40,6 @@ impl Entry { fullscreen: None, start_time: time::OffsetDateTime::now_utc(), start_instant: std::time::Instant::now(), - exit_info: None, } } @@ -47,7 +53,7 @@ impl Entry { scrolling: bool, offset: time::UtcOffset, ) { - let time = self.exit_info.as_ref().map_or_else( + let time = self.exit_info().map_or_else( || { format!( "[{}]", @@ -75,7 +81,7 @@ impl Entry { out.reset_attributes(); set_bgcolor(out, idx, focused); - if let Some(info) = &self.exit_info { + if let Some(info) = self.exit_info() { if info.status.signal().is_some() { out.set_fgcolor(textmode::color::MAGENTA); } else if info.status.success() { @@ -91,20 +97,45 @@ impl Entry { set_bgcolor(out, idx, focused); out.write_str("$ "); - if self.running() { - out.set_bgcolor(textmode::Color::Rgb(16, 64, 16)); - } - let cmd = self.cmd(); let start = usize::from(out.screen().cursor_position().1); let end = usize::from(size.1) - time.len() - 2; let max_len = end - start; - if cmd.len() > max_len { - out.write_str(&cmd[..(max_len - 4)]); - out.set_fgcolor(textmode::color::BLUE); - out.write_str(" ..."); + let cmd = if self.cmd().len() > max_len { + &self.cmd()[..(max_len - 4)] + } else { + self.cmd() + }; + if let State::Running(span) = self.state { + let span = (span.0.min(cmd.len()), span.1.min(cmd.len())); + if !cmd[..span.0].is_empty() { + out.write_str(&cmd[..span.0]); + } + if !cmd[span.0..span.1].is_empty() { + out.set_bgcolor(textmode::Color::Rgb(16, 64, 16)); + out.write_str(&cmd[span.0..span.1]); + set_bgcolor(out, idx, focused); + } + if !cmd[span.1..].is_empty() { + out.write_str(&cmd[span.1..]); + } } else { out.write_str(cmd); } + if self.cmd().len() > max_len { + if let State::Running(span) = self.state { + if span.0 < cmd.len() && span.1 > cmd.len() { + out.set_bgcolor(textmode::Color::Rgb(16, 64, 16)); + } + } + out.write_str(" "); + if let State::Running(span) = self.state { + if span.1 > cmd.len() { + out.set_bgcolor(textmode::Color::Rgb(16, 64, 16)); + } + } + out.set_fgcolor(textmode::color::BLUE); + out.write_str("..."); + } out.reset_attributes(); set_bgcolor(out, idx, focused); @@ -233,7 +264,7 @@ impl Entry { } pub fn running(&self) -> bool { - self.exit_info.is_none() + matches!(self.state, State::Running(_)) } pub fn binary(&self) -> bool { @@ -281,15 +312,26 @@ impl Entry { .unwrap_or_else(|| self.vt.screen().alternate_screen()) } + pub fn set_span(&mut self, span: (usize, usize)) { + self.state = State::Running(span); + } + pub async fn finish( &mut self, env: Env, event_w: async_std::channel::Sender, ) { - self.exit_info = Some(ExitInfo::new(*env.latest_status())); + self.state = State::Exited(ExitInfo::new(*env.latest_status())); self.env = env; event_w.send(Event::PtyClose).await.unwrap(); } + + fn exit_info(&self) -> Option<&ExitInfo> { + match &self.state { + State::Running(..) => None, + State::Exited(exit_info) => Some(exit_info), + } + } } struct ExitInfo { diff --git a/src/shell/history/mod.rs b/src/shell/history/mod.rs index e206a9b..8f4f56a 100644 --- a/src/shell/history/mod.rs +++ b/src/shell/history/mod.rs @@ -356,6 +356,13 @@ async fn spawn_commands( }; match read.or(exit).await { Res::Read(Ok(event)) => match event { + crate::runner::Event::RunPipeline(idx, span) => { + event_w + .send(Event::ChildRunPipeline(idx, span)) + .await + .unwrap(); + new_read(); + } crate::runner::Event::Suspend(idx) => { event_w.send(Event::ChildSuspend(idx)).await.unwrap(); new_read(); diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 629f482..7371933 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -289,6 +289,9 @@ impl Shell { } } } + Event::ChildRunPipeline(idx, span) => { + self.history.entry(idx).await.set_span(span); + } Event::ChildSuspend(idx) => { if self.focus_idx() == Some(idx) { self.set_focus(Focus::Readline, None).await; -- cgit v1.2.3-54-g00ecf