Rearrange the terminal code to not have a cyclic dependency with the project
This commit is contained in:
parent
1b8763d0cf
commit
83aefffa38
12 changed files with 270 additions and 300 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -6271,6 +6271,7 @@ dependencies = [
|
||||||
"mio-extras",
|
"mio-extras",
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
"procinfo",
|
"procinfo",
|
||||||
|
"rand 0.8.5",
|
||||||
"serde",
|
"serde",
|
||||||
"settings",
|
"settings",
|
||||||
"shellexpand",
|
"shellexpand",
|
||||||
|
@ -6278,13 +6279,13 @@ dependencies = [
|
||||||
"smol",
|
"smol",
|
||||||
"theme",
|
"theme",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "terminal_view"
|
name = "terminal_view"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alacritty_terminal",
|
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"client",
|
"client",
|
||||||
"context_menu",
|
"context_menu",
|
||||||
|
@ -6307,6 +6308,7 @@ dependencies = [
|
||||||
"shellexpand",
|
"shellexpand",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"smol",
|
"smol",
|
||||||
|
"terminal",
|
||||||
"theme",
|
"theme",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"util",
|
"util",
|
||||||
|
|
|
@ -2422,7 +2422,7 @@ impl Editor {
|
||||||
let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
|
let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
|
||||||
let excerpt_range = excerpt_range.to_offset(buffer);
|
let excerpt_range = excerpt_range.to_offset(buffer);
|
||||||
buffer
|
buffer
|
||||||
.edited_ranges_for_transaction(transaction)
|
.edited_ranges_for_transaction::<usize>(transaction)
|
||||||
.all(|range| {
|
.all(|range| {
|
||||||
excerpt_range.start <= range.start
|
excerpt_range.start <= range.start
|
||||||
&& excerpt_range.end >= range.end
|
&& excerpt_range.end >= range.end
|
||||||
|
|
|
@ -60,9 +60,9 @@ use std::{
|
||||||
atomic::{AtomicUsize, Ordering::SeqCst},
|
atomic::{AtomicUsize, Ordering::SeqCst},
|
||||||
Arc,
|
Arc,
|
||||||
},
|
},
|
||||||
thread::panicking,
|
|
||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
use terminal::Terminal;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use util::{defer, post_inc, ResultExt, TryFutureExt as _};
|
use util::{defer, post_inc, ResultExt, TryFutureExt as _};
|
||||||
|
|
||||||
|
@ -1196,12 +1196,14 @@ impl Project {
|
||||||
|
|
||||||
pub fn create_terminal_connection(
|
pub fn create_terminal_connection(
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &mut ModelContext<Self>,
|
_cx: &mut ModelContext<Self>,
|
||||||
) -> Result<ModelHandle<TerminalConnection>> {
|
) -> Result<ModelHandle<Terminal>> {
|
||||||
if self.is_remote() {
|
if self.is_remote() {
|
||||||
return Err(anyhow!(
|
return Err(anyhow!(
|
||||||
"creating terminals as a guest is not supported yet"
|
"creating terminals as a guest is not supported yet"
|
||||||
));
|
));
|
||||||
|
} else {
|
||||||
|
unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ gpui = { path = "../gpui" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
db = { path = "../db" }
|
db = { path = "../db" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
|
util = { path = "../util" }
|
||||||
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty", rev = "a51dbe25d67e84d6ed4261e640d3954fbdd9be45" }
|
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty", rev = "a51dbe25d67e84d6ed4261e640d3954fbdd9be45" }
|
||||||
procinfo = { git = "https://github.com/zed-industries/wezterm", rev = "5cd757e5f2eb039ed0c6bb6512223e69d5efc64d", default-features = false }
|
procinfo = { git = "https://github.com/zed-industries/wezterm", rev = "5cd757e5f2eb039ed0c6bb6512223e69d5efc64d", default-features = false }
|
||||||
smallvec = { version = "1.6", features = ["union"] }
|
smallvec = { version = "1.6", features = ["union"] }
|
||||||
|
@ -27,4 +28,7 @@ libc = "0.2"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
rand = "0.8.5"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
pub mod mappings;
|
pub mod mappings;
|
||||||
mod persistence;
|
pub use alacritty_terminal;
|
||||||
|
|
||||||
use alacritty_terminal::{
|
use alacritty_terminal::{
|
||||||
ansi::{ClearMode, Handler},
|
ansi::{ClearMode, Handler},
|
||||||
|
@ -30,7 +30,6 @@ use mappings::mouse::{
|
||||||
alt_scroll, grid_point, mouse_button_report, mouse_moved_report, mouse_side, scroll_report,
|
alt_scroll, grid_point, mouse_button_report, mouse_moved_report, mouse_side, scroll_report,
|
||||||
};
|
};
|
||||||
|
|
||||||
use persistence::TERMINAL_CONNECTION;
|
|
||||||
use procinfo::LocalProcessInfo;
|
use procinfo::LocalProcessInfo;
|
||||||
use settings::{AlternateScroll, Settings, Shell, TerminalBlink};
|
use settings::{AlternateScroll, Settings, Shell, TerminalBlink};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
@ -53,8 +52,7 @@ use gpui::{
|
||||||
geometry::vector::{vec2f, Vector2F},
|
geometry::vector::{vec2f, Vector2F},
|
||||||
keymap::Keystroke,
|
keymap::Keystroke,
|
||||||
scene::{MouseDown, MouseDrag, MouseScrollWheel, MouseUp},
|
scene::{MouseDown, MouseDrag, MouseScrollWheel, MouseUp},
|
||||||
AppContext, ClipboardItem, Entity, ModelContext, MouseButton, MouseMovedEvent,
|
ClipboardItem, Entity, ModelContext, MouseButton, MouseMovedEvent, Task,
|
||||||
MutableAppContext, Task,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::mappings::{
|
use crate::mappings::{
|
||||||
|
@ -63,12 +61,6 @@ use crate::mappings::{
|
||||||
};
|
};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
///Initialize and register all of our action handlers
|
|
||||||
pub fn init(cx: &mut MutableAppContext) {
|
|
||||||
terminal_view::init(cx);
|
|
||||||
terminal_container_view::init(cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
///Scrolling is unbearably sluggish by default. Alacritty supports a configurable
|
///Scrolling is unbearably sluggish by default. Alacritty supports a configurable
|
||||||
///Scroll multiplier that is set to 3 by default. This will be removed when I
|
///Scroll multiplier that is set to 3 by default. This will be removed when I
|
||||||
///Implement scroll bars.
|
///Implement scroll bars.
|
||||||
|
@ -124,10 +116,10 @@ impl EventListener for ZedListener {
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct TerminalSize {
|
pub struct TerminalSize {
|
||||||
cell_width: f32,
|
pub cell_width: f32,
|
||||||
line_height: f32,
|
pub line_height: f32,
|
||||||
height: f32,
|
pub height: f32,
|
||||||
width: f32,
|
pub width: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TerminalSize {
|
impl TerminalSize {
|
||||||
|
@ -281,8 +273,6 @@ impl TerminalBuilder {
|
||||||
blink_settings: Option<TerminalBlink>,
|
blink_settings: Option<TerminalBlink>,
|
||||||
alternate_scroll: &AlternateScroll,
|
alternate_scroll: &AlternateScroll,
|
||||||
window_id: usize,
|
window_id: usize,
|
||||||
item_id: ItemId,
|
|
||||||
workspace_id: WorkspaceId,
|
|
||||||
) -> Result<TerminalBuilder> {
|
) -> Result<TerminalBuilder> {
|
||||||
let pty_config = {
|
let pty_config = {
|
||||||
let alac_shell = shell.clone().and_then(|shell| match shell {
|
let alac_shell = shell.clone().and_then(|shell| match shell {
|
||||||
|
@ -387,8 +377,6 @@ impl TerminalBuilder {
|
||||||
last_mouse_position: None,
|
last_mouse_position: None,
|
||||||
next_link_id: 0,
|
next_link_id: 0,
|
||||||
selection_phase: SelectionPhase::Ended,
|
selection_phase: SelectionPhase::Ended,
|
||||||
workspace_id,
|
|
||||||
item_id,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(TerminalBuilder {
|
Ok(TerminalBuilder {
|
||||||
|
@ -460,9 +448,9 @@ impl TerminalBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct IndexedCell {
|
pub struct IndexedCell {
|
||||||
point: Point,
|
pub point: Point,
|
||||||
cell: Cell,
|
pub cell: Cell,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for IndexedCell {
|
impl Deref for IndexedCell {
|
||||||
|
@ -474,17 +462,18 @@ impl Deref for IndexedCell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Un-pub
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TerminalContent {
|
pub struct TerminalContent {
|
||||||
cells: Vec<IndexedCell>,
|
pub cells: Vec<IndexedCell>,
|
||||||
mode: TermMode,
|
pub mode: TermMode,
|
||||||
display_offset: usize,
|
pub display_offset: usize,
|
||||||
selection_text: Option<String>,
|
pub selection_text: Option<String>,
|
||||||
selection: Option<SelectionRange>,
|
pub selection: Option<SelectionRange>,
|
||||||
cursor: RenderableCursor,
|
pub cursor: RenderableCursor,
|
||||||
cursor_char: char,
|
pub cursor_char: char,
|
||||||
size: TerminalSize,
|
pub size: TerminalSize,
|
||||||
last_hovered_hyperlink: Option<(String, RangeInclusive<Point>, usize)>,
|
pub last_hovered_hyperlink: Option<(String, RangeInclusive<Point>, usize)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TerminalContent {
|
impl Default for TerminalContent {
|
||||||
|
@ -521,19 +510,17 @@ pub struct Terminal {
|
||||||
/// This is only used for terminal hyperlink checking
|
/// This is only used for terminal hyperlink checking
|
||||||
last_mouse_position: Option<Vector2F>,
|
last_mouse_position: Option<Vector2F>,
|
||||||
pub matches: Vec<RangeInclusive<Point>>,
|
pub matches: Vec<RangeInclusive<Point>>,
|
||||||
last_content: TerminalContent,
|
pub last_content: TerminalContent,
|
||||||
last_synced: Instant,
|
last_synced: Instant,
|
||||||
sync_task: Option<Task<()>>,
|
sync_task: Option<Task<()>>,
|
||||||
selection_head: Option<Point>,
|
pub selection_head: Option<Point>,
|
||||||
breadcrumb_text: String,
|
pub breadcrumb_text: String,
|
||||||
shell_pid: u32,
|
shell_pid: u32,
|
||||||
shell_fd: u32,
|
shell_fd: u32,
|
||||||
foreground_process_info: Option<LocalProcessInfo>,
|
pub foreground_process_info: Option<LocalProcessInfo>,
|
||||||
scroll_px: f32,
|
scroll_px: f32,
|
||||||
next_link_id: usize,
|
next_link_id: usize,
|
||||||
selection_phase: SelectionPhase,
|
selection_phase: SelectionPhase,
|
||||||
workspace_id: WorkspaceId,
|
|
||||||
item_id: ItemId,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Terminal {
|
impl Terminal {
|
||||||
|
@ -574,20 +561,6 @@ impl Terminal {
|
||||||
|
|
||||||
if self.update_process_info() {
|
if self.update_process_info() {
|
||||||
cx.emit(Event::TitleChanged);
|
cx.emit(Event::TitleChanged);
|
||||||
|
|
||||||
if let Some(foreground_info) = &self.foreground_process_info {
|
|
||||||
let cwd = foreground_info.cwd.clone();
|
|
||||||
let item_id = self.item_id;
|
|
||||||
let workspace_id = self.workspace_id;
|
|
||||||
cx.background()
|
|
||||||
.spawn(async move {
|
|
||||||
TERMINAL_CONNECTION
|
|
||||||
.save_working_directory(item_id, workspace_id, cwd)
|
|
||||||
.await
|
|
||||||
.log_err();
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AlacTermEvent::ColorRequest(idx, fun_ptr) => {
|
AlacTermEvent::ColorRequest(idx, fun_ptr) => {
|
||||||
|
@ -1190,42 +1163,13 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_workspace_id(&mut self, id: WorkspaceId, cx: &AppContext) {
|
|
||||||
let old_workspace_id = self.workspace_id;
|
|
||||||
let item_id = self.item_id;
|
|
||||||
cx.background()
|
|
||||||
.spawn(async move {
|
|
||||||
TERMINAL_CONNECTION
|
|
||||||
.update_workspace_id(id, old_workspace_id, item_id)
|
|
||||||
.await
|
|
||||||
.log_err()
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
|
|
||||||
self.workspace_id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn find_matches(
|
pub fn find_matches(
|
||||||
&mut self,
|
&mut self,
|
||||||
query: project::search::SearchQuery,
|
searcher: RegexSearch,
|
||||||
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();
|
||||||
cx.background().spawn(async move {
|
cx.background().spawn(async move {
|
||||||
let searcher = match query {
|
|
||||||
project::search::SearchQuery::Text { query, .. } => {
|
|
||||||
RegexSearch::new(query.as_ref())
|
|
||||||
}
|
|
||||||
project::search::SearchQuery::Regex { query, .. } => {
|
|
||||||
RegexSearch::new(query.as_ref())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if searcher.is_err() {
|
|
||||||
return Vec::new();
|
|
||||||
}
|
|
||||||
let searcher = searcher.unwrap();
|
|
||||||
|
|
||||||
let term = term.lock();
|
let term = term.lock();
|
||||||
|
|
||||||
all_search_matches(&term, &searcher).collect()
|
all_search_matches(&term, &searcher).collect()
|
||||||
|
@ -1322,14 +1266,14 @@ fn open_uri(uri: &str) -> Result<(), std::io::Error> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use alacritty_terminal::{
|
||||||
|
index::{Column, Line, Point},
|
||||||
|
term::cell::Cell,
|
||||||
|
};
|
||||||
use gpui::geometry::vector::vec2f;
|
use gpui::geometry::vector::vec2f;
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{rngs::ThreadRng, thread_rng, Rng};
|
||||||
|
|
||||||
use crate::content_index_for_mouse;
|
use crate::{content_index_for_mouse, IndexedCell, TerminalContent, TerminalSize};
|
||||||
|
|
||||||
use self::terminal_test_context::TerminalTestContext;
|
|
||||||
|
|
||||||
pub mod terminal_test_context;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mouse_to_cell() {
|
fn test_mouse_to_cell() {
|
||||||
|
@ -1346,7 +1290,7 @@ mod tests {
|
||||||
width: cell_size * (viewport_cells as f32),
|
width: cell_size * (viewport_cells as f32),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (content, cells) = TerminalTestContext::create_terminal_content(size, &mut rng);
|
let (content, cells) = create_terminal_content(size, &mut rng);
|
||||||
|
|
||||||
for i in 0..(viewport_cells - 1) {
|
for i in 0..(viewport_cells - 1) {
|
||||||
let i = i as usize;
|
let i = i as usize;
|
||||||
|
@ -1382,7 +1326,7 @@ mod tests {
|
||||||
width: 100.,
|
width: 100.,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (content, cells) = TerminalTestContext::create_terminal_content(size, &mut rng);
|
let (content, cells) = create_terminal_content(size, &mut rng);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
content.cells[content_index_for_mouse(vec2f(-10., -10.), &content)].c,
|
content.cells[content_index_for_mouse(vec2f(-10., -10.), &content)].c,
|
||||||
|
@ -1393,4 +1337,37 @@ mod tests {
|
||||||
cells[9][9]
|
cells[9][9]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_terminal_content(
|
||||||
|
size: TerminalSize,
|
||||||
|
rng: &mut ThreadRng,
|
||||||
|
) -> (TerminalContent, Vec<Vec<char>>) {
|
||||||
|
let mut ic = Vec::new();
|
||||||
|
let mut cells = Vec::new();
|
||||||
|
|
||||||
|
for row in 0..((size.height() / size.line_height()) as usize) {
|
||||||
|
let mut row_vec = Vec::new();
|
||||||
|
for col in 0..((size.width() / size.cell_width()) as usize) {
|
||||||
|
let cell_char = rng.gen();
|
||||||
|
ic.push(IndexedCell {
|
||||||
|
point: Point::new(Line(row as i32), Column(col)),
|
||||||
|
cell: Cell {
|
||||||
|
c: cell_char,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
});
|
||||||
|
row_vec.push(cell_char)
|
||||||
|
}
|
||||||
|
cells.push(row_vec)
|
||||||
|
}
|
||||||
|
|
||||||
|
(
|
||||||
|
TerminalContent {
|
||||||
|
cells: ic,
|
||||||
|
size,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
cells,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,143 +0,0 @@
|
||||||
use std::{path::Path, time::Duration};
|
|
||||||
|
|
||||||
use alacritty_terminal::{
|
|
||||||
index::{Column, Line, Point},
|
|
||||||
term::cell::Cell,
|
|
||||||
};
|
|
||||||
use gpui::{ModelHandle, TestAppContext, ViewHandle};
|
|
||||||
|
|
||||||
use project::{Entry, Project, ProjectPath, Worktree};
|
|
||||||
use rand::{rngs::ThreadRng, Rng};
|
|
||||||
use workspace::{AppState, Workspace};
|
|
||||||
|
|
||||||
use crate::{IndexedCell, TerminalContent, TerminalSize};
|
|
||||||
|
|
||||||
pub struct TerminalTestContext<'a> {
|
|
||||||
pub cx: &'a mut TestAppContext,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TerminalTestContext<'a> {
|
|
||||||
pub fn new(cx: &'a mut TestAppContext) -> Self {
|
|
||||||
cx.set_condition_duration(Some(Duration::from_secs(5)));
|
|
||||||
|
|
||||||
TerminalTestContext { cx }
|
|
||||||
}
|
|
||||||
|
|
||||||
///Creates a worktree with 1 file: /root.txt
|
|
||||||
pub async fn blank_workspace(&mut self) -> (ModelHandle<Project>, ViewHandle<Workspace>) {
|
|
||||||
let params = self.cx.update(AppState::test);
|
|
||||||
|
|
||||||
let project = Project::test(params.fs.clone(), [], self.cx).await;
|
|
||||||
let (_, workspace) = self.cx.add_window(|cx| {
|
|
||||||
Workspace::new(
|
|
||||||
Default::default(),
|
|
||||||
0,
|
|
||||||
project.clone(),
|
|
||||||
|_, _| unimplemented!(),
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
(project, workspace)
|
|
||||||
}
|
|
||||||
|
|
||||||
///Creates a worktree with 1 folder: /root{suffix}/
|
|
||||||
pub async fn create_folder_wt(
|
|
||||||
&mut self,
|
|
||||||
project: ModelHandle<Project>,
|
|
||||||
path: impl AsRef<Path>,
|
|
||||||
) -> (ModelHandle<Worktree>, Entry) {
|
|
||||||
self.create_wt(project, true, path).await
|
|
||||||
}
|
|
||||||
|
|
||||||
///Creates a worktree with 1 file: /root{suffix}.txt
|
|
||||||
pub async fn create_file_wt(
|
|
||||||
&mut self,
|
|
||||||
project: ModelHandle<Project>,
|
|
||||||
path: impl AsRef<Path>,
|
|
||||||
) -> (ModelHandle<Worktree>, Entry) {
|
|
||||||
self.create_wt(project, false, path).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create_wt(
|
|
||||||
&mut self,
|
|
||||||
project: ModelHandle<Project>,
|
|
||||||
is_dir: bool,
|
|
||||||
path: impl AsRef<Path>,
|
|
||||||
) -> (ModelHandle<Worktree>, Entry) {
|
|
||||||
let (wt, _) = project
|
|
||||||
.update(self.cx, |project, cx| {
|
|
||||||
project.find_or_create_local_worktree(path, true, cx)
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let entry = self
|
|
||||||
.cx
|
|
||||||
.update(|cx| {
|
|
||||||
wt.update(cx, |wt, cx| {
|
|
||||||
wt.as_local()
|
|
||||||
.unwrap()
|
|
||||||
.create_entry(Path::new(""), is_dir, cx)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
(wt, entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_active_entry_for(
|
|
||||||
&mut self,
|
|
||||||
wt: ModelHandle<Worktree>,
|
|
||||||
entry: Entry,
|
|
||||||
project: ModelHandle<Project>,
|
|
||||||
) {
|
|
||||||
self.cx.update(|cx| {
|
|
||||||
let p = ProjectPath {
|
|
||||||
worktree_id: wt.read(cx).id(),
|
|
||||||
path: entry.path,
|
|
||||||
};
|
|
||||||
project.update(cx, |project, cx| project.set_active_path(Some(p), cx));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_terminal_content(
|
|
||||||
size: TerminalSize,
|
|
||||||
rng: &mut ThreadRng,
|
|
||||||
) -> (TerminalContent, Vec<Vec<char>>) {
|
|
||||||
let mut ic = Vec::new();
|
|
||||||
let mut cells = Vec::new();
|
|
||||||
|
|
||||||
for row in 0..((size.height() / size.line_height()) as usize) {
|
|
||||||
let mut row_vec = Vec::new();
|
|
||||||
for col in 0..((size.width() / size.cell_width()) as usize) {
|
|
||||||
let cell_char = rng.gen();
|
|
||||||
ic.push(IndexedCell {
|
|
||||||
point: Point::new(Line(row as i32), Column(col)),
|
|
||||||
cell: Cell {
|
|
||||||
c: cell_char,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
});
|
|
||||||
row_vec.push(cell_char)
|
|
||||||
}
|
|
||||||
cells.push(row_vec)
|
|
||||||
}
|
|
||||||
|
|
||||||
(
|
|
||||||
TerminalContent {
|
|
||||||
cells: ic,
|
|
||||||
size,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
cells,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Drop for TerminalTestContext<'a> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.cx.set_condition_duration(None);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -18,8 +18,8 @@ theme = { path = "../theme" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
db = { path = "../db" }
|
db = { path = "../db" }
|
||||||
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty", rev = "a51dbe25d67e84d6ed4261e640d3954fbdd9be45" }
|
|
||||||
procinfo = { git = "https://github.com/zed-industries/wezterm", rev = "5cd757e5f2eb039ed0c6bb6512223e69d5efc64d", default-features = false }
|
procinfo = { git = "https://github.com/zed-industries/wezterm", rev = "5cd757e5f2eb039ed0c6bb6512223e69d5efc64d", default-features = false }
|
||||||
|
terminal = { path = "../terminal" }
|
||||||
smallvec = { version = "1.6", features = ["union"] }
|
smallvec = { version = "1.6", features = ["union"] }
|
||||||
smol = "1.2.5"
|
smol = "1.2.5"
|
||||||
mio-extras = "2.0.6"
|
mio-extras = "2.0.6"
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use db::{define_connection, query, sqlez_macros::sql};
|
use db::{define_connection, query, sqlez_macros::sql};
|
||||||
|
use workspace::{WorkspaceDb, WorkspaceId};
|
||||||
|
|
||||||
type ModelId = usize;
|
type ModelId = usize;
|
||||||
|
|
||||||
define_connection! {
|
define_connection! {
|
||||||
pub static ref TERMINAL_CONNECTION: TerminalDb<()> =
|
pub static ref TERMINAL_DB: TerminalDb<WorkspaceDb> =
|
||||||
&[sql!(
|
&[sql!(
|
||||||
CREATE TABLE terminals (
|
CREATE TABLE terminals (
|
||||||
workspace_id INTEGER,
|
workspace_id INTEGER,
|
||||||
|
@ -34,7 +35,7 @@ impl TerminalDb {
|
||||||
query! {
|
query! {
|
||||||
pub async fn save_working_directory(
|
pub async fn save_working_directory(
|
||||||
item_id: ModelId,
|
item_id: ModelId,
|
||||||
workspace_id: WorkspaceId,
|
workspace_id: i64,
|
||||||
working_directory: PathBuf
|
working_directory: PathBuf
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
INSERT OR REPLACE INTO terminals(item_id, workspace_id, working_directory)
|
INSERT OR REPLACE INTO terminals(item_id, workspace_id, working_directory)
|
|
@ -1,13 +1,18 @@
|
||||||
use crate::persistence::TERMINAL_CONNECTION;
|
mod persistence;
|
||||||
use crate::terminal_view::TerminalView;
|
pub mod terminal_element;
|
||||||
use crate::{Event, TerminalBuilder, TerminalError};
|
pub mod terminal_view;
|
||||||
|
|
||||||
|
use crate::persistence::TERMINAL_DB;
|
||||||
|
use crate::terminal_view::TerminalView;
|
||||||
|
use terminal::alacritty_terminal::index::Point;
|
||||||
|
use terminal::{Event, TerminalBuilder, TerminalError};
|
||||||
|
|
||||||
use alacritty_terminal::index::Point;
|
|
||||||
use dirs::home_dir;
|
use dirs::home_dir;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, MutableAppContext, Task,
|
actions, elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, MutableAppContext, Task,
|
||||||
View, ViewContext, ViewHandle, WeakViewHandle,
|
View, ViewContext, ViewHandle, WeakViewHandle,
|
||||||
};
|
};
|
||||||
|
use terminal_view::regex_search_for_query;
|
||||||
use util::{truncate_and_trailoff, ResultExt};
|
use util::{truncate_and_trailoff, ResultExt};
|
||||||
use workspace::searchable::{SearchEvent, SearchOptions, SearchableItem, SearchableItemHandle};
|
use workspace::searchable::{SearchEvent, SearchOptions, SearchableItem, SearchableItemHandle};
|
||||||
use workspace::{
|
use workspace::{
|
||||||
|
@ -30,6 +35,8 @@ pub fn init(cx: &mut MutableAppContext) {
|
||||||
cx.add_action(TerminalContainer::deploy);
|
cx.add_action(TerminalContainer::deploy);
|
||||||
|
|
||||||
register_deserializable_item::<TerminalContainer>(cx);
|
register_deserializable_item::<TerminalContainer>(cx);
|
||||||
|
|
||||||
|
terminal_view::init(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Make terminal view an enum, that can give you views for the error and non-error states
|
//Make terminal view an enum, that can give you views for the error and non-error states
|
||||||
|
@ -92,7 +99,7 @@ impl TerminalContainer {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
working_directory: Option<PathBuf>,
|
working_directory: Option<PathBuf>,
|
||||||
modal: bool,
|
modal: bool,
|
||||||
workspace_id: WorkspaceId,
|
_workspace_id: WorkspaceId,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let settings = cx.global::<Settings>();
|
let settings = cx.global::<Settings>();
|
||||||
|
@ -119,8 +126,6 @@ impl TerminalContainer {
|
||||||
settings.terminal_overrides.blinking.clone(),
|
settings.terminal_overrides.blinking.clone(),
|
||||||
scroll,
|
scroll,
|
||||||
cx.window_id(),
|
cx.window_id(),
|
||||||
cx.view_id(),
|
|
||||||
workspace_id,
|
|
||||||
) {
|
) {
|
||||||
Ok(terminal) => {
|
Ok(terminal) => {
|
||||||
let terminal = cx.add_model(|cx| terminal.subscribe(cx));
|
let terminal = cx.add_model(|cx| terminal.subscribe(cx));
|
||||||
|
@ -389,7 +394,7 @@ impl Item for TerminalContainer {
|
||||||
item_id: workspace::ItemId,
|
item_id: workspace::ItemId,
|
||||||
cx: &mut ViewContext<Pane>,
|
cx: &mut ViewContext<Pane>,
|
||||||
) -> Task<anyhow::Result<ViewHandle<Self>>> {
|
) -> Task<anyhow::Result<ViewHandle<Self>>> {
|
||||||
let working_directory = TERMINAL_CONNECTION.get_working_directory(item_id, workspace_id);
|
let working_directory = TERMINAL_DB.get_working_directory(item_id, workspace_id);
|
||||||
Task::ready(Ok(cx.add_view(|cx| {
|
Task::ready(Ok(cx.add_view(|cx| {
|
||||||
TerminalContainer::new(
|
TerminalContainer::new(
|
||||||
working_directory.log_err().flatten(),
|
working_directory.log_err().flatten(),
|
||||||
|
@ -400,11 +405,14 @@ impl Item for TerminalContainer {
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext<Self>) {
|
fn added_to_workspace(&mut self, _workspace: &mut Workspace, cx: &mut ViewContext<Self>) {
|
||||||
if let Some(connected) = self.connected() {
|
if let Some(_connected) = self.connected() {
|
||||||
let id = workspace.database_id();
|
// let id = workspace.database_id();
|
||||||
let terminal_handle = connected.read(cx).terminal().clone();
|
// let terminal_handle = connected.read(cx).terminal().clone();
|
||||||
terminal_handle.update(cx, |terminal, cx| terminal.set_workspace_id(id, cx))
|
//TODO
|
||||||
|
cx.background()
|
||||||
|
.spawn(TERMINAL_DB.update_workspace_id(0, 0, 0))
|
||||||
|
.detach();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -477,7 +485,11 @@ impl SearchableItem for TerminalContainer {
|
||||||
) -> Task<Vec<Self::Match>> {
|
) -> Task<Vec<Self::Match>> {
|
||||||
if let TerminalContainerContent::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))
|
if let Some(searcher) = regex_search_for_query(query) {
|
||||||
|
terminal.update(cx, |term, cx| term.find_matches(searcher, cx))
|
||||||
|
} else {
|
||||||
|
cx.background().spawn(async { Vec::new() })
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Task::ready(Vec::new())
|
Task::ready(Vec::new())
|
||||||
}
|
}
|
||||||
|
@ -585,21 +597,20 @@ mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use gpui::TestAppContext;
|
use gpui::TestAppContext;
|
||||||
|
use project::{Entry, Worktree};
|
||||||
|
use workspace::AppState;
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::tests::terminal_test_context::TerminalTestContext;
|
|
||||||
|
|
||||||
///Working directory calculation tests
|
///Working directory calculation tests
|
||||||
|
|
||||||
///No Worktrees in project -> home_dir()
|
///No Worktrees in project -> home_dir()
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn no_worktree(cx: &mut TestAppContext) {
|
async fn no_worktree(cx: &mut TestAppContext) {
|
||||||
//Setup variables
|
//Setup variables
|
||||||
let mut cx = TerminalTestContext::new(cx);
|
let (project, workspace) = blank_workspace(cx).await;
|
||||||
let (project, workspace) = cx.blank_workspace().await;
|
|
||||||
//Test
|
//Test
|
||||||
cx.cx.read(|cx| {
|
cx.read(|cx| {
|
||||||
let workspace = workspace.read(cx);
|
let workspace = workspace.read(cx);
|
||||||
let active_entry = project.read(cx).active_entry();
|
let active_entry = project.read(cx).active_entry();
|
||||||
|
|
||||||
|
@ -619,11 +630,10 @@ mod tests {
|
||||||
async fn no_active_entry_worktree_is_file(cx: &mut TestAppContext) {
|
async fn no_active_entry_worktree_is_file(cx: &mut TestAppContext) {
|
||||||
//Setup variables
|
//Setup variables
|
||||||
|
|
||||||
let mut cx = TerminalTestContext::new(cx);
|
let (project, workspace) = blank_workspace(cx).await;
|
||||||
let (project, workspace) = cx.blank_workspace().await;
|
create_file_wt(project.clone(), "/root.txt", cx).await;
|
||||||
cx.create_file_wt(project.clone(), "/root.txt").await;
|
|
||||||
|
|
||||||
cx.cx.read(|cx| {
|
cx.read(|cx| {
|
||||||
let workspace = workspace.read(cx);
|
let workspace = workspace.read(cx);
|
||||||
let active_entry = project.read(cx).active_entry();
|
let active_entry = project.read(cx).active_entry();
|
||||||
|
|
||||||
|
@ -642,12 +652,11 @@ mod tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn no_active_entry_worktree_is_dir(cx: &mut TestAppContext) {
|
async fn no_active_entry_worktree_is_dir(cx: &mut TestAppContext) {
|
||||||
//Setup variables
|
//Setup variables
|
||||||
let mut cx = TerminalTestContext::new(cx);
|
let (project, workspace) = blank_workspace(cx).await;
|
||||||
let (project, workspace) = cx.blank_workspace().await;
|
let (_wt, _entry) = create_folder_wt(project.clone(), "/root/", cx).await;
|
||||||
let (_wt, _entry) = cx.create_folder_wt(project.clone(), "/root/").await;
|
|
||||||
|
|
||||||
//Test
|
//Test
|
||||||
cx.cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
let workspace = workspace.read(cx);
|
let workspace = workspace.read(cx);
|
||||||
let active_entry = project.read(cx).active_entry();
|
let active_entry = project.read(cx).active_entry();
|
||||||
|
|
||||||
|
@ -665,14 +674,14 @@ mod tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn active_entry_worktree_is_file(cx: &mut TestAppContext) {
|
async fn active_entry_worktree_is_file(cx: &mut TestAppContext) {
|
||||||
//Setup variables
|
//Setup variables
|
||||||
let mut cx = TerminalTestContext::new(cx);
|
|
||||||
let (project, workspace) = cx.blank_workspace().await;
|
let (project, workspace) = blank_workspace(cx).await;
|
||||||
let (_wt, _entry) = cx.create_folder_wt(project.clone(), "/root1/").await;
|
let (_wt, _entry) = create_folder_wt(project.clone(), "/root1/", cx).await;
|
||||||
let (wt2, entry2) = cx.create_file_wt(project.clone(), "/root2.txt").await;
|
let (wt2, entry2) = create_file_wt(project.clone(), "/root2.txt", cx).await;
|
||||||
cx.insert_active_entry_for(wt2, entry2, project.clone());
|
insert_active_entry_for(wt2, entry2, project.clone(), cx);
|
||||||
|
|
||||||
//Test
|
//Test
|
||||||
cx.cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
let workspace = workspace.read(cx);
|
let workspace = workspace.read(cx);
|
||||||
let active_entry = project.read(cx).active_entry();
|
let active_entry = project.read(cx).active_entry();
|
||||||
|
|
||||||
|
@ -689,14 +698,13 @@ mod tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn active_entry_worktree_is_dir(cx: &mut TestAppContext) {
|
async fn active_entry_worktree_is_dir(cx: &mut TestAppContext) {
|
||||||
//Setup variables
|
//Setup variables
|
||||||
let mut cx = TerminalTestContext::new(cx);
|
let (project, workspace) = blank_workspace(cx).await;
|
||||||
let (project, workspace) = cx.blank_workspace().await;
|
let (_wt, _entry) = create_folder_wt(project.clone(), "/root1/", cx).await;
|
||||||
let (_wt, _entry) = cx.create_folder_wt(project.clone(), "/root1/").await;
|
let (wt2, entry2) = create_folder_wt(project.clone(), "/root2/", cx).await;
|
||||||
let (wt2, entry2) = cx.create_folder_wt(project.clone(), "/root2/").await;
|
insert_active_entry_for(wt2, entry2, project.clone(), cx);
|
||||||
cx.insert_active_entry_for(wt2, entry2, project.clone());
|
|
||||||
|
|
||||||
//Test
|
//Test
|
||||||
cx.cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
let workspace = workspace.read(cx);
|
let workspace = workspace.read(cx);
|
||||||
let active_entry = project.read(cx).active_entry();
|
let active_entry = project.read(cx).active_entry();
|
||||||
|
|
||||||
|
@ -708,4 +716,84 @@ mod tests {
|
||||||
assert_eq!(res, Some((Path::new("/root1/")).to_path_buf()));
|
assert_eq!(res, Some((Path::new("/root1/")).to_path_buf()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Creates a worktree with 1 file: /root.txt
|
||||||
|
pub async fn blank_workspace(
|
||||||
|
cx: &mut TestAppContext,
|
||||||
|
) -> (ModelHandle<Project>, ViewHandle<Workspace>) {
|
||||||
|
let params = cx.update(AppState::test);
|
||||||
|
|
||||||
|
let project = Project::test(params.fs.clone(), [], cx).await;
|
||||||
|
let (_, workspace) = cx.add_window(|cx| {
|
||||||
|
Workspace::new(
|
||||||
|
Default::default(),
|
||||||
|
0,
|
||||||
|
project.clone(),
|
||||||
|
|_, _| unimplemented!(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
(project, workspace)
|
||||||
|
}
|
||||||
|
|
||||||
|
///Creates a worktree with 1 folder: /root{suffix}/
|
||||||
|
async fn create_folder_wt(
|
||||||
|
project: ModelHandle<Project>,
|
||||||
|
path: impl AsRef<Path>,
|
||||||
|
cx: &mut TestAppContext,
|
||||||
|
) -> (ModelHandle<Worktree>, Entry) {
|
||||||
|
create_wt(project, true, path, cx).await
|
||||||
|
}
|
||||||
|
|
||||||
|
///Creates a worktree with 1 file: /root{suffix}.txt
|
||||||
|
async fn create_file_wt(
|
||||||
|
project: ModelHandle<Project>,
|
||||||
|
path: impl AsRef<Path>,
|
||||||
|
cx: &mut TestAppContext,
|
||||||
|
) -> (ModelHandle<Worktree>, Entry) {
|
||||||
|
create_wt(project, false, path, cx).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_wt(
|
||||||
|
project: ModelHandle<Project>,
|
||||||
|
is_dir: bool,
|
||||||
|
path: impl AsRef<Path>,
|
||||||
|
cx: &mut TestAppContext,
|
||||||
|
) -> (ModelHandle<Worktree>, Entry) {
|
||||||
|
let (wt, _) = project
|
||||||
|
.update(cx, |project, cx| {
|
||||||
|
project.find_or_create_local_worktree(path, true, cx)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let entry = cx
|
||||||
|
.update(|cx| {
|
||||||
|
wt.update(cx, |wt, cx| {
|
||||||
|
wt.as_local()
|
||||||
|
.unwrap()
|
||||||
|
.create_entry(Path::new(""), is_dir, cx)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
(wt, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_active_entry_for(
|
||||||
|
wt: ModelHandle<Worktree>,
|
||||||
|
entry: Entry,
|
||||||
|
project: ModelHandle<Project>,
|
||||||
|
cx: &mut TestAppContext,
|
||||||
|
) {
|
||||||
|
cx.update(|cx| {
|
||||||
|
let p = ProjectPath {
|
||||||
|
worktree_id: wt.read(cx).id(),
|
||||||
|
path: entry.path,
|
||||||
|
};
|
||||||
|
project.update(cx, |project, cx| project.set_active_path(Some(p), cx));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,3 @@
|
||||||
use alacritty_terminal::{
|
|
||||||
ansi::{Color as AnsiColor, Color::Named, CursorShape as AlacCursorShape, NamedColor},
|
|
||||||
grid::Dimensions,
|
|
||||||
index::Point,
|
|
||||||
term::{cell::Flags, TermMode},
|
|
||||||
};
|
|
||||||
use editor::{Cursor, HighlightedRange, HighlightedRangeLine};
|
use editor::{Cursor, HighlightedRange, HighlightedRangeLine};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
color::Color,
|
color::Color,
|
||||||
|
@ -22,17 +16,23 @@ use itertools::Itertools;
|
||||||
use language::CursorShape;
|
use language::CursorShape;
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
|
use terminal::{
|
||||||
|
alacritty_terminal::{
|
||||||
|
ansi::{Color as AnsiColor, CursorShape as AlacCursorShape, NamedColor},
|
||||||
|
grid::Dimensions,
|
||||||
|
index::Point,
|
||||||
|
term::{cell::Flags, TermMode},
|
||||||
|
},
|
||||||
|
mappings::colors::convert_color,
|
||||||
|
IndexedCell, Terminal, TerminalContent, TerminalSize,
|
||||||
|
};
|
||||||
use theme::TerminalStyle;
|
use theme::TerminalStyle;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
use std::{fmt::Debug, ops::RangeInclusive};
|
use std::{fmt::Debug, ops::RangeInclusive};
|
||||||
use std::{mem, ops::Range};
|
use std::{mem, ops::Range};
|
||||||
|
|
||||||
use crate::{
|
use crate::terminal_view::{DeployContextMenu, TerminalView};
|
||||||
mappings::colors::convert_color,
|
|
||||||
terminal_view::{DeployContextMenu, TerminalView},
|
|
||||||
IndexedCell, Terminal, TerminalContent, TerminalSize,
|
|
||||||
};
|
|
||||||
|
|
||||||
///The information generated during layout that is nescessary for painting
|
///The information generated during layout that is nescessary for painting
|
||||||
pub struct LayoutState {
|
pub struct LayoutState {
|
||||||
|
@ -198,7 +198,10 @@ impl TerminalElement {
|
||||||
|
|
||||||
//Expand background rect range
|
//Expand background rect range
|
||||||
{
|
{
|
||||||
if matches!(bg, Named(NamedColor::Background)) {
|
if matches!(
|
||||||
|
bg,
|
||||||
|
terminal::alacritty_terminal::ansi::Color::Named(NamedColor::Background)
|
||||||
|
) {
|
||||||
//Continue to next cell, resetting variables if nescessary
|
//Continue to next cell, resetting variables if nescessary
|
||||||
cur_alac_color = None;
|
cur_alac_color = None;
|
||||||
if let Some(rect) = cur_rect {
|
if let Some(rect) = cur_rect {
|
||||||
|
@ -299,7 +302,7 @@ impl TerminalElement {
|
||||||
///Convert the Alacritty cell styles to GPUI text styles and background color
|
///Convert the Alacritty cell styles to GPUI text styles and background color
|
||||||
fn cell_style(
|
fn cell_style(
|
||||||
indexed: &IndexedCell,
|
indexed: &IndexedCell,
|
||||||
fg: AnsiColor,
|
fg: terminal::alacritty_terminal::ansi::Color,
|
||||||
style: &TerminalStyle,
|
style: &TerminalStyle,
|
||||||
text_style: &TextStyle,
|
text_style: &TextStyle,
|
||||||
font_cache: &FontCache,
|
font_cache: &FontCache,
|
||||||
|
@ -636,7 +639,7 @@ impl Element for TerminalElement {
|
||||||
|
|
||||||
//Layout cursor. Rectangle is used for IME, so we should lay it out even
|
//Layout cursor. Rectangle is used for IME, so we should lay it out even
|
||||||
//if we don't end up showing it.
|
//if we don't end up showing it.
|
||||||
let cursor = if let AlacCursorShape::Hidden = cursor.shape {
|
let cursor = if let terminal::alacritty_terminal::ansi::CursorShape::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);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use std::{ops::RangeInclusive, time::Duration};
|
use std::{ops::RangeInclusive, path::PathBuf, time::Duration};
|
||||||
|
|
||||||
use alacritty_terminal::{index::Point, term::TermMode};
|
|
||||||
use context_menu::{ContextMenu, ContextMenuItem};
|
use context_menu::{ContextMenu, ContextMenuItem};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions,
|
actions,
|
||||||
|
@ -14,10 +13,17 @@ use gpui::{
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use settings::{Settings, TerminalBlink};
|
use settings::{Settings, TerminalBlink};
|
||||||
use smol::Timer;
|
use smol::Timer;
|
||||||
|
use terminal::{
|
||||||
|
alacritty_terminal::{
|
||||||
|
index::Point,
|
||||||
|
term::{search::RegexSearch, TermMode},
|
||||||
|
},
|
||||||
|
Terminal,
|
||||||
|
};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::pane;
|
use workspace::pane;
|
||||||
|
|
||||||
use crate::{terminal_element::TerminalElement, Event, Terminal};
|
use crate::{persistence::TERMINAL_DB, terminal_element::TerminalElement, Event};
|
||||||
|
|
||||||
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
|
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
|
||||||
|
|
||||||
|
@ -95,6 +101,22 @@ impl TerminalView {
|
||||||
cx.emit(Event::Wakeup);
|
cx.emit(Event::Wakeup);
|
||||||
}
|
}
|
||||||
Event::BlinkChanged => this.blinking_on = !this.blinking_on,
|
Event::BlinkChanged => this.blinking_on = !this.blinking_on,
|
||||||
|
Event::TitleChanged => {
|
||||||
|
// if let Some(foreground_info) = &terminal.read(cx).foreground_process_info {
|
||||||
|
// let cwd = foreground_info.cwd.clone();
|
||||||
|
//TODO
|
||||||
|
// let item_id = self.item_id;
|
||||||
|
// let workspace_id = self.workspace_id;
|
||||||
|
cx.background()
|
||||||
|
.spawn(async move {
|
||||||
|
TERMINAL_DB
|
||||||
|
.save_working_directory(0, 0, PathBuf::new())
|
||||||
|
.await
|
||||||
|
.log_err();
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
// }
|
||||||
|
}
|
||||||
_ => cx.emit(*event),
|
_ => cx.emit(*event),
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
@ -246,8 +268,14 @@ impl TerminalView {
|
||||||
query: project::search::SearchQuery,
|
query: project::search::SearchQuery,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Task<Vec<RangeInclusive<Point>>> {
|
) -> Task<Vec<RangeInclusive<Point>>> {
|
||||||
self.terminal
|
let searcher = regex_search_for_query(query);
|
||||||
.update(cx, |term, cx| term.find_matches(query, cx))
|
|
||||||
|
if let Some(searcher) = searcher {
|
||||||
|
self.terminal
|
||||||
|
.update(cx, |term, cx| term.find_matches(searcher, cx))
|
||||||
|
} else {
|
||||||
|
cx.background().spawn(async { Vec::new() })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn terminal(&self) -> &ModelHandle<Terminal> {
|
pub fn terminal(&self) -> &ModelHandle<Terminal> {
|
||||||
|
@ -302,6 +330,14 @@ impl TerminalView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn regex_search_for_query(query: project::search::SearchQuery) -> Option<RegexSearch> {
|
||||||
|
let searcher = match query {
|
||||||
|
project::search::SearchQuery::Text { query, .. } => RegexSearch::new(&query),
|
||||||
|
project::search::SearchQuery::Regex { query, .. } => RegexSearch::new(&query),
|
||||||
|
};
|
||||||
|
searcher.ok()
|
||||||
|
}
|
||||||
|
|
||||||
impl View for TerminalView {
|
impl View for TerminalView {
|
||||||
fn ui_name() -> &'static str {
|
fn ui_name() -> &'static str {
|
||||||
"Terminal"
|
"Terminal"
|
||||||
|
|
|
@ -32,7 +32,7 @@ use settings::{
|
||||||
use smol::process::Command;
|
use smol::process::Command;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::{env, ffi::OsStr, panic, path::PathBuf, sync::Arc, thread, time::Duration};
|
use std::{env, ffi::OsStr, panic, path::PathBuf, sync::Arc, thread, time::Duration};
|
||||||
use terminal::terminal_container_view::{get_working_directory, TerminalContainer};
|
use terminal_view::{get_working_directory, TerminalContainer};
|
||||||
|
|
||||||
use fs::RealFs;
|
use fs::RealFs;
|
||||||
use settings::watched_json::{watch_keymap_file, watch_settings_file, WatchedJsonFile};
|
use settings::watched_json::{watch_keymap_file, watch_settings_file, WatchedJsonFile};
|
||||||
|
@ -119,7 +119,7 @@ fn main() {
|
||||||
diagnostics::init(cx);
|
diagnostics::init(cx);
|
||||||
search::init(cx);
|
search::init(cx);
|
||||||
vim::init(cx);
|
vim::init(cx);
|
||||||
terminal::init(cx);
|
terminal_view::init(cx);
|
||||||
theme_testbench::init(cx);
|
theme_testbench::init(cx);
|
||||||
|
|
||||||
cx.spawn(|cx| watch_themes(fs.clone(), themes.clone(), cx))
|
cx.spawn(|cx| watch_themes(fs.clone(), themes.clone(), cx))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue