Improved performance of terminal rendering further

Mikayla Maki created

Change summary

crates/terminal/src/modal.rs                   |   7 
crates/terminal/src/terminal.rs                | 182 +++++++++++++++----
crates/terminal/src/terminal_container_view.rs |  40 ++-
crates/terminal/src/terminal_element.rs        |  85 ++------
crates/terminal/src/terminal_view.rs           |  11 
styles/package-lock.json                       |   1 
6 files changed, 194 insertions(+), 132 deletions(-)

Detailed changes

crates/terminal/src/modal.rs 🔗

@@ -4,7 +4,7 @@ use workspace::Workspace;
 
 use crate::{
     terminal_container_view::{
-        get_working_directory, DeployModal, TerminalContainer, TerminalContent,
+        get_working_directory, DeployModal, TerminalContainer, TerminalContainerContent,
     },
     Event, Terminal,
 };
@@ -42,7 +42,7 @@ pub fn deploy_modal(workspace: &mut Workspace, _: &DeployModal, cx: &mut ViewCon
 
             let this = cx.add_view(|cx| TerminalContainer::new(working_directory, true, cx));
 
-            if let TerminalContent::Connected(connected) = &this.read(cx).content {
+            if let TerminalContainerContent::Connected(connected) = &this.read(cx).content {
                 let terminal_handle = connected.read(cx).handle();
                 cx.subscribe(&terminal_handle, on_event).detach();
                 // Set the global immediately if terminal construction was successful,
@@ -55,7 +55,8 @@ pub fn deploy_modal(workspace: &mut Workspace, _: &DeployModal, cx: &mut ViewCon
             this
         }) {
             // Terminal modal was dismissed. Store terminal if the terminal view is connected
-            if let TerminalContent::Connected(connected) = &closed_terminal_handle.read(cx).content
+            if let TerminalContainerContent::Connected(connected) =
+                &closed_terminal_handle.read(cx).content
             {
                 let terminal_handle = connected.read(cx).handle();
                 // Set the global immediately if terminal construction was successful,

crates/terminal/src/terminal.rs 🔗

@@ -11,17 +11,19 @@ use alacritty_terminal::{
     event_loop::{EventLoop, Msg, Notifier},
     grid::{Dimensions, Scroll as AlacScroll},
     index::{Column, Direction, Line, Point},
-    selection::{Selection, SelectionType},
+    selection::{Selection, SelectionRange, SelectionType},
     sync::FairMutex,
     term::{
+        cell::Cell,
         color::Rgb,
         search::{Match, RegexIter, RegexSearch},
-        RenderableContent, TermMode,
+        RenderableCursor, TermMode,
     },
     tty::{self, setup_env},
     Term,
 };
 use anyhow::{bail, Result};
+
 use futures::{
     channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender},
     FutureExt,
@@ -36,10 +38,10 @@ use settings::{AlternateScroll, Settings, Shell, TerminalBlink};
 use std::{
     collections::{HashMap, VecDeque},
     fmt::Display,
-    ops::{RangeInclusive, Sub},
+    ops::{Deref, RangeInclusive, Sub},
     path::PathBuf,
     sync::Arc,
-    time::Duration,
+    time::{Duration, Instant},
 };
 use thiserror::Error;
 
@@ -376,12 +378,12 @@ impl TerminalBuilder {
             events: VecDeque::with_capacity(10), //Should never get this high.
             title: shell_txt.clone(),
             default_title: shell_txt,
-            last_mode: TermMode::NONE,
+            last_content: Default::default(),
             cur_size: initial_size,
             last_mouse: None,
-            last_offset: 0,
             matches: Vec::new(),
-            selection_text: None,
+            last_synced: Instant::now(),
+            sync_task: None,
         };
 
         Ok(TerminalBuilder {
@@ -443,18 +445,61 @@ impl TerminalBuilder {
     }
 }
 
+#[derive(Debug, Clone)]
+struct IndexedCell {
+    point: Point,
+    cell: Cell,
+}
+
+impl Deref for IndexedCell {
+    type Target = Cell;
+
+    #[inline]
+    fn deref(&self) -> &Cell {
+        &self.cell
+    }
+}
+
+#[derive(Clone)]
+pub struct TerminalContent {
+    cells: Vec<IndexedCell>,
+    mode: TermMode,
+    display_offset: usize,
+    selection_text: Option<String>,
+    selection: Option<SelectionRange>,
+    cursor: RenderableCursor,
+    cursor_char: char,
+}
+
+impl Default for TerminalContent {
+    fn default() -> Self {
+        TerminalContent {
+            cells: Default::default(),
+            mode: Default::default(),
+            display_offset: Default::default(),
+            selection_text: Default::default(),
+            selection: Default::default(),
+            cursor: RenderableCursor {
+                shape: alacritty_terminal::ansi::CursorShape::Block,
+                point: Point::new(Line(0), Column(0)),
+            },
+            cursor_char: Default::default(),
+        }
+    }
+}
+
 pub struct Terminal {
     pty_tx: Notifier,
     term: Arc<FairMutex<Term<ZedListener>>>,
     events: VecDeque<InternalEvent>,
     default_title: String,
     title: String,
-    cur_size: TerminalSize,
-    last_mode: TermMode,
-    last_offset: usize,
     last_mouse: Option<(Point, Direction)>,
     pub matches: Vec<RangeInclusive<Point>>,
-    pub selection_text: Option<String>,
+    cur_size: TerminalSize,
+    last_content: TerminalContent,
+    last_synced: Instant,
+    sync_task: Option<Task<()>>,
 }
 
 impl Terminal {
@@ -576,6 +621,10 @@ impl Terminal {
         }
     }
 
+    pub fn last_content(&self) -> &TerminalContent {
+        &self.last_content
+    }
+
     fn begin_select(&mut self, sel: Selection) {
         self.events
             .push_back(InternalEvent::SetSelection(Some(sel)));
@@ -648,7 +697,7 @@ impl Terminal {
     }
 
     pub fn try_keystroke(&mut self, keystroke: &Keystroke) -> bool {
-        let esc = to_esc_str(keystroke, &self.last_mode);
+        let esc = to_esc_str(keystroke, &self.last_content.mode);
         if let Some(esc) = esc {
             self.input(esc);
             true
@@ -659,7 +708,7 @@ impl Terminal {
 
     ///Paste text into the terminal
     pub fn paste(&mut self, text: &str) {
-        let paste_text = if self.last_mode.contains(TermMode::BRACKETED_PASTE) {
+        let paste_text = if self.last_content.mode.contains(TermMode::BRACKETED_PASTE) {
             format!("{}{}{}", "\x1b[200~", text.replace('\x1b', ""), "\x1b[201~")
         } else {
             text.replace("\r\n", "\r").replace('\n', "\r")
@@ -667,38 +716,76 @@ impl Terminal {
         self.input(paste_text)
     }
 
-    pub fn render_lock<F, T>(&mut self, cx: &mut ModelContext<Self>, f: F) -> T
-    where
-        F: FnOnce(RenderableContent, char) -> T,
-    {
+    pub fn try_sync(&mut self, cx: &mut ModelContext<Self>) {
         let term = self.term.clone();
-        let mut term = term.lock();
+
+        let mut terminal = if let Some(term) = term.try_lock_unfair() {
+            term
+        } else if self.last_synced.elapsed().as_secs_f32() > 0.25 {
+            term.lock_unfair()
+        } else if let None = self.sync_task {
+            //Skip this frame
+            let delay = cx.background().timer(Duration::from_millis(16));
+            self.sync_task = Some(cx.spawn_weak(|weak_handle, mut cx| async move {
+                delay.await;
+                cx.update(|cx| {
+                    if let Some(handle) = weak_handle.upgrade(cx) {
+                        handle.update(cx, |terminal, cx| {
+                            terminal.sync_task.take();
+                            cx.notify();
+                        });
+                    }
+                });
+            }));
+            return;
+        } else {
+            //No lock and delayed rendering already scheduled, nothing to do
+            return;
+        };
 
         //Note that this ordering matters for event processing
         while let Some(e) = self.events.pop_front() {
-            self.process_terminal_event(&e, &mut term, cx)
+            self.process_terminal_event(&e, &mut terminal, cx)
         }
 
-        self.last_mode = *term.mode();
+        self.last_content = Self::make_content(&terminal);
+        self.last_synced = Instant::now();
+    }
 
+    fn make_content(term: &Term<ZedListener>) -> TerminalContent {
         let content = term.renderable_content();
-
-        self.selection_text = term.selection_to_string();
-        self.last_offset = content.display_offset;
-
-        let cursor_text = term.grid()[content.cursor.point].c;
-
-        f(content, cursor_text)
+        TerminalContent {
+            cells: content
+                .display_iter
+                //TODO: Add this once there's a way to retain empty lines
+                // .filter(|ic| {
+                //     !ic.flags.contains(Flags::HIDDEN)
+                //         && !(ic.bg == Named(NamedColor::Background)
+                //             && ic.c == ' '
+                //             && !ic.flags.contains(Flags::INVERSE))
+                // })
+                .map(|ic| IndexedCell {
+                    point: ic.point,
+                    cell: ic.cell.clone(),
+                })
+                .collect::<Vec<IndexedCell>>(),
+            mode: content.mode,
+            display_offset: content.display_offset,
+            selection_text: term.selection_to_string(),
+            selection: content.selection,
+            cursor: content.cursor,
+            cursor_char: term.grid()[content.cursor.point].c,
+        }
     }
 
     pub fn focus_in(&self) {
-        if self.last_mode.contains(TermMode::FOCUS_IN_OUT) {
+        if self.last_content.mode.contains(TermMode::FOCUS_IN_OUT) {
             self.write_to_pty("\x1b[I".to_string());
         }
     }
 
     pub fn focus_out(&self) {
-        if self.last_mode.contains(TermMode::FOCUS_IN_OUT) {
+        if self.last_content.mode.contains(TermMode::FOCUS_IN_OUT) {
             self.write_to_pty("\x1b[O".to_string());
         }
     }
@@ -721,17 +808,17 @@ impl Terminal {
     }
 
     pub fn mouse_mode(&self, shift: bool) -> bool {
-        self.last_mode.intersects(TermMode::MOUSE_MODE) && !shift
+        self.last_content.mode.intersects(TermMode::MOUSE_MODE) && !shift
     }
 
     pub fn mouse_move(&mut self, e: &MouseMovedEvent, origin: Vector2F) {
         let position = e.position.sub(origin);
 
-        let point = mouse_point(position, self.cur_size, self.last_offset);
+        let point = mouse_point(position, self.cur_size, self.last_content.display_offset);
         let side = mouse_side(position, self.cur_size);
 
         if self.mouse_changed(point, side) && self.mouse_mode(e.shift) {
-            if let Some(bytes) = mouse_moved_report(point, e, self.last_mode) {
+            if let Some(bytes) = mouse_moved_report(point, e, self.last_content.mode) {
                 self.pty_tx.notify(bytes);
             }
         }
@@ -746,7 +833,7 @@ impl Terminal {
             self.continue_selection(position);
 
             // Doesn't make sense to scroll the alt screen
-            if !self.last_mode.contains(TermMode::ALT_SCREEN) {
+            if !self.last_content.mode.contains(TermMode::ALT_SCREEN) {
                 let scroll_delta = match self.drag_line_delta(e) {
                     Some(value) => value,
                     None => return,
@@ -775,11 +862,11 @@ impl Terminal {
 
     pub fn mouse_down(&mut self, e: &DownRegionEvent, origin: Vector2F) {
         let position = e.position.sub(origin);
-        let point = mouse_point(position, self.cur_size, self.last_offset);
+        let point = mouse_point(position, self.cur_size, self.last_content.display_offset);
         let side = mouse_side(position, self.cur_size);
 
         if self.mouse_mode(e.shift) {
-            if let Some(bytes) = mouse_button_report(point, e, true, self.last_mode) {
+            if let Some(bytes) = mouse_button_report(point, e, true, self.last_content.mode) {
                 self.pty_tx.notify(bytes);
             }
         } else if e.button == MouseButton::Left {
@@ -791,7 +878,7 @@ impl Terminal {
         let position = e.position.sub(origin);
 
         if !self.mouse_mode(e.shift) {
-            let point = mouse_point(position, self.cur_size, self.last_offset);
+            let point = mouse_point(position, self.cur_size, self.last_content.display_offset);
             let side = mouse_side(position, self.cur_size);
 
             let selection_type = match e.click_count {
@@ -814,9 +901,9 @@ impl Terminal {
     pub fn mouse_up(&mut self, e: &UpRegionEvent, origin: Vector2F) {
         let position = e.position.sub(origin);
         if self.mouse_mode(e.shift) {
-            let point = mouse_point(position, self.cur_size, self.last_offset);
+            let point = mouse_point(position, self.cur_size, self.last_content.display_offset);
 
-            if let Some(bytes) = mouse_button_report(point, e, false, self.last_mode) {
+            if let Some(bytes) = mouse_button_report(point, e, false, self.last_content.mode) {
                 self.pty_tx.notify(bytes);
             }
         } else if e.button == MouseButton::Left {
@@ -835,15 +922,22 @@ impl Terminal {
             //The scroll enters 'TouchPhase::Started'. Do I need to replicate this?
             //This would be consistent with a scroll model based on 'distance from origin'...
             let scroll_lines = (e.delta.y() / self.cur_size.line_height) as i32;
-            let point = mouse_point(e.position.sub(origin), self.cur_size, self.last_offset);
-
-            if let Some(scrolls) = scroll_report(point, scroll_lines as i32, e, self.last_mode) {
+            let point = mouse_point(
+                e.position.sub(origin),
+                self.cur_size,
+                self.last_content.display_offset,
+            );
+
+            if let Some(scrolls) =
+                scroll_report(point, scroll_lines as i32, e, self.last_content.mode)
+            {
                 for scroll in scrolls {
                     self.pty_tx.notify(scroll);
                 }
             };
         } else if self
-            .last_mode
+            .last_content
+            .mode
             .contains(TermMode::ALT_SCREEN | TermMode::ALTERNATE_SCROLL)
             && !e.shift
         {
@@ -868,7 +962,6 @@ impl Terminal {
         cx: &mut ModelContext<Self>,
     ) -> Task<Vec<RangeInclusive<Point>>> {
         let term = self.term.clone();
-        dbg!("Spawning find_matches");
         cx.background().spawn(async move {
             let searcher = match query {
                 project::search::SearchQuery::Text { query, .. } => {
@@ -885,7 +978,8 @@ impl Terminal {
             let searcher = searcher.unwrap();
 
             let term = term.lock();
-            dbg!(make_search_matches(&term, &searcher).collect())
+
+            make_search_matches(&term, &searcher).collect()
         })
     }
 }

crates/terminal/src/terminal_container_view.rs 🔗

@@ -29,12 +29,12 @@ pub fn init(cx: &mut MutableAppContext) {
 //Take away all the result unwrapping in the current TerminalView by making it 'infallible'
 //Bubble up to deploy(_modal)() calls
 
-pub enum TerminalContent {
+pub enum TerminalContainerContent {
     Connected(ViewHandle<TerminalView>),
     Error(ViewHandle<ErrorView>),
 }
 
-impl TerminalContent {
+impl TerminalContainerContent {
     fn handle(&self) -> AnyViewHandle {
         match self {
             Self::Connected(handle) => handle.into(),
@@ -45,7 +45,7 @@ impl TerminalContent {
 
 pub struct TerminalContainer {
     modal: bool,
-    pub content: TerminalContent,
+    pub content: TerminalContainerContent,
     associated_directory: Option<PathBuf>,
 }
 
@@ -119,13 +119,13 @@ impl TerminalContainer {
                 let view = cx.add_view(|cx| TerminalView::from_terminal(terminal, modal, cx));
                 cx.subscribe(&view, |_this, _content, event, cx| cx.emit(*event))
                     .detach();
-                TerminalContent::Connected(view)
+                TerminalContainerContent::Connected(view)
             }
             Err(error) => {
                 let view = cx.add_view(|_| ErrorView {
                     error: error.downcast::<TerminalError>().unwrap(),
                 });
-                TerminalContent::Error(view)
+                TerminalContainerContent::Error(view)
             }
         };
         cx.focus(content.handle());
@@ -145,7 +145,7 @@ impl TerminalContainer {
         let connected_view = cx.add_view(|cx| TerminalView::from_terminal(terminal, modal, cx));
         TerminalContainer {
             modal,
-            content: TerminalContent::Connected(connected_view),
+            content: TerminalContainerContent::Connected(connected_view),
             associated_directory: None,
         }
     }
@@ -158,8 +158,8 @@ impl View for TerminalContainer {
 
     fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> ElementBox {
         let child_view = match &self.content {
-            TerminalContent::Connected(connected) => ChildView::new(connected),
-            TerminalContent::Error(error) => ChildView::new(error),
+            TerminalContainerContent::Connected(connected) => ChildView::new(connected),
+            TerminalContainerContent::Error(error) => ChildView::new(error),
         };
         if self.modal {
             let settings = cx.global::<Settings>();
@@ -238,10 +238,10 @@ impl Item for TerminalContainer {
         cx: &gpui::AppContext,
     ) -> ElementBox {
         let title = match &self.content {
-            TerminalContent::Connected(connected) => {
+            TerminalContainerContent::Connected(connected) => {
                 connected.read(cx).handle().read(cx).title.to_string()
             }
-            TerminalContent::Error(_) => "Terminal".to_string(),
+            TerminalContainerContent::Error(_) => "Terminal".to_string(),
         };
 
         Flex::row()
@@ -309,7 +309,7 @@ impl Item for TerminalContainer {
     }
 
     fn is_dirty(&self, cx: &gpui::AppContext) -> bool {
-        if let TerminalContent::Connected(connected) = &self.content {
+        if let TerminalContainerContent::Connected(connected) = &self.content {
             connected.read(cx).has_new_content()
         } else {
             false
@@ -317,7 +317,7 @@ impl Item for TerminalContainer {
     }
 
     fn has_conflict(&self, cx: &AppContext) -> bool {
-        if let TerminalContent::Connected(connected) = &self.content {
+        if let TerminalContainerContent::Connected(connected) = &self.content {
             connected.read(cx).has_bell()
         } else {
             false
@@ -351,7 +351,7 @@ impl SearchableItem for TerminalContainer {
 
     /// Clear stored matches
     fn clear_matches(&mut self, cx: &mut ViewContext<Self>) {
-        if let TerminalContent::Connected(connected) = &self.content {
+        if let TerminalContainerContent::Connected(connected) = &self.content {
             let terminal = connected.read(cx).terminal().clone();
             terminal.update(cx, |term, _| term.matches.clear())
         }
@@ -359,18 +359,22 @@ impl SearchableItem for TerminalContainer {
 
     /// Store matches returned from find_matches somewhere for rendering
     fn update_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>) {
-        if let TerminalContent::Connected(connected) = &self.content {
+        if let TerminalContainerContent::Connected(connected) = &self.content {
             let terminal = connected.read(cx).terminal().clone();
-            dbg!(&matches);
             terminal.update(cx, |term, _| term.matches = matches)
         }
     }
 
     /// Return the selection content to pre-load into this search
     fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String {
-        if let TerminalContent::Connected(connected) = &self.content {
+        if let TerminalContainerContent::Connected(connected) = &self.content {
             let terminal = connected.read(cx).terminal().clone();
-            terminal.read(cx).selection_text.clone().unwrap_or_default()
+            terminal
+                .read(cx)
+                .last_content
+                .selection_text
+                .clone()
+                .unwrap_or_default()
         } else {
             Default::default()
         }
@@ -403,7 +407,7 @@ impl SearchableItem for TerminalContainer {
         query: project::search::SearchQuery,
         cx: &mut ViewContext<Self>,
     ) -> Task<Vec<Self::Match>> {
-        if let TerminalContent::Connected(connected) = &self.content {
+        if let TerminalContainerContent::Connected(connected) = &self.content {
             let terminal = connected.read(cx).terminal().clone();
             terminal.update(cx, |term, cx| term.find_matches(query, cx))
         } else {

crates/terminal/src/terminal_element.rs 🔗

@@ -2,10 +2,7 @@ use alacritty_terminal::{
     ansi::{Color as AnsiColor, Color::Named, CursorShape as AlacCursorShape, NamedColor},
     grid::Dimensions,
     index::Point,
-    term::{
-        cell::{Cell, Flags},
-        TermMode,
-    },
+    term::{cell::Flags, TermMode},
 };
 use editor::{Cursor, CursorShape, HighlightedRange, HighlightedRangeLine};
 use gpui::{
@@ -27,15 +24,12 @@ use theme::TerminalStyle;
 use util::ResultExt;
 
 use std::{fmt::Debug, ops::RangeInclusive};
-use std::{
-    mem,
-    ops::{Deref, Range},
-};
+use std::{mem, ops::Range};
 
 use crate::{
     mappings::colors::convert_color,
     terminal_view::{DeployContextMenu, TerminalView},
-    Terminal, TerminalSize,
+    IndexedCell, Terminal, TerminalContent, TerminalSize,
 };
 
 ///The information generated during layout that is nescessary for painting
@@ -50,21 +44,6 @@ pub struct LayoutState {
     display_offset: usize,
 }
 
-#[derive(Debug)]
-struct IndexedCell {
-    point: Point,
-    cell: Cell,
-}
-
-impl Deref for IndexedCell {
-    type Target = Cell;
-
-    #[inline]
-    fn deref(&self) -> &Cell {
-        &self.cell
-    }
-}
-
 ///Helper struct for converting data between alacritty's cursor points, and displayed cursor points
 struct DisplayCursor {
     line: i32,
@@ -195,7 +174,7 @@ impl TerminalElement {
     //Vec<Range<Point>> -> Clip out the parts of the ranges
 
     fn layout_grid(
-        grid: Vec<IndexedCell>,
+        grid: &Vec<IndexedCell>,
         text_style: &TextStyle,
         terminal_theme: &TerminalStyle,
         text_layout_cache: &TextLayoutCache,
@@ -581,40 +560,22 @@ impl Element for TerminalElement {
         } else {
             terminal_theme.colors.background
         };
+        let terminal_handle = self.terminal.upgrade(cx).unwrap();
 
-        let (cells, selection, cursor, display_offset, cursor_text, mode) = self
-            .terminal
-            .upgrade(cx)
-            .unwrap()
-            .update(cx.app, |terminal, cx| {
-                terminal.set_size(dimensions);
-                terminal.render_lock(cx, |content, cursor_text| {
-                    let mut cells = vec![];
-                    cells.extend(
-                        content
-                            .display_iter
-                            //TODO: Add this once there's a way to retain empty lines
-                            // .filter(|ic| {
-                            //     !ic.flags.contains(Flags::HIDDEN)
-                            //         && !(ic.bg == Named(NamedColor::Background)
-                            //             && ic.c == ' '
-                            //             && !ic.flags.contains(Flags::INVERSE))
-                            // })
-                            .map(|ic| IndexedCell {
-                                point: ic.point,
-                                cell: ic.cell.clone(),
-                            }),
-                    );
-                    (
-                        cells,
-                        content.selection,
-                        content.cursor,
-                        content.display_offset,
-                        cursor_text,
-                        content.mode,
-                    )
-                })
-            });
+        terminal_handle.update(cx.app, |terminal, cx| {
+            terminal.set_size(dimensions);
+            terminal.try_sync(cx)
+        });
+
+        let TerminalContent {
+            cells,
+            mode,
+            display_offset,
+            cursor_char,
+            selection,
+            cursor,
+            ..
+        } = &terminal_handle.read(cx).last_content;
 
         // searches, highlights to a single range representations
         let mut relative_highlighted_ranges = Vec::new();
@@ -641,9 +602,9 @@ impl Element for TerminalElement {
         let cursor = if let AlacCursorShape::Hidden = cursor.shape {
             None
         } else {
-            let cursor_point = DisplayCursor::from(cursor.point, display_offset);
+            let cursor_point = DisplayCursor::from(cursor.point, *display_offset);
             let cursor_text = {
-                let str_trxt = cursor_text.to_string();
+                let str_trxt = cursor_char.to_string();
 
                 let color = if self.focused {
                     terminal_theme.colors.background
@@ -699,8 +660,8 @@ impl Element for TerminalElement {
                 size: dimensions,
                 rects,
                 relative_highlighted_ranges,
-                mode,
-                display_offset,
+                mode: *mode,
+                display_offset: *display_offset,
             },
         )
     }

crates/terminal/src/terminal_view.rs 🔗

@@ -149,7 +149,8 @@ impl TerminalView {
         if !self
             .terminal
             .read(cx)
-            .last_mode
+            .last_content
+            .mode
             .contains(TermMode::ALT_SCREEN)
         {
             cx.show_character_palette();
@@ -177,7 +178,8 @@ impl TerminalView {
             || self
                 .terminal
                 .read(cx)
-                .last_mode
+                .last_content
+                .mode
                 .contains(TermMode::ALT_SCREEN)
         {
             return true;
@@ -362,7 +364,8 @@ impl View for TerminalView {
         if self
             .terminal
             .read(cx)
-            .last_mode
+            .last_content
+            .mode
             .contains(TermMode::ALT_SCREEN)
         {
             None
@@ -387,7 +390,7 @@ impl View for TerminalView {
         if self.modal {
             context.set.insert("ModalTerminal".into());
         }
-        let mode = self.terminal.read(cx).last_mode;
+        let mode = self.terminal.read(cx).last_content.mode;
         context.map.insert(
             "screen".to_string(),
             (if mode.contains(TermMode::ALT_SCREEN) {

styles/package-lock.json 🔗

@@ -5,7 +5,6 @@
   "requires": true,
   "packages": {
     "": {
-      "name": "styles",
       "version": "1.0.0",
       "license": "ISC",
       "dependencies": {