Improved performance of terminal rendering further

This commit is contained in:
Mikayla Maki 2022-09-01 11:43:27 -07:00
parent a8b8003980
commit faad24542f
6 changed files with 193 additions and 131 deletions

View file

@ -4,7 +4,7 @@ use workspace::Workspace;
use crate::{ use crate::{
terminal_container_view::{ terminal_container_view::{
get_working_directory, DeployModal, TerminalContainer, TerminalContent, get_working_directory, DeployModal, TerminalContainer, TerminalContainerContent,
}, },
Event, Terminal, 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)); 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(); let terminal_handle = connected.read(cx).handle();
cx.subscribe(&terminal_handle, on_event).detach(); cx.subscribe(&terminal_handle, on_event).detach();
// Set the global immediately if terminal construction was successful, // 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 this
}) { }) {
// Terminal modal was dismissed. Store terminal if the terminal view is connected // 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(); let terminal_handle = connected.read(cx).handle();
// Set the global immediately if terminal construction was successful, // Set the global immediately if terminal construction was successful,

View file

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

View file

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

View file

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

View file

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

View file

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