From b1c5f2f31874fc019b67ae981f66e0492b22c867 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Fri, 4 Mar 2022 16:31:38 -0500 Subject: another large refactor --- src/shell/history/mod.rs | 257 +++++++---------------------------------------- 1 file changed, 37 insertions(+), 220 deletions(-) (limited to 'src/shell/history/mod.rs') diff --git a/src/shell/history/mod.rs b/src/shell/history/mod.rs index 2e8b817..df995e6 100644 --- a/src/shell/history/mod.rs +++ b/src/shell/history/mod.rs @@ -2,12 +2,12 @@ use crate::shell::prelude::*; mod entry; pub use entry::Entry; +mod job; mod pty; -mod vt; pub struct History { size: (u16, u16), - entries: Vec>>, + entries: Vec, scroll_pos: usize, } @@ -28,22 +28,21 @@ impl History { scrolling: bool, offset: time::UtcOffset, ) { - let mut used_lines = repl_lines; let mut cursor = None; - for (idx, mut entry) in + for (idx, used_lines, mut vt, state) in self.visible(repl_lines, focus, scrolling).rev() { let focused = focus.map_or(false, |focus| idx == focus); - used_lines += - entry.lines(self.entry_count(), focused && !scrolling); out.move_to( (usize::from(self.size.0) - used_lines).try_into().unwrap(), 0, ); - entry.render( + self.entries[idx].render( out, idx, self.entry_count(), + &*state, + &mut *vt, self.size, focused, scrolling, @@ -62,30 +61,18 @@ impl History { } } - pub fn render_fullscreen( - &self, - out: &mut impl textmode::Textmode, - idx: usize, - ) { - self.with_entry_mut(idx, |entry| entry.render_fullscreen(out)); - } - - pub fn send_input(&mut self, idx: usize, input: Vec) { - self.with_entry(idx, |entry| entry.send_input(input)); + pub fn entry(&self, idx: usize) -> &Entry { + &self.entries[idx] } - pub fn should_fullscreen(&self, idx: usize) -> bool { - self.with_entry(idx, Entry::should_fullscreen) - } - - pub fn running(&self, idx: usize) -> bool { - self.with_entry(idx, Entry::running) + pub fn entry_mut(&mut self, idx: usize) -> &mut Entry { + &mut self.entries[idx] } pub fn resize(&mut self, size: (u16, u16)) { self.size = size; for entry in &self.entries { - entry.lock().unwrap().resize(size); + entry.resize(size); } } @@ -94,46 +81,9 @@ impl History { cmdline: String, env: Env, event_w: crate::shell::event::Writer, - ) -> usize { - let (input_w, input_r) = tokio::sync::mpsc::unbounded_channel(); - let (resize_w, resize_r) = tokio::sync::mpsc::unbounded_channel(); - - let entry = std::sync::Arc::new(std::sync::Mutex::new(Entry::new( - cmdline.clone(), - env.clone(), - self.size, - input_w, - resize_w, - ))); - run_commands( - cmdline, - std::sync::Arc::clone(&entry), - env, - input_r, - resize_r, - event_w, - ); - - self.entries.push(entry); - self.entries.len() - 1 - } - - pub fn with_entry( - &self, - idx: usize, - f: impl FnOnce(&Entry) -> T, - ) -> T { - let entry = self.entries[idx].lock().unwrap(); - f(&*entry) - } - - pub fn with_entry_mut( - &self, - idx: usize, - f: impl FnOnce(&mut Entry) -> T, - ) -> T { - let mut entry = self.entries[idx].lock().unwrap(); - f(&mut *entry) + ) { + self.entries + .push(Entry::new(cmdline, env, self.size, event_w)); } pub fn entry_count(&self) -> usize { @@ -155,7 +105,7 @@ impl History { while focus < self .visible(repl_lines, Some(focus), scrolling) - .map(|(idx, _)| idx) + .map(|(idx, ..)| idx) .next() .unwrap() { @@ -169,7 +119,7 @@ impl History { while focus > self .visible(repl_lines, Some(focus), scrolling) - .map(|(idx, _)| idx) + .map(|(idx, ..)| idx) .last() .unwrap() { @@ -184,30 +134,29 @@ impl History { scrolling: bool, ) -> VisibleEntries { let mut iter = VisibleEntries::new(); - if self.entries.is_empty() { - return iter; - } - let mut used_lines = repl_lines; for (idx, entry) in self.entries.iter().enumerate().rev().skip(self.scroll_pos) { - let entry = entry.lock().unwrap(); let focused = focus.map_or(false, |focus| idx == focus); used_lines += entry.lines(self.entry_count(), focused && !scrolling); if used_lines > usize::from(self.size.0) { break; } - iter.add(idx, entry); + iter.add(idx, used_lines, entry.lock_vt(), entry.lock_state()); } iter } } struct VisibleEntries<'a> { - entries: - std::collections::VecDeque<(usize, std::sync::MutexGuard<'a, Entry>)>, + entries: std::collections::VecDeque<( + usize, + usize, + std::sync::MutexGuard<'a, pty::Vt>, + std::sync::MutexGuard<'a, job::State>, + )>, } impl<'a> VisibleEntries<'a> { @@ -217,14 +166,25 @@ impl<'a> VisibleEntries<'a> { } } - fn add(&mut self, idx: usize, entry: std::sync::MutexGuard<'a, Entry>) { + fn add( + &mut self, + idx: usize, + offset: usize, + vt: std::sync::MutexGuard<'a, pty::Vt>, + state: std::sync::MutexGuard<'a, job::State>, + ) { // push_front because we are adding them in reverse order - self.entries.push_front((idx, entry)); + self.entries.push_front((idx, offset, vt, state)); } } impl<'a> std::iter::Iterator for VisibleEntries<'a> { - type Item = (usize, std::sync::MutexGuard<'a, Entry>); + type Item = ( + usize, + usize, + std::sync::MutexGuard<'a, pty::Vt>, + std::sync::MutexGuard<'a, job::State>, + ); fn next(&mut self) -> Option { self.entries.pop_front() @@ -236,146 +196,3 @@ impl<'a> std::iter::DoubleEndedIterator for VisibleEntries<'a> { self.entries.pop_back() } } - -fn run_commands( - cmdline: String, - entry: std::sync::Arc>, - mut env: Env, - input_r: tokio::sync::mpsc::UnboundedReceiver>, - resize_r: tokio::sync::mpsc::UnboundedReceiver<(u16, u16)>, - event_w: crate::shell::event::Writer, -) { - tokio::task::spawn(async move { - let size = entry.lock().unwrap().size(); - let pty = match pty::Pty::new( - size, - &entry, - input_r, - resize_r, - event_w.clone(), - ) { - Ok(pty) => pty, - Err(e) => { - let mut entry = entry.lock().unwrap(); - entry.process( - format!("nbsh: failed to allocate pty: {}\r\n", e) - .as_bytes(), - ); - env.set_status(std::process::ExitStatus::from_raw(1 << 8)); - entry.finish(env, &event_w); - return; - } - }; - - let status = - match spawn_commands(&cmdline, &pty, &mut env, event_w.clone()) - .await - { - Ok(status) => status, - Err(e) => { - let mut entry = entry.lock().unwrap(); - entry.process( - format!( - "nbsh: failed to spawn {}: {}\r\n", - cmdline, e - ) - .as_bytes(), - ); - env.set_status(std::process::ExitStatus::from_raw( - 1 << 8, - )); - entry.finish(env, &event_w); - return; - } - }; - env.set_status(status); - - entry.lock().unwrap().finish(env, &event_w); - pty.close(); - }); -} - -async fn spawn_commands( - cmdline: &str, - pty: &pty::Pty, - env: &mut Env, - event_w: crate::shell::event::Writer, -) -> Result { - enum Res { - Read(crate::runner::Event), - Exit(std::io::Result), - } - - let mut cmd = pty_process::Command::new(std::env::current_exe()?); - cmd.args(&["-c", cmdline, "--status-fd", "3"]); - env.apply(&mut cmd); - let (from_r, from_w) = nix::unistd::pipe2(nix::fcntl::OFlag::O_CLOEXEC)?; - // Safety: from_r was just opened above and is not used anywhere else - let fh = unsafe { std::fs::File::from_raw_fd(from_r) }; - // Safety: dup2 is an async-signal-safe function - unsafe { - cmd.pre_exec(move || { - nix::unistd::dup2(from_w, 3)?; - Ok(()) - }); - } - let mut child = pty.spawn(cmd)?; - nix::unistd::close(from_w)?; - - let (read_w, read_r) = tokio::sync::mpsc::unbounded_channel(); - tokio::task::spawn_blocking(move || loop { - let event = bincode::deserialize_from(&fh); - match event { - Ok(event) => { - read_w.send(event).unwrap(); - } - Err(e) => { - match &*e { - bincode::ErrorKind::Io(io_e) => { - assert!( - io_e.kind() == std::io::ErrorKind::UnexpectedEof - ); - } - e => { - panic!("{}", e); - } - } - break; - } - } - }); - - let mut stream: futures_util::stream::SelectAll<_> = [ - tokio_stream::wrappers::UnboundedReceiverStream::new(read_r) - .map(Res::Read) - .boxed(), - futures_util::stream::once(child.wait()) - .map(Res::Exit) - .boxed(), - ] - .into_iter() - .collect(); - let mut exit_status = None; - while let Some(res) = stream.next().await { - match res { - Res::Read(event) => match event { - crate::runner::Event::RunPipeline(idx, span) => { - event_w.send(Event::ChildRunPipeline(idx, span)); - } - crate::runner::Event::Suspend(idx) => { - event_w.send(Event::ChildSuspend(idx)); - } - crate::runner::Event::Exit(new_env) => { - *env = new_env; - } - }, - Res::Exit(Ok(status)) => { - exit_status = Some(status); - } - Res::Exit(Err(e)) => { - anyhow::bail!(e); - } - } - } - Ok(exit_status.unwrap()) -} -- cgit v1.2.3-54-g00ecf