From 9b6df1fb6139bfc570926d458ab89ae94aad506a Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 20 Jul 2022 12:41:04 -0700 Subject: [PATCH] Checkpoint, this commit does not compile --- Cargo.lock | 1 + crates/terminal/Cargo.toml | 1 + crates/terminal/src/connection.rs | 140 +++++++++++++----- crates/terminal/src/modal.rs | 6 +- crates/terminal/src/terminal.rs | 16 +- .../src/tests/terminal_test_context.rs | 2 +- 6 files changed, 116 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 844ad59f7a756374bbb84a74b49c3ed60f022b8f..8e9e976efd7edc431ea897a574c79885f2bf5f92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5368,6 +5368,7 @@ dependencies = [ "shellexpand", "smallvec", "theme", + "thiserror", "util", "workspace", ] diff --git a/crates/terminal/Cargo.toml b/crates/terminal/Cargo.toml index 4ddf1a2fd3263f581ac06bf85626759cb12b886a..03c6a26b7db643322c3a3cd9cc98bcce7cba7310 100644 --- a/crates/terminal/Cargo.toml +++ b/crates/terminal/Cargo.toml @@ -25,6 +25,7 @@ dirs = "4.0.0" shellexpand = "2.1.0" libc = "0.2" anyhow = "1" +thiserror = "1.0" [dev-dependencies] diff --git a/crates/terminal/src/connection.rs b/crates/terminal/src/connection.rs index df09496f5cb776b436088597405ca30e306c2480..2672a68457270afe96361843f0c64d456c53bd2a 100644 --- a/crates/terminal/src/connection.rs +++ b/crates/terminal/src/connection.rs @@ -13,12 +13,14 @@ use alacritty_terminal::{ tty::{self, setup_env}, Term, }; +use anyhow::{bail, Result}; use futures::{ - channel::mpsc::{unbounded, UnboundedSender}, + channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}, StreamExt, }; use settings::{Settings, Shell}; -use std::{collections::HashMap, path::PathBuf, sync::Arc}; +use std::{collections::HashMap, fmt::Display, path::PathBuf, sync::Arc}; +use thiserror::Error; use gpui::{keymap::Keystroke, ClipboardItem, CursorStyle, Entity, ModelContext}; @@ -52,23 +54,84 @@ impl EventListener for ZedListener { } } -pub enum TerminalConnection { - Connected(Terminal), - Disconnected { - directory: Option, - shell: Option, - error: Option, - }, +#[derive(Error, Debug)] +pub struct TerminalError { + directory: Option, + shell: Option, + source: std::io::Error, +} + +impl Display for TerminalError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let dir_string: String = self + .directory + .map(|path| { + match path + .into_os_string() + .into_string() + .map_err(|os_str| format!(" {}", os_str.to_string_lossy())) + { + Ok(s) => s, + Err(s) => s, + } + }) + .unwrap_or_else(|| { + let default_dir = + dirs::home_dir().map(|buf| buf.into_os_string().to_string_lossy()); + match default_dir { + Some(dir) => format!(" {}", dir), + None => "".to_string(), + } + }); + + let shell = self + .shell + .map(|shell| match shell { + Shell::System => { + let mut buf = [0; 1024]; + let pw = alacritty_unix::get_pw_entry(&mut buf).ok(); + + match pw { + Some(pw) => format!(" {}", pw.shell), + None => "".to_string(), + } + } + Shell::Program(s) => s, + Shell::WithArguments { program, args } => format!("{} {}", program, args.join(" ")), + }) + .unwrap_or_else(|| { + let mut buf = [0; 1024]; + let pw = alacritty_unix::get_pw_entry(&mut buf).ok(); + match pw { + Some(pw) => { + format!(" {}", pw.shell) + } + None => { + " {}".to_string() + } + } + }); + + write!( + f, + "Working directory: {} Shell command: `{}`, IOError: {}", + dir_string, shell, self.source + ) + } +} + +pub struct DisconnectedPTY { + terminal: Terminal, + events_rx: UnboundedReceiver, } -impl TerminalConnection { +impl DisconnectedPTY { pub fn new( working_directory: Option, shell: Option, env: Option>, initial_size: TerminalDimensions, - cx: &mut ModelContext, - ) -> TerminalConnection { + ) -> Result { let pty_config = { let alac_shell = shell.clone().and_then(|shell| match shell { Shell::System => None, @@ -107,11 +170,11 @@ impl TerminalConnection { let pty = match tty::new(&pty_config, initial_size.into(), None) { Ok(pty) => pty, Err(error) => { - return TerminalConnection::Disconnected { + bail!(TerminalError { directory: working_directory, shell, - error: Some(error), - }; + source: error, + }); } }; @@ -149,20 +212,20 @@ impl TerminalConnection { associated_directory: working_directory, }; + Ok(DisconnectedPTY { + terminal, + events_rx, + }) + } + + pub fn connect(self, cx: &mut ModelContext) -> Terminal { cx.spawn_weak(|this, mut cx| async move { //Listen for terminal events - while let Some(event) = events_rx.next().await { + while let Some(event) = self.events_rx.next().await { match this.upgrade(&cx) { Some(this) => { this.update(&mut cx, |this, cx| { - match this { - TerminalConnection::Connected(conn) => { - conn.process_terminal_event(event, cx) - } - //There should never be a state where the terminal is disconnected - //And receiving events from the pty - TerminalConnection::Disconnected { .. } => unreachable!(), - } + this.process_terminal_event(event, cx); cx.notify(); }); @@ -173,14 +236,7 @@ impl TerminalConnection { }) .detach(); - TerminalConnection::Connected(terminal) - } - - pub fn get_terminal(&self) -> Option<&Terminal> { - match self { - TerminalConnection::Connected(conn) => Some(&conn), - TerminalConnection::Disconnected { .. } => None, - } + self.terminal } } @@ -196,7 +252,7 @@ impl Terminal { fn process_terminal_event( &mut self, event: alacritty_terminal::event::Event, - cx: &mut ModelContext, + cx: &mut ModelContext, ) { match event { // TODO: Handle is_self_focused in subscription on terminal view @@ -361,21 +417,23 @@ impl Terminal { } } -impl Drop for TerminalConnection { +impl Drop for DisconnectedPTY { fn drop(&mut self) { - match self { - TerminalConnection::Connected(conn) => { - conn.pty_tx.0.send(Msg::Shutdown).ok(); - } - _ => {} - }; + self.terminal.pty_tx.0.send(Msg::Shutdown).ok(); + } +} + +impl Drop for Terminal { + fn drop(&mut self) { + self.pty_tx.0.send(Msg::Shutdown).ok(); } } -impl Entity for TerminalConnection { +impl Entity for Terminal { type Event = Event; } +//TODO Move this around mod alacritty_unix { use alacritty_terminal::config::Program; use gpui::anyhow::{bail, Result}; diff --git a/crates/terminal/src/modal.rs b/crates/terminal/src/modal.rs index 91d2d9ff738b634513e862cd5d7ac9ad7d706f59..cabfc5b44ca2539a228f43c0647f905d8bc1f778 100644 --- a/crates/terminal/src/modal.rs +++ b/crates/terminal/src/modal.rs @@ -1,10 +1,10 @@ use gpui::{ModelHandle, ViewContext}; use workspace::Workspace; -use crate::{get_wd_for_workspace, DeployModal, Event, TerminalConnection, TerminalView}; +use crate::{connection::Terminal, get_wd_for_workspace, DeployModal, Event, TerminalView}; #[derive(Debug)] -struct StoredConnection(ModelHandle); +struct StoredConnection(ModelHandle); pub fn deploy_modal(workspace: &mut Workspace, _: &DeployModal, cx: &mut ViewContext) { // Pull the terminal connection out of the global if it has been stored @@ -46,7 +46,7 @@ pub fn deploy_modal(workspace: &mut Workspace, _: &DeployModal, cx: &mut ViewCon pub fn on_event( workspace: &mut Workspace, - _: ModelHandle, + _: ModelHandle, event: &Event, cx: &mut ViewContext, ) { diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index fc35d0eeea0f5472adceb174a5b2aa829f8dc4c7..67e18409c7fffaa58a2fc252f07985895802f7d4 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -3,7 +3,7 @@ pub mod connection; mod modal; pub mod terminal_element; -use connection::{Event, TerminalConnection}; +use connection::{DisconnectedPTY, Event, Terminal, TerminalError}; use dirs::home_dir; use gpui::{ actions, elements::*, geometry::vector::vec2f, keymap::Keystroke, AppContext, ClipboardItem, @@ -64,7 +64,7 @@ pub fn init(cx: &mut MutableAppContext) { ///A terminal view, maintains the PTY's file handles and communicates with the terminal pub struct TerminalView { - connection: ModelHandle, + connection: Result, TerminalError>, has_new_content: bool, //Currently using iTerm bell, show bell emoji in tab until input is received has_bell: bool, @@ -94,14 +94,20 @@ impl TerminalView { (shell, envs) }; - let connection = cx - .add_model(|cx| TerminalConnection::new(working_directory, shell, envs, size_info, cx)); + let connection = DisconnectedPTY::new(working_directory, shell, envs, size_info) + .map(|pty| cx.add_model(|cx| pty.connect(cx))) + .map_err(|err| { + match err.downcast::() { + Ok(err) => err, + Err(_) => unreachable!(), //This should never happen + } + }); TerminalView::from_connection(connection, modal, cx) } fn from_connection( - connection: ModelHandle, + connection: Result, TerminalError>, modal: bool, cx: &mut ViewContext, ) -> TerminalView { diff --git a/crates/terminal/src/tests/terminal_test_context.rs b/crates/terminal/src/tests/terminal_test_context.rs index f6d95106e337e39c5c7317213ce728bceb38f7bf..ec967fb183cb54237d41cbfcc00b022fd15a2333 100644 --- a/crates/terminal/src/tests/terminal_test_context.rs +++ b/crates/terminal/src/tests/terminal_test_context.rs @@ -24,7 +24,7 @@ impl<'a> TerminalTestContext<'a> { ); let connection = - cx.add_model(|cx| TerminalConnection::new(None, None, None, size_info, cx)); + cx.add_model(|cx| TerminalConnection::new_tty(None, None, None, size_info, cx)); TerminalTestContext { cx, connection } }