Improved performance of terminal rendering further
This commit is contained in:
parent
a8b8003980
commit
faad24542f
6 changed files with 193 additions and 131 deletions
|
@ -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,
|
||||||
|
|
|
@ -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()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,41 +560,23 @@ 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
|
|
||||||
.upgrade(cx)
|
|
||||||
.unwrap()
|
|
||||||
.update(cx.app, |terminal, cx| {
|
|
||||||
terminal.set_size(dimensions);
|
terminal.set_size(dimensions);
|
||||||
terminal.render_lock(cx, |content, cursor_text| {
|
terminal.try_sync(cx)
|
||||||
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,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let TerminalContent {
|
||||||
|
cells,
|
||||||
|
mode,
|
||||||
|
display_offset,
|
||||||
|
cursor_char,
|
||||||
|
selection,
|
||||||
|
cursor,
|
||||||
|
..
|
||||||
|
} = &terminal_handle.read(cx).last_content;
|
||||||
|
|
||||||
// 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();
|
||||||
if let Some(selection) = selection {
|
if let Some(selection) = selection {
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
1
styles/package-lock.json
generated
1
styles/package-lock.json
generated
|
@ -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": {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue