summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2022-01-11 00:10:56 -0500
committerJesse Luehrs <doy@tozt.net>2022-01-11 00:37:09 -0500
commit4086fc1f83673554f9b9695e853c44dd895add23 (patch)
tree0ce4aa198f1cce027c726dc90e11c923cd0e4447
parent5bce22093b4b0778f729d8d92e37f42214f78fc5 (diff)
downloadnbsh-4086fc1f83673554f9b9695e853c44dd895add23.tar.gz
nbsh-4086fc1f83673554f9b9695e853c44dd895add23.zip
highlight the specific pipeline that is currently running
-rw-r--r--src/parse/ast.rs7
-rw-r--r--src/runner/mod.rs3
-rw-r--r--src/shell/event.rs9
-rw-r--r--src/shell/history/entry.rs70
-rw-r--r--src/shell/history/mod.rs7
-rw-r--r--src/shell/mod.rs3
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<Exe>,
+ 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<Rule>) -> 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<usize>,
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<ExitInfo>,
}
impl Entry {
@@ -22,9 +27,11 @@ impl Entry {
input: async_std::channel::Sender<Vec<u8>>,
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<Event>,
) {
- 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;