summaryrefslogtreecommitdiffstats
path: root/src/state/history/mod.rs
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2022-01-05 06:22:35 -0500
committerJesse Luehrs <doy@tozt.net>2022-01-05 06:22:35 -0500
commit53193c426d19d529382e75e8af4ab9266b992557 (patch)
tree225eb74a24f1a46a5dbf1ac445a49d717fe930ba /src/state/history/mod.rs
parentf97c276722fda4fe12d81041706ea5c7753eb0e2 (diff)
downloadnbsh-53193c426d19d529382e75e8af4ab9266b992557.tar.gz
nbsh-53193c426d19d529382e75e8af4ab9266b992557.zip
split out more
Diffstat (limited to 'src/state/history/mod.rs')
-rw-r--r--src/state/history/mod.rs304
1 files changed, 5 insertions, 299 deletions
diff --git a/src/state/history/mod.rs b/src/state/history/mod.rs
index 8b15916..9df3103 100644
--- a/src/state/history/mod.rs
+++ b/src/state/history/mod.rs
@@ -3,6 +3,8 @@ use futures_lite::future::FutureExt as _;
use std::os::unix::io::{FromRawFd as _, IntoRawFd as _};
use std::os::unix::process::ExitStatusExt as _;
+mod entry;
+pub use entry::Entry;
mod pty;
pub struct History {
@@ -144,7 +146,7 @@ impl History {
self.entries.push(async_std::sync::Arc::clone(&entry));
let mut entry = entry.lock_arc().await;
- entry.vt.process(err_str.replace('\n', "\r\n").as_bytes());
+ entry.process(err_str.replace('\n', "\r\n").as_bytes());
let mut env = env.clone();
env.set_status(async_std::process::ExitStatus::from_raw(1 << 8));
entry.finish(env, event_w).await;
@@ -267,291 +269,6 @@ impl std::iter::DoubleEndedIterator for VisibleEntries {
}
}
-pub struct Entry {
- cmdline: String,
- env: crate::env::Env,
- vt: vt100::Parser,
- audible_bell_state: usize,
- visual_bell_state: usize,
- fullscreen: Option<bool>,
- input: async_std::channel::Sender<Vec<u8>>,
- resize: async_std::channel::Sender<(u16, u16)>,
- start_time: time::OffsetDateTime,
- start_instant: std::time::Instant,
- exit_info: Option<ExitInfo>,
-}
-
-impl Entry {
- fn new(
- cmdline: String,
- env: crate::env::Env,
- size: (u16, u16),
- input: async_std::channel::Sender<Vec<u8>>,
- resize: async_std::channel::Sender<(u16, u16)>,
- ) -> Self {
- Self {
- cmdline,
- env,
- vt: vt100::Parser::new(size.0, size.1, 0),
- audible_bell_state: 0,
- visual_bell_state: 0,
- input,
- resize,
- fullscreen: None,
- start_time: time::OffsetDateTime::now_utc(),
- start_instant: std::time::Instant::now(),
- exit_info: None,
- }
- }
-
- fn render(
- &self,
- out: &mut impl textmode::Textmode,
- idx: usize,
- entry_count: usize,
- width: u16,
- focused: bool,
- scrolling: bool,
- offset: time::UtcOffset,
- ) {
- let time = self.exit_info.as_ref().map_or_else(
- || {
- format!(
- "[{}]",
- crate::format::time(self.start_time.to_offset(offset))
- )
- },
- |info| {
- format!(
- "({}) [{}]",
- crate::format::duration(
- info.instant - self.start_instant
- ),
- crate::format::time(self.start_time.to_offset(offset)),
- )
- },
- );
-
- set_bgcolor(out, idx, focused);
- out.set_fgcolor(textmode::color::YELLOW);
- let entry_count_width = format!("{}", entry_count + 1).len();
- let idx_str = format!("{}", idx + 1);
- out.write_str(&" ".repeat(entry_count_width - idx_str.len()));
- out.write_str(&idx_str);
- out.write_str(" ");
- out.reset_attributes();
-
- set_bgcolor(out, idx, focused);
- if let Some(info) = &self.exit_info {
- if info.status.signal().is_some() {
- out.set_fgcolor(textmode::color::MAGENTA);
- } else if info.status.success() {
- out.set_fgcolor(textmode::color::DARKGREY);
- } else {
- out.set_fgcolor(textmode::color::RED);
- }
- out.write_str(&crate::format::exit_status(info.status));
- } else {
- out.write_str(" ");
- }
- out.reset_attributes();
-
- 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(width) - 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(" ...");
- } else {
- out.write_str(cmd);
- }
- out.reset_attributes();
-
- set_bgcolor(out, idx, focused);
- let cur_pos = out.screen().cursor_position();
- out.write_str(&" ".repeat(
- usize::from(width) - time.len() - 1 - usize::from(cur_pos.1),
- ));
- out.write_str(&time);
- out.write_str(" ");
- out.reset_attributes();
-
- if self.binary() {
- let msg = "This appears to be binary data. Fullscreen this entry to view anyway.";
- let len: u16 = msg.len().try_into().unwrap();
- out.move_to(
- out.screen().cursor_position().0 + 1,
- (width - len) / 2,
- );
- out.set_fgcolor(textmode::color::RED);
- out.write_str(msg);
- out.hide_cursor(true);
- } else {
- let last_row = self.output_lines(width, focused && !scrolling);
- if last_row > 5 {
- out.write(b"\r\n");
- out.set_fgcolor(textmode::color::BLUE);
- out.write_str("...");
- out.reset_attributes();
- }
- let mut out_row = out.screen().cursor_position().0 + 1;
- let screen = self.vt.screen();
- let pos = screen.cursor_position();
- let mut wrapped = false;
- let mut cursor_found = None;
- for (idx, row) in screen
- .rows_formatted(0, width)
- .enumerate()
- .take(last_row)
- .skip(last_row.saturating_sub(5))
- {
- let idx: u16 = idx.try_into().unwrap();
- out.reset_attributes();
- if !wrapped {
- out.move_to(out_row, 0);
- }
- out.write(&row);
- wrapped = screen.row_wrapped(idx);
- if pos.0 == idx {
- cursor_found = Some(out_row);
- }
- out_row += 1;
- }
- if focused && !scrolling {
- if let Some(row) = cursor_found {
- out.hide_cursor(screen.hide_cursor());
- out.move_to(row, pos.1);
- } else {
- out.hide_cursor(true);
- }
- }
- }
- out.reset_attributes();
- }
-
- fn render_fullscreen(&mut self, out: &mut impl textmode::Textmode) {
- let screen = self.vt.screen();
- let new_audible_bell_state = screen.audible_bell_count();
- let new_visual_bell_state = screen.visual_bell_count();
-
- out.write(&screen.state_formatted());
-
- if self.audible_bell_state != new_audible_bell_state {
- out.write(b"\x07");
- self.audible_bell_state = new_audible_bell_state;
- }
-
- if self.visual_bell_state != new_visual_bell_state {
- out.write(b"\x1bg");
- self.visual_bell_state = new_visual_bell_state;
- }
-
- out.reset_attributes();
- }
-
- pub async fn send_input(&self, bytes: Vec<u8>) {
- if self.running() {
- self.input.send(bytes).await.unwrap();
- }
- }
-
- pub async fn resize(&self, size: (u16, u16)) {
- if self.running() {
- self.resize.send(size).await.unwrap();
- }
- }
-
- pub fn cmd(&self) -> &str {
- &self.cmdline
- }
-
- pub fn env(&self) -> &crate::env::Env {
- &self.env
- }
-
- pub fn toggle_fullscreen(&mut self) {
- if let Some(fullscreen) = self.fullscreen {
- self.fullscreen = Some(!fullscreen);
- } else {
- self.fullscreen = Some(!self.vt.screen().alternate_screen());
- }
- }
-
- pub fn set_fullscreen(&mut self, fullscreen: bool) {
- self.fullscreen = Some(fullscreen);
- }
-
- pub fn running(&self) -> bool {
- self.exit_info.is_none()
- }
-
- pub fn binary(&self) -> bool {
- self.vt.screen().errors() > 5
- }
-
- pub fn lines(&self, width: u16, focused: bool) -> usize {
- let lines = self.output_lines(width, focused);
- 1 + std::cmp::min(6, lines)
- }
-
- pub fn output_lines(&self, width: u16, focused: bool) -> usize {
- if self.binary() {
- return 1;
- }
-
- let screen = self.vt.screen();
- let mut last_row = 0;
- for (idx, row) in screen.rows(0, width).enumerate() {
- if !row.is_empty() {
- last_row = idx + 1;
- }
- }
- if focused && self.running() {
- last_row = std::cmp::max(
- last_row,
- usize::from(screen.cursor_position().0) + 1,
- );
- }
- last_row
- }
-
- pub fn should_fullscreen(&self) -> bool {
- self.fullscreen
- .unwrap_or_else(|| self.vt.screen().alternate_screen())
- }
-
- async fn finish(
- &mut self,
- env: crate::env::Env,
- event_w: async_std::channel::Sender<crate::event::Event>,
- ) {
- self.exit_info = Some(ExitInfo::new(*env.latest_status()));
- self.env = env;
- event_w.send(crate::event::Event::PtyClose).await.unwrap();
- }
-}
-
-struct ExitInfo {
- status: async_std::process::ExitStatus,
- instant: std::time::Instant,
-}
-
-impl ExitInfo {
- fn new(status: async_std::process::ExitStatus) -> Self {
- Self {
- status,
- instant: std::time::Instant::now(),
- }
- }
-}
-
fn run_commands(
ast: crate::parse::Commands,
entry: async_std::sync::Arc<async_std::sync::Mutex<Entry>>,
@@ -562,7 +279,7 @@ fn run_commands(
) {
async_std::task::spawn(async move {
let pty = match pty::Pty::new(
- entry.lock_arc().await.vt.screen().size(),
+ entry.lock_arc().await.size(),
&entry,
input_r,
resize_r,
@@ -571,7 +288,7 @@ fn run_commands(
Ok(pty) => pty,
Err(e) => {
let mut entry = entry.lock_arc().await;
- entry.vt.process(
+ entry.process(
format!("nbsh: failed to allocate pty: {}\r\n", e)
.as_bytes(),
);
@@ -596,7 +313,6 @@ fn run_commands(
entry
.lock_arc()
.await
- .vt
.process(format!("nbsh: {}\r\n", e).as_bytes());
env.set_status(async_std::process::ExitStatus::from_raw(
1 << 8,
@@ -696,13 +412,3 @@ async fn run_pipeline(
}
}
}
-
-fn set_bgcolor(out: &mut impl textmode::Textmode, idx: usize, focus: bool) {
- if focus {
- out.set_bgcolor(textmode::Color::Rgb(0x56, 0x1b, 0x8b));
- } else if idx % 2 == 0 {
- out.set_bgcolor(textmode::Color::Rgb(0x24, 0x21, 0x00));
- } else {
- out.set_bgcolor(textmode::Color::Rgb(0x20, 0x20, 0x20));
- }
-}