Attempting to continue rebase

Mikayla Maki created

Change summary

crates/gpui/src/app.rs                             |  26 --
crates/terminal/src/connection.rs                  | 109 +++++++++--
crates/terminal/src/modal.rs                       |  12 
crates/terminal/src/terminal.rs                    | 147 ++++++++-------
crates/terminal/src/terminal_element.rs            | 107 +++++-----
crates/terminal/src/tests/terminal_test_context.rs |  14 
6 files changed, 229 insertions(+), 186 deletions(-)

Detailed changes

crates/gpui/src/app.rs 🔗

@@ -1781,21 +1781,6 @@ impl MutableAppContext {
         })
     }
 
-    pub fn try_add_model<T, F>(&mut self, build_model: F) -> Result<ModelHandle<T>>
-    where
-        T: Entity,
-        F: FnOnce(&mut ModelContext<T>) -> Result<T>,
-    {
-        self.update(|this| {
-            let model_id = post_inc(&mut this.next_entity_id);
-            let handle = ModelHandle::new(model_id, &this.cx.ref_counts);
-            let mut cx = ModelContext::new(this, model_id);
-            let model = build_model(&mut cx)?;
-            this.cx.models.insert(model_id, Box::new(model));
-            Ok(handle)
-        })
-    }
-
     pub fn add_window<T, F>(
         &mut self,
         window_options: WindowOptions,
@@ -1970,13 +1955,10 @@ impl MutableAppContext {
             for model_id in dropped_models {
                 self.subscriptions.lock().remove(&model_id);
                 self.observations.lock().remove(&model_id);
-                //Model handles and IDs may have been created to instantiate a model without
-                //finishing successfully (`try_add_model()`)
-                if let Some(mut model) = self.cx.models.remove(&model_id) {
-                    model.release(self);
-                    self.pending_effects
-                        .push_back(Effect::ModelRelease { model_id, model });
-                }
+                let mut model = self.cx.models.remove(&model_id).unwrap();
+                model.release(self);
+                self.pending_effects
+                    .push_back(Effect::ModelRelease { model_id, model });
             }
 
             for (window_id, view_id) in dropped_views {

crates/terminal/src/connection.rs 🔗

@@ -6,10 +6,11 @@ use alacritty_terminal::{
     event::{Event as AlacTermEvent, EventListener, Notify},
     event_loop::{EventLoop, Msg, Notifier},
     grid::Scroll,
+    selection::Selection,
     sync::FairMutex,
-    term::{SizeInfo, TermMode},
+    term::{cell::Cell, RenderableContent, SizeInfo, TermMode},
     tty::{self, setup_env},
-    Term,
+    Grid, Term,
 };
 use anyhow::Result;
 use futures::channel::mpsc::{
@@ -48,12 +49,13 @@ impl EventListener for ZedListener {
     }
 }
 
-pub struct TerminalConnection {
-    pub pty_tx: Notifier,
-    pub term: Arc<FairMutex<Term<ZedListener>>>,
-    pub title: String,
-    pub associated_directory: Option<PathBuf>,
-    pub cur_size: SizeInfo,
+pub enum TerminalConnection {
+    Connected(Terminal),
+    Disconnected {
+        directory: Option<PathBuf>,
+        shell: Option<Shell>,
+        error: std::io::Error,
+    },
 }
 
 impl TerminalConnection {
@@ -63,7 +65,7 @@ impl TerminalConnection {
         env: Option<HashMap<String, String>>,
         initial_size: SizeInfo,
         cx: &mut ModelContext<Self>,
-    ) -> Result<TerminalConnection> {
+    ) -> TerminalConnection {
         let pty_config = {
             let shell = shell.and_then(|shell| match shell {
                 Shell::System => None,
@@ -99,7 +101,16 @@ impl TerminalConnection {
         let term = Arc::new(FairMutex::new(term));
 
         //Setup the pty...
-        let pty = tty::new(&pty_config, &initial_size, None)?;
+        let pty = match tty::new(&pty_config, &initial_size, None) {
+            Ok(pty) => pty,
+            Err(error) => {
+                return TerminalConnection::Disconnected {
+                    directory: working_directory,
+                    shell,
+                    error,
+                };
+            }
+        };
 
         let shell = {
             let mut buf = [0; 1024];
@@ -121,13 +132,20 @@ impl TerminalConnection {
         let pty_tx = event_loop.channel();
         let _io_thread = event_loop.spawn();
 
+        let terminal = Terminal {
+            pty_tx: Notifier(pty_tx),
+            term,
+            title: DEFAULT_TITLE.to_string(),
+            associated_directory: working_directory,
+        };
+
         cx.spawn_weak(|this, mut cx| async move {
             //Listen for terminal events
             while let Some(event) = events_rx.next().await {
                 match this.upgrade(&cx) {
                     Some(this) => {
                         this.update(&mut cx, |this, cx| {
-                            this.process_terminal_event(event, cx);
+                            terminal.process_terminal_event(event, cx);
                             cx.notify();
                         });
                     }
@@ -137,20 +155,30 @@ impl TerminalConnection {
         })
         .detach();
 
-        Ok(TerminalConnection {
-            pty_tx: Notifier(pty_tx),
-            term,
-            title: shell.to_string(),
-            cur_size: initial_size,
-            associated_directory: working_directory,
-        })
+        TerminalConnection::Connected(terminal)
     }
 
+    pub fn get_terminal(&self) -> Option<&Terminal> {
+        match self {
+            TerminalConnection::Connected(conn) => Some(&conn),
+            TerminalConnection::Disconnected { .. } => None,
+        }
+    }
+}
+
+pub struct Terminal {
+    pty_tx: Notifier,
+    term: Arc<FairMutex<Term<ZedListener>>>,
+    pub title: String,
+    pub associated_directory: Option<PathBuf>,
+}
+
+impl Terminal {
     ///Takes events from Alacritty and translates them to behavior on this view
     fn process_terminal_event(
         &mut self,
         event: alacritty_terminal::event::Event,
-        cx: &mut ModelContext<Self>,
+        cx: &mut ModelContext<TerminalConnection>,
     ) {
         match event {
             // TODO: Handle is_self_focused in subscription on terminal view
@@ -198,12 +226,12 @@ impl TerminalConnection {
     }
 
     ///Write the Input payload to the tty. This locks the terminal so we can scroll it.
-    pub fn write_to_pty(&mut self, input: String) {
+    pub fn write_to_pty(&self, input: String) {
         self.write_bytes_to_pty(input.into_bytes());
     }
 
     ///Write the Input payload to the tty. This locks the terminal so we can scroll it.
-    fn write_bytes_to_pty(&mut self, input: Vec<u8>) {
+    fn write_bytes_to_pty(&self, input: Vec<u8>) {
         self.term.lock().scroll_display(Scroll::Bottom);
         self.pty_tx.notify(input);
     }
@@ -214,12 +242,12 @@ impl TerminalConnection {
         self.term.lock().resize(new_size);
     }
 
-    pub fn clear(&mut self) {
+    pub fn clear(&self) {
         self.write_to_pty("\x0c".into());
         self.term.lock().clear_screen(ClearMode::Saved);
     }
 
-    pub fn try_keystroke(&mut self, keystroke: &Keystroke) -> bool {
+    pub fn try_keystroke(&self, keystroke: &Keystroke) -> bool {
         let guard = self.term.lock();
         let mode = guard.mode();
         let esc = to_esc_str(keystroke, mode);
@@ -233,7 +261,7 @@ impl TerminalConnection {
     }
 
     ///Paste text into the terminal
-    pub fn paste(&mut self, text: &str) {
+    pub fn paste(&self, text: &str) {
         if self.term.lock().mode().contains(TermMode::BRACKETED_PASTE) {
             self.write_to_pty("\x1b[200~".to_string());
             self.write_to_pty(text.replace('\x1b', "").to_string());
@@ -243,6 +271,32 @@ impl TerminalConnection {
         }
     }
 
+    pub fn copy(&self) -> Option<String> {
+        let term = self.term.lock();
+        term.selection_to_string()
+    }
+
+    ///Takes the selection out of the terminal
+    pub fn take_selection(&self) -> Option<Selection> {
+        self.term.lock().selection.take()
+    }
+
+    ///Sets the selection object on the terminal
+    pub fn set_selection(&self, sel: Option<Selection>) {
+        self.term.lock().selection = sel;
+    }
+
+    ///Get the relevant rendering values from the terminal
+    pub fn renderable_content(&self) -> (RenderableContent, &Grid<Cell>) {
+        let term = self.term.lock();
+        (term.renderable_content(), term.grid())
+    }
+
+    ///Scroll the terminal
+    pub fn scroll(&self, scroll: Scroll) {
+        self.term.lock().scroll_display(scroll)
+    }
+
     // pub fn click(&mut self, pos: Vector2F, clicks: usize) {}
 
     // pub fn drag(prev_pos: Vector2F, pos: Vector2F) {}
@@ -252,7 +306,12 @@ impl TerminalConnection {
 
 impl Drop for TerminalConnection {
     fn drop(&mut self) {
-        self.pty_tx.0.send(Msg::Shutdown).ok();
+        match self {
+            TerminalConnection::Connected(conn) => {
+                conn.pty_tx.0.send(Msg::Shutdown).ok();
+            }
+            _ => {}
+        };
     }
 }
 

crates/terminal/src/modal.rs 🔗

@@ -1,8 +1,7 @@
 use gpui::{ModelHandle, ViewContext};
-use util::ResultExt;
 use workspace::Workspace;
 
-use crate::{get_wd_for_workspace, DeployModal, Event, Terminal, TerminalConnection};
+use crate::{get_wd_for_workspace, DeployModal, Event, TerminalConnection, TerminalView};
 
 #[derive(Debug)]
 struct StoredConnection(ModelHandle<TerminalConnection>);
@@ -17,7 +16,7 @@ pub fn deploy_modal(workspace: &mut Workspace, _: &DeployModal, cx: &mut ViewCon
     if let Some(StoredConnection(stored_connection)) = possible_connection {
         // Create a view from the stored connection
         workspace.toggle_modal(cx, |_, cx| {
-            cx.add_view(|cx| Terminal::from_connection(stored_connection.clone(), true, cx))
+            cx.add_view(|cx| TerminalView::from_connection(stored_connection.clone(), true, cx))
         });
         cx.set_global::<Option<StoredConnection>>(Some(StoredConnection(
             stored_connection.clone(),
@@ -27,10 +26,7 @@ pub fn deploy_modal(workspace: &mut Workspace, _: &DeployModal, cx: &mut ViewCon
         if let Some(closed_terminal_handle) = workspace.toggle_modal(cx, |workspace, cx| {
             let wd = get_wd_for_workspace(workspace, cx);
 
-            //TODO: Create a 'failed to launch' view which prints the error and config details.
-            let this = cx
-                .add_option_view(|cx| Terminal::new(wd, true, cx).log_err())
-                .unwrap();
+            let this = cx.add_view(|cx| TerminalView::new(wd, true, cx));
 
             let connection_handle = this.read(cx).connection.clone();
             cx.subscribe(&connection_handle, on_event).detach();
@@ -60,7 +56,7 @@ pub fn on_event(
         if workspace
             .modal()
             .cloned()
-            .and_then(|modal| modal.downcast::<Terminal>())
+            .and_then(|modal| modal.downcast::<TerminalView>())
             .is_some()
         {
             workspace.dismiss_modal(cx)

crates/terminal/src/terminal.rs 🔗

@@ -4,7 +4,6 @@ mod modal;
 pub mod terminal_element;
 
 use alacritty_terminal::term::SizeInfo;
-use anyhow::Result;
 use connection::{Event, TerminalConnection};
 use dirs::home_dir;
 use gpui::{
@@ -17,7 +16,6 @@ use project::{LocalWorktree, Project, ProjectPath};
 use settings::{Settings, WorkingDirectory};
 use smallvec::SmallVec;
 use std::path::{Path, PathBuf};
-use util::ResultExt;
 use workspace::{Item, Workspace};
 
 use crate::terminal_element::TerminalEl;
@@ -50,21 +48,21 @@ actions!(
 ///Initialize and register all of our action handlers
 pub fn init(cx: &mut MutableAppContext) {
     //Global binding overrrides
-    cx.add_action(Terminal::ctrl_c);
-    cx.add_action(Terminal::up);
-    cx.add_action(Terminal::down);
-    cx.add_action(Terminal::escape);
-    cx.add_action(Terminal::enter);
+    cx.add_action(TerminalView::ctrl_c);
+    cx.add_action(TerminalView::up);
+    cx.add_action(TerminalView::down);
+    cx.add_action(TerminalView::escape);
+    cx.add_action(TerminalView::enter);
     //Useful terminal actions
-    cx.add_action(Terminal::deploy);
+    cx.add_action(TerminalView::deploy);
     cx.add_action(deploy_modal);
-    cx.add_action(Terminal::copy);
-    cx.add_action(Terminal::paste);
-    cx.add_action(Terminal::clear);
+    cx.add_action(TerminalView::copy);
+    cx.add_action(TerminalView::paste);
+    cx.add_action(TerminalView::clear);
 }
 
 ///A terminal view, maintains the PTY's file handles and communicates with the terminal
-pub struct Terminal {
+pub struct TerminalView {
     connection: ModelHandle<TerminalConnection>,
     has_new_content: bool,
     //Currently using iTerm bell, show bell emoji in tab until input is received
@@ -73,18 +71,14 @@ pub struct Terminal {
     modal: bool,
 }
 
-impl Entity for Terminal {
+impl Entity for TerminalView {
     type Event = Event;
 }
 
-impl Terminal {
+impl TerminalView {
     ///Create a new Terminal view. This spawns a task, a thread, and opens the TTY devices
     ///To get the right working directory from a workspace, use: `get_wd_for_workspace()`
-    fn new(
-        working_directory: Option<PathBuf>,
-        modal: bool,
-        cx: &mut ViewContext<Self>,
-    ) -> Result<Self> {
+    fn new(working_directory: Option<PathBuf>, modal: bool, cx: &mut ViewContext<Self>) -> Self {
         //The details here don't matter, the terminal will be resized on the first layout
         let size_info = SizeInfo::new(
             DEBUG_TERMINAL_WIDTH,
@@ -103,18 +97,17 @@ impl Terminal {
             (shell, envs)
         };
 
-        let connection = cx.try_add_model(|cx| {
-            TerminalConnection::new(working_directory, shell, envs, size_info, cx)
-        })?;
+        let connection = cx
+            .add_model(|cx| TerminalConnection::new(working_directory, shell, envs, size_info, cx));
 
-        Ok(Terminal::from_connection(connection, modal, cx))
+        TerminalView::from_connection(connection, modal, cx)
     }
 
     fn from_connection(
         connection: ModelHandle<TerminalConnection>,
         modal: bool,
         cx: &mut ViewContext<Self>,
-    ) -> Terminal {
+    ) -> TerminalView {
         cx.observe(&connection, |_, _, cx| cx.notify()).detach();
         cx.subscribe(&connection, |this, _, event, cx| match event {
             Event::Wakeup => {
@@ -137,7 +130,7 @@ impl Terminal {
         })
         .detach();
 
-        Terminal {
+        TerminalView {
             connection,
             has_new_content: true,
             has_bell: false,
@@ -152,73 +145,79 @@ impl Terminal {
 
     fn clear(&mut self, _: &Clear, cx: &mut ViewContext<Self>) {
         self.connection
-            .update(cx, |connection, _| connection.clear());
+            .read(cx)
+            .get_terminal()
+            .map(|term| term.clear());
     }
 
     ///Create a new Terminal in the current working directory or the user's home directory
     fn deploy(workspace: &mut Workspace, _: &Deploy, cx: &mut ViewContext<Workspace>) {
         let wd = get_wd_for_workspace(workspace, cx);
-        if let Some(view) = cx.add_option_view(|cx| Terminal::new(wd, false, cx).log_err()) {
-            workspace.add_item(Box::new(view), cx);
-        }
+        let view = cx.add_view(|cx| TerminalView::new(wd, false, cx));
+        workspace.add_item(Box::new(view), cx);
     }
 
     ///Attempt to paste the clipboard into the terminal
     fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
-        let term = self.connection.read(cx).term.lock();
-        let copy_text = term.selection_to_string();
-        match copy_text {
-            Some(s) => cx.write_to_clipboard(ClipboardItem::new(s)),
-            None => (),
-        }
+        self.connection
+            .read(cx)
+            .get_terminal()
+            .and_then(|term| term.copy())
+            .map(|text| cx.write_to_clipboard(ClipboardItem::new(text)));
     }
 
     ///Attempt to paste the clipboard into the terminal
     fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
-        if let Some(item) = cx.read_from_clipboard() {
-            self.connection.update(cx, |connection, _| {
-                connection.paste(item.text());
-            })
-        }
+        cx.read_from_clipboard().map(|item| {
+            self.connection
+                .read(cx)
+                .get_terminal()
+                .map(|term| term.paste(item.text()));
+        });
     }
 
     ///Synthesize the keyboard event corresponding to 'up'
     fn up(&mut self, _: &Up, cx: &mut ViewContext<Self>) {
-        self.connection.update(cx, |connection, _| {
-            connection.try_keystroke(&Keystroke::parse("up").unwrap());
-        });
+        self.connection
+            .read(cx)
+            .get_terminal()
+            .map(|term| term.try_keystroke(&Keystroke::parse("up").unwrap()));
     }
 
     ///Synthesize the keyboard event corresponding to 'down'
     fn down(&mut self, _: &Down, cx: &mut ViewContext<Self>) {
-        self.connection.update(cx, |connection, _| {
-            connection.try_keystroke(&Keystroke::parse("down").unwrap());
-        });
+        self.connection
+            .read(cx)
+            .get_terminal()
+            .map(|term| term.try_keystroke(&Keystroke::parse("down").unwrap()));
     }
 
     ///Synthesize the keyboard event corresponding to 'ctrl-c'
     fn ctrl_c(&mut self, _: &CtrlC, cx: &mut ViewContext<Self>) {
-        self.connection.update(cx, |connection, _| {
-            connection.try_keystroke(&Keystroke::parse("ctrl-c").unwrap());
-        });
+        self.connection
+            .read(cx)
+            .get_terminal()
+            .map(|term| term.try_keystroke(&Keystroke::parse("ctrl-c").unwrap()));
     }
 
     ///Synthesize the keyboard event corresponding to 'escape'
     fn escape(&mut self, _: &Escape, cx: &mut ViewContext<Self>) {
-        self.connection.update(cx, |connection, _| {
-            connection.try_keystroke(&Keystroke::parse("escape").unwrap());
-        });
+        self.connection
+            .read(cx)
+            .get_terminal()
+            .map(|term| term.try_keystroke(&Keystroke::parse("escape").unwrap()));
     }
 
     ///Synthesize the keyboard event corresponding to 'enter'
     fn enter(&mut self, _: &Enter, cx: &mut ViewContext<Self>) {
-        self.connection.update(cx, |connection, _| {
-            connection.try_keystroke(&Keystroke::parse("enter").unwrap());
-        });
+        self.connection
+            .read(cx)
+            .get_terminal()
+            .map(|term| term.try_keystroke(&Keystroke::parse("enter").unwrap()));
     }
 }
 
-impl View for Terminal {
+impl View for TerminalView {
     fn ui_name() -> &'static str {
         "Terminal"
     }
@@ -252,36 +251,42 @@ impl View for Terminal {
     }
 }
 
-impl Item for Terminal {
+impl Item for TerminalView {
     fn tab_content(
         &self,
         _detail: Option<usize>,
         tab_theme: &theme::Tab,
         cx: &gpui::AppContext,
     ) -> ElementBox {
-        Flex::row()
-            .with_child(
-                Label::new(
-                    self.connection.read(cx).title.clone(),
-                    tab_theme.label.clone(),
-                )
+        let mut flex = Flex::row();
+
+        let title = match self.connection.read(cx) {
+            TerminalConnection::Connected(conn) => conn.title,
+            TerminalConnection::Disconnected { .. } => "Terminal".to_string(), //TODO ask nate about htis
+        };
+
+        flex.with_child(
+            Label::new(title, tab_theme.label.clone())
                 .aligned()
                 .contained()
                 .boxed(),
-            )
-            .boxed()
+        )
+        .boxed()
     }
 
     fn clone_on_split(&self, cx: &mut ViewContext<Self>) -> Option<Self> {
         //From what I can tell, there's no  way to tell the current working
         //Directory of the terminal from outside the shell. There might be
         //solutions to this, but they are non-trivial and require more IPC
-        Terminal::new(
-            self.connection.read(cx).associated_directory.clone(),
-            false,
-            cx,
-        )
-        .ok()
+
+        let wd = self
+            .connection
+            .read(cx)
+            .get_terminal()
+            .and_then(|term| term.associated_directory)
+            .clone();
+
+        Some(TerminalView::new(wd, false, cx))
     }
 
     fn project_path(&self, _cx: &gpui::AppContext) -> Option<ProjectPath> {

crates/terminal/src/terminal_element.rs 🔗

@@ -105,17 +105,20 @@ impl TerminalEl {
                     MouseButton::Left,
                     move |MouseButtonEvent { position, .. }, cx| {
                         if let Some(conn_handle) = mouse_down_connection.upgrade(cx.app) {
-                            conn_handle.update(cx.app, |conn, _cx| {
-                                let mut term = conn.term.lock();
-                                let (point, side) = mouse_to_cell_data(
-                                    position,
-                                    origin,
-                                    conn.cur_size,
-                                    term.renderable_content().display_offset,
-                                );
-                                term.selection =
-                                    Some(Selection::new(SelectionType::Simple, point, side))
-                            });
+                            conn_handle.update(cx.app, |connection, cx| {
+                                connection.get_terminal().map(|terminal| {
+                                    let (point, side) = mouse_to_cell_data(
+                                        position,
+                                        origin,
+                                        cur_size,
+                                        terminal.get_display_offset(),
+                                    );
+
+                                    terminal.mouse_down(point, side);
+
+                                    cx.notify();
+                                });
+                            })
                         }
                     },
                 )
@@ -130,13 +133,11 @@ impl TerminalEl {
                         cx.focus_parent_view();
                         if let Some(conn_handle) = click_connection.upgrade(cx.app) {
                             conn_handle.update(cx.app, |conn, cx| {
-                                let mut term = conn.term.lock();
-
                                 let (point, side) = mouse_to_cell_data(
                                     position,
                                     origin,
-                                    conn.cur_size,
-                                    term.renderable_content().display_offset,
+                                    // conn.cur_size,
+                                    // term.renderable_content().display_offset,
                                 );
 
                                 let selection_type = match click_count {
@@ -150,9 +151,7 @@ impl TerminalEl {
                                 let selection = selection_type.map(|selection_type| {
                                     Selection::new(selection_type, point, side)
                                 });
-
-                                term.selection = selection;
-
+                                conn.set_selection(selection);
                                 cx.notify();
                             });
                         }
@@ -162,22 +161,19 @@ impl TerminalEl {
                     MouseButton::Left,
                     move |_, MouseMovedEvent { position, .. }, cx| {
                         if let Some(conn_handle) = drag_connection.upgrade(cx.app) {
-                            conn_handle.update(cx.app, |conn, cx| {
-                                let mut term = conn.term.lock();
-
-                                let (point, side) = mouse_to_cell_data(
-                                    position,
-                                    origin,
-                                    conn.cur_size,
-                                    term.renderable_content().display_offset,
-                                );
-
-                                if let Some(mut selection) = term.selection.take() {
-                                    selection.update(point, side);
-                                    term.selection = Some(selection);
-                                }
-
-                                cx.notify()
+                            conn_handle.update(cx.app, |connection, cx| {
+                                connection.get_terminal().map(|terminal| {
+                                    let (point, side) = mouse_to_cell_data(
+                                        position,
+                                        origin,
+                                        cur_size,
+                                        terminal.get_display_offset(),
+                                    );
+
+                                    terminal.drag(point, side);
+
+                                    cx.notify()
+                                });
                             });
                         }
                     },
@@ -197,14 +193,17 @@ impl Element for TerminalEl {
     ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
         let tcx = TerminalLayoutContext::new(cx.global::<Settings>(), &cx.font_cache());
 
-        let term = {
-            let connection = self.connection.upgrade(cx).unwrap().read(cx);
-            //This locks the terminal, so resize it first.
-            connection.set_size(make_new_size(constraint, &tcx.cell_width, &tcx.line_height));
-            connection.term.lock()
-        };
+        let terminal = self
+            .connection
+            .upgrade(cx)
+            .unwrap()
+            .read(cx)
+            .get_terminal() //TODO!
+            .unwrap();
+        //This locks the terminal, so resize it first.
+        terminal.set_size(make_new_size(constraint, &tcx.cell_width, &tcx.line_height));
 
-        let content = term.renderable_content();
+        let (content, grid) = terminal.renderable_content();
 
         //Layout grid cells
 
@@ -219,7 +218,7 @@ impl Element for TerminalEl {
 
         //Layout cursor
         let cursor = layout_cursor(
-            term.grid(),
+            grid,
             cx.text_layout_cache,
             &tcx,
             content.cursor.point,
@@ -390,14 +389,12 @@ impl Element for TerminalEl {
                     let vertical_scroll =
                         (delta.y() / layout.line_height.0) * ALACRITTY_SCROLL_MULTIPLIER;
 
-                    if let Some(connection) = self.connection.upgrade(cx.app) {
-                        connection.update(cx.app, |connection, _| {
-                            connection
-                                .term
-                                .lock()
-                                .scroll_display(Scroll::Delta(vertical_scroll.round() as i32));
-                        })
-                    }
+                    self.connection
+                        .upgrade(cx.app)
+                        .and_then(|handle| handle.read(cx.app).get_terminal())
+                        .map(|terminal| {
+                            terminal.scroll(Scroll::Delta(vertical_scroll.round() as i32));
+                        });
                 })
                 .is_some(),
             Event::KeyDown(KeyDownEvent { keystroke, .. }) => {
@@ -412,16 +409,18 @@ impl Element for TerminalEl {
 
                 self.connection
                     .upgrade(cx.app)
-                    .map(|connection| {
-                        connection
-                            .update(cx.app, |connection, _| connection.try_keystroke(keystroke))
-                    })
+                    .and_then(|model_handle| model_handle.read(cx.app).get_terminal())
+                    .map(|term| term.try_keystroke(keystroke))
                     .unwrap_or(false)
             }
             _ => false,
         }
     }
 
+    fn metadata(&self) -> Option<&dyn std::any::Any> {
+        None
+    }
+
     fn debug(
         &self,
         _bounds: gpui::geometry::rect::RectF,

crates/terminal/src/tests/terminal_test_context.rs 🔗

@@ -29,7 +29,7 @@ impl<'a> TerminalTestContext<'a> {
         );
 
         let connection =
-            cx.add_model(|cx| TerminalConnection::new(None, None, None, size_info, cx).unwrap());
+            cx.add_model(|cx| TerminalConnection::new(None, None, None, size_info, cx));
 
         TerminalTestContext { cx, connection }
     }
@@ -40,8 +40,11 @@ impl<'a> TerminalTestContext<'a> {
     {
         let command = command.to_string();
         self.connection.update(self.cx, |connection, _| {
-            connection.write_to_pty(command);
-            connection.write_to_pty("\r".to_string());
+            connection.get_terminal().unwrap().write_to_pty(command);
+            connection
+                .get_terminal()
+                .unwrap()
+                .write_to_pty("\r".to_string());
         });
 
         self.connection
@@ -58,9 +61,8 @@ impl<'a> TerminalTestContext<'a> {
     }
 
     fn grid_as_str(connection: &TerminalConnection) -> String {
-        let term = connection.term.lock();
-        let grid_iterator = term.renderable_content().display_iter;
-        let lines = grid_iterator.group_by(|i| i.point.line.0);
+        let (grid_iterator, _) = connection.get_terminal().unwrap().renderable_content();
+        let lines = grid_iterator.display_iter.group_by(|i| i.point.line.0);
         lines
             .into_iter()
             .map(|(_, line)| line.map(|i| i.c).collect::<String>())