From 83aefffa38a630651104cce729efef77737cebab Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 8 Dec 2022 10:48:28 -0800 Subject: [PATCH] Rearrange the terminal code to not have a cyclic dependency with the project --- Cargo.lock | 4 +- crates/editor/src/editor.rs | 2 +- crates/project/src/project.rs | 8 +- crates/terminal/Cargo.toml | 6 +- crates/terminal/src/terminal.rs | 153 +++++++--------- .../src/tests/terminal_test_context.rs | 143 --------------- crates/terminal_view/Cargo.toml | 2 +- .../src/persistence.rs | 5 +- .../src/terminal_container_view.rs | 166 ++++++++++++++---- crates/terminal_view/src/terminal_element.rs | 31 ++-- crates/terminal_view/src/terminal_view.rs | 46 ++++- crates/zed/src/main.rs | 4 +- 12 files changed, 270 insertions(+), 300 deletions(-) delete mode 100644 crates/terminal/src/tests/terminal_test_context.rs rename crates/{terminal => terminal_view}/src/persistence.rs (91%) diff --git a/Cargo.lock b/Cargo.lock index 1868959a09..ddd7a0f7fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6271,6 +6271,7 @@ dependencies = [ "mio-extras", "ordered-float", "procinfo", + "rand 0.8.5", "serde", "settings", "shellexpand", @@ -6278,13 +6279,13 @@ dependencies = [ "smol", "theme", "thiserror", + "util", ] [[package]] name = "terminal_view" version = "0.1.0" dependencies = [ - "alacritty_terminal", "anyhow", "client", "context_menu", @@ -6307,6 +6308,7 @@ dependencies = [ "shellexpand", "smallvec", "smol", + "terminal", "theme", "thiserror", "util", diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 417b60bc5b..ad21622fd9 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2422,7 +2422,7 @@ impl Editor { let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| { let excerpt_range = excerpt_range.to_offset(buffer); buffer - .edited_ranges_for_transaction(transaction) + .edited_ranges_for_transaction::(transaction) .all(|range| { excerpt_range.start <= range.start && excerpt_range.end >= range.end diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index e61f0fe0b7..40f1c93e51 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -60,9 +60,9 @@ use std::{ atomic::{AtomicUsize, Ordering::SeqCst}, Arc, }, - thread::panicking, time::Instant, }; +use terminal::Terminal; use thiserror::Error; use util::{defer, post_inc, ResultExt, TryFutureExt as _}; @@ -1196,12 +1196,14 @@ impl Project { pub fn create_terminal_connection( &mut self, - cx: &mut ModelContext, - ) -> Result> { + _cx: &mut ModelContext, + ) -> Result> { if self.is_remote() { return Err(anyhow!( "creating terminals as a guest is not supported yet" )); + } else { + unimplemented!() } } diff --git a/crates/terminal/Cargo.toml b/crates/terminal/Cargo.toml index 2948eaec69..0dea7bfbcf 100644 --- a/crates/terminal/Cargo.toml +++ b/crates/terminal/Cargo.toml @@ -13,6 +13,7 @@ gpui = { path = "../gpui" } settings = { path = "../settings" } db = { path = "../db" } theme = { path = "../theme" } +util = { path = "../util" } alacritty_terminal = { git = "https://github.com/zed-industries/alacritty", rev = "a51dbe25d67e84d6ed4261e640d3954fbdd9be45" } procinfo = { git = "https://github.com/zed-industries/wezterm", rev = "5cd757e5f2eb039ed0c6bb6512223e69d5efc64d", default-features = false } smallvec = { version = "1.6", features = ["union"] } @@ -27,4 +28,7 @@ libc = "0.2" anyhow = "1" thiserror = "1.0" lazy_static = "1.4.0" -serde = { version = "1.0", features = ["derive"] } \ No newline at end of file +serde = { version = "1.0", features = ["derive"] } + +[dev-dependencies] +rand = "0.8.5" diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 62df8aca82..937678df0b 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -1,5 +1,5 @@ pub mod mappings; -mod persistence; +pub use alacritty_terminal; use alacritty_terminal::{ ansi::{ClearMode, Handler}, @@ -30,7 +30,6 @@ use mappings::mouse::{ alt_scroll, grid_point, mouse_button_report, mouse_moved_report, mouse_side, scroll_report, }; -use persistence::TERMINAL_CONNECTION; use procinfo::LocalProcessInfo; use settings::{AlternateScroll, Settings, Shell, TerminalBlink}; use util::ResultExt; @@ -53,8 +52,7 @@ use gpui::{ geometry::vector::{vec2f, Vector2F}, keymap::Keystroke, scene::{MouseDown, MouseDrag, MouseScrollWheel, MouseUp}, - AppContext, ClipboardItem, Entity, ModelContext, MouseButton, MouseMovedEvent, - MutableAppContext, Task, + ClipboardItem, Entity, ModelContext, MouseButton, MouseMovedEvent, Task, }; use crate::mappings::{ @@ -63,12 +61,6 @@ use crate::mappings::{ }; 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 ///Scroll multiplier that is set to 3 by default. This will be removed when I ///Implement scroll bars. @@ -124,10 +116,10 @@ impl EventListener for ZedListener { #[derive(Clone, Copy, Debug)] pub struct TerminalSize { - cell_width: f32, - line_height: f32, - height: f32, - width: f32, + pub cell_width: f32, + pub line_height: f32, + pub height: f32, + pub width: f32, } impl TerminalSize { @@ -281,8 +273,6 @@ impl TerminalBuilder { blink_settings: Option, alternate_scroll: &AlternateScroll, window_id: usize, - item_id: ItemId, - workspace_id: WorkspaceId, ) -> Result { let pty_config = { let alac_shell = shell.clone().and_then(|shell| match shell { @@ -387,8 +377,6 @@ impl TerminalBuilder { last_mouse_position: None, next_link_id: 0, selection_phase: SelectionPhase::Ended, - workspace_id, - item_id, }; Ok(TerminalBuilder { @@ -460,9 +448,9 @@ impl TerminalBuilder { } #[derive(Debug, Clone)] -struct IndexedCell { - point: Point, - cell: Cell, +pub struct IndexedCell { + pub point: Point, + pub cell: Cell, } impl Deref for IndexedCell { @@ -474,17 +462,18 @@ impl Deref for IndexedCell { } } +// TODO: Un-pub #[derive(Clone)] pub struct TerminalContent { - cells: Vec, - mode: TermMode, - display_offset: usize, - selection_text: Option, - selection: Option, - cursor: RenderableCursor, - cursor_char: char, - size: TerminalSize, - last_hovered_hyperlink: Option<(String, RangeInclusive, usize)>, + pub cells: Vec, + pub mode: TermMode, + pub display_offset: usize, + pub selection_text: Option, + pub selection: Option, + pub cursor: RenderableCursor, + pub cursor_char: char, + pub size: TerminalSize, + pub last_hovered_hyperlink: Option<(String, RangeInclusive, usize)>, } impl Default for TerminalContent { @@ -521,19 +510,17 @@ pub struct Terminal { /// This is only used for terminal hyperlink checking last_mouse_position: Option, pub matches: Vec>, - last_content: TerminalContent, + pub last_content: TerminalContent, last_synced: Instant, sync_task: Option>, - selection_head: Option, - breadcrumb_text: String, + pub selection_head: Option, + pub breadcrumb_text: String, shell_pid: u32, shell_fd: u32, - foreground_process_info: Option, + pub foreground_process_info: Option, scroll_px: f32, next_link_id: usize, selection_phase: SelectionPhase, - workspace_id: WorkspaceId, - item_id: ItemId, } impl Terminal { @@ -574,20 +561,6 @@ impl Terminal { if self.update_process_info() { 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) => { @@ -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( &mut self, - query: project::search::SearchQuery, + searcher: RegexSearch, cx: &mut ModelContext, ) -> Task>> { let term = self.term.clone(); 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(); all_search_matches(&term, &searcher).collect() @@ -1322,14 +1266,14 @@ fn open_uri(uri: &str) -> Result<(), std::io::Error> { #[cfg(test)] mod tests { + use alacritty_terminal::{ + index::{Column, Line, Point}, + term::cell::Cell, + }; use gpui::geometry::vector::vec2f; - use rand::{thread_rng, Rng}; + use rand::{rngs::ThreadRng, thread_rng, Rng}; - use crate::content_index_for_mouse; - - use self::terminal_test_context::TerminalTestContext; - - pub mod terminal_test_context; + use crate::{content_index_for_mouse, IndexedCell, TerminalContent, TerminalSize}; #[test] fn test_mouse_to_cell() { @@ -1346,7 +1290,7 @@ mod tests { 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) { let i = i as usize; @@ -1382,7 +1326,7 @@ mod tests { width: 100., }; - let (content, cells) = TerminalTestContext::create_terminal_content(size, &mut rng); + let (content, cells) = create_terminal_content(size, &mut rng); assert_eq!( content.cells[content_index_for_mouse(vec2f(-10., -10.), &content)].c, @@ -1393,4 +1337,37 @@ mod tests { cells[9][9] ); } + + fn create_terminal_content( + size: TerminalSize, + rng: &mut ThreadRng, + ) -> (TerminalContent, Vec>) { + 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, + ) + } } diff --git a/crates/terminal/src/tests/terminal_test_context.rs b/crates/terminal/src/tests/terminal_test_context.rs deleted file mode 100644 index 67ebb55805..0000000000 --- a/crates/terminal/src/tests/terminal_test_context.rs +++ /dev/null @@ -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, ViewHandle) { - 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, - path: impl AsRef, - ) -> (ModelHandle, 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, - path: impl AsRef, - ) -> (ModelHandle, Entry) { - self.create_wt(project, false, path).await - } - - async fn create_wt( - &mut self, - project: ModelHandle, - is_dir: bool, - path: impl AsRef, - ) -> (ModelHandle, 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, - entry: Entry, - project: ModelHandle, - ) { - 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>) { - 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); - } -} diff --git a/crates/terminal_view/Cargo.toml b/crates/terminal_view/Cargo.toml index 181ed606e0..fae60a943d 100644 --- a/crates/terminal_view/Cargo.toml +++ b/crates/terminal_view/Cargo.toml @@ -18,8 +18,8 @@ theme = { path = "../theme" } util = { path = "../util" } workspace = { path = "../workspace" } 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 } +terminal = { path = "../terminal" } smallvec = { version = "1.6", features = ["union"] } smol = "1.2.5" mio-extras = "2.0.6" diff --git a/crates/terminal/src/persistence.rs b/crates/terminal_view/src/persistence.rs similarity index 91% rename from crates/terminal/src/persistence.rs rename to crates/terminal_view/src/persistence.rs index 333911ee6d..db715aeef7 100644 --- a/crates/terminal/src/persistence.rs +++ b/crates/terminal_view/src/persistence.rs @@ -1,11 +1,12 @@ use std::path::PathBuf; use db::{define_connection, query, sqlez_macros::sql}; +use workspace::{WorkspaceDb, WorkspaceId}; type ModelId = usize; define_connection! { - pub static ref TERMINAL_CONNECTION: TerminalDb<()> = + pub static ref TERMINAL_DB: TerminalDb = &[sql!( CREATE TABLE terminals ( workspace_id INTEGER, @@ -34,7 +35,7 @@ impl TerminalDb { query! { pub async fn save_working_directory( item_id: ModelId, - workspace_id: WorkspaceId, + workspace_id: i64, working_directory: PathBuf ) -> Result<()> { INSERT OR REPLACE INTO terminals(item_id, workspace_id, working_directory) diff --git a/crates/terminal_view/src/terminal_container_view.rs b/crates/terminal_view/src/terminal_container_view.rs index 8f4bfeeb53..bf1e7bbddb 100644 --- a/crates/terminal_view/src/terminal_container_view.rs +++ b/crates/terminal_view/src/terminal_container_view.rs @@ -1,13 +1,18 @@ -use crate::persistence::TERMINAL_CONNECTION; -use crate::terminal_view::TerminalView; -use crate::{Event, TerminalBuilder, TerminalError}; +mod persistence; +pub mod terminal_element; +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 gpui::{ actions, elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, MutableAppContext, Task, View, ViewContext, ViewHandle, WeakViewHandle, }; +use terminal_view::regex_search_for_query; use util::{truncate_and_trailoff, ResultExt}; use workspace::searchable::{SearchEvent, SearchOptions, SearchableItem, SearchableItemHandle}; use workspace::{ @@ -30,6 +35,8 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(TerminalContainer::deploy); register_deserializable_item::(cx); + + terminal_view::init(cx); } //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( working_directory: Option, modal: bool, - workspace_id: WorkspaceId, + _workspace_id: WorkspaceId, cx: &mut ViewContext, ) -> Self { let settings = cx.global::(); @@ -119,8 +126,6 @@ impl TerminalContainer { settings.terminal_overrides.blinking.clone(), scroll, cx.window_id(), - cx.view_id(), - workspace_id, ) { Ok(terminal) => { let terminal = cx.add_model(|cx| terminal.subscribe(cx)); @@ -389,7 +394,7 @@ impl Item for TerminalContainer { item_id: workspace::ItemId, cx: &mut ViewContext, ) -> Task>> { - 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| { TerminalContainer::new( 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) { - if let Some(connected) = self.connected() { - let id = workspace.database_id(); - let terminal_handle = connected.read(cx).terminal().clone(); - terminal_handle.update(cx, |terminal, cx| terminal.set_workspace_id(id, cx)) + fn added_to_workspace(&mut self, _workspace: &mut Workspace, cx: &mut ViewContext) { + if let Some(_connected) = self.connected() { + // let id = workspace.database_id(); + // let terminal_handle = connected.read(cx).terminal().clone(); + //TODO + cx.background() + .spawn(TERMINAL_DB.update_workspace_id(0, 0, 0)) + .detach(); } } } @@ -477,7 +485,11 @@ impl SearchableItem for TerminalContainer { ) -> Task> { if let TerminalContainerContent::Connected(connected) = &self.content { 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 { Task::ready(Vec::new()) } @@ -585,21 +597,20 @@ mod tests { use super::*; use gpui::TestAppContext; + use project::{Entry, Worktree}; + use workspace::AppState; use std::path::Path; - use crate::tests::terminal_test_context::TerminalTestContext; - ///Working directory calculation tests ///No Worktrees in project -> home_dir() #[gpui::test] async fn no_worktree(cx: &mut TestAppContext) { //Setup variables - let mut cx = TerminalTestContext::new(cx); - let (project, workspace) = cx.blank_workspace().await; + let (project, workspace) = blank_workspace(cx).await; //Test - cx.cx.read(|cx| { + cx.read(|cx| { let workspace = workspace.read(cx); 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) { //Setup variables - let mut cx = TerminalTestContext::new(cx); - let (project, workspace) = cx.blank_workspace().await; - cx.create_file_wt(project.clone(), "/root.txt").await; + let (project, workspace) = blank_workspace(cx).await; + create_file_wt(project.clone(), "/root.txt", cx).await; - cx.cx.read(|cx| { + cx.read(|cx| { let workspace = workspace.read(cx); let active_entry = project.read(cx).active_entry(); @@ -642,12 +652,11 @@ mod tests { #[gpui::test] async fn no_active_entry_worktree_is_dir(cx: &mut TestAppContext) { //Setup variables - let mut cx = TerminalTestContext::new(cx); - let (project, workspace) = cx.blank_workspace().await; - let (_wt, _entry) = cx.create_folder_wt(project.clone(), "/root/").await; + let (project, workspace) = blank_workspace(cx).await; + let (_wt, _entry) = create_folder_wt(project.clone(), "/root/", cx).await; //Test - cx.cx.update(|cx| { + cx.update(|cx| { let workspace = workspace.read(cx); let active_entry = project.read(cx).active_entry(); @@ -665,14 +674,14 @@ mod tests { #[gpui::test] async fn active_entry_worktree_is_file(cx: &mut TestAppContext) { //Setup variables - let mut cx = TerminalTestContext::new(cx); - let (project, workspace) = cx.blank_workspace().await; - let (_wt, _entry) = cx.create_folder_wt(project.clone(), "/root1/").await; - let (wt2, entry2) = cx.create_file_wt(project.clone(), "/root2.txt").await; - cx.insert_active_entry_for(wt2, entry2, project.clone()); + + let (project, workspace) = blank_workspace(cx).await; + let (_wt, _entry) = create_folder_wt(project.clone(), "/root1/", cx).await; + let (wt2, entry2) = create_file_wt(project.clone(), "/root2.txt", cx).await; + insert_active_entry_for(wt2, entry2, project.clone(), cx); //Test - cx.cx.update(|cx| { + cx.update(|cx| { let workspace = workspace.read(cx); let active_entry = project.read(cx).active_entry(); @@ -689,14 +698,13 @@ mod tests { #[gpui::test] async fn active_entry_worktree_is_dir(cx: &mut TestAppContext) { //Setup variables - let mut cx = TerminalTestContext::new(cx); - let (project, workspace) = cx.blank_workspace().await; - let (_wt, _entry) = cx.create_folder_wt(project.clone(), "/root1/").await; - let (wt2, entry2) = cx.create_folder_wt(project.clone(), "/root2/").await; - cx.insert_active_entry_for(wt2, entry2, project.clone()); + let (project, workspace) = blank_workspace(cx).await; + let (_wt, _entry) = create_folder_wt(project.clone(), "/root1/", cx).await; + let (wt2, entry2) = create_folder_wt(project.clone(), "/root2/", cx).await; + insert_active_entry_for(wt2, entry2, project.clone(), cx); //Test - cx.cx.update(|cx| { + cx.update(|cx| { let workspace = workspace.read(cx); let active_entry = project.read(cx).active_entry(); @@ -708,4 +716,84 @@ mod tests { 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, ViewHandle) { + 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, + path: impl AsRef, + cx: &mut TestAppContext, + ) -> (ModelHandle, Entry) { + create_wt(project, true, path, cx).await + } + + ///Creates a worktree with 1 file: /root{suffix}.txt + async fn create_file_wt( + project: ModelHandle, + path: impl AsRef, + cx: &mut TestAppContext, + ) -> (ModelHandle, Entry) { + create_wt(project, false, path, cx).await + } + + async fn create_wt( + project: ModelHandle, + is_dir: bool, + path: impl AsRef, + cx: &mut TestAppContext, + ) -> (ModelHandle, 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, + entry: Entry, + project: ModelHandle, + 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)); + }); + } } diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index adfcb47024..53a38ec20a 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/crates/terminal_view/src/terminal_element.rs @@ -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 gpui::{ color::Color, @@ -22,17 +16,23 @@ use itertools::Itertools; use language::CursorShape; use ordered_float::OrderedFloat; 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 util::ResultExt; use std::{fmt::Debug, ops::RangeInclusive}; use std::{mem, ops::Range}; -use crate::{ - mappings::colors::convert_color, - terminal_view::{DeployContextMenu, TerminalView}, - IndexedCell, Terminal, TerminalContent, TerminalSize, -}; +use crate::terminal_view::{DeployContextMenu, TerminalView}; ///The information generated during layout that is nescessary for painting pub struct LayoutState { @@ -198,7 +198,10 @@ impl TerminalElement { //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 cur_alac_color = None; if let Some(rect) = cur_rect { @@ -299,7 +302,7 @@ impl TerminalElement { ///Convert the Alacritty cell styles to GPUI text styles and background color fn cell_style( indexed: &IndexedCell, - fg: AnsiColor, + fg: terminal::alacritty_terminal::ansi::Color, style: &TerminalStyle, text_style: &TextStyle, 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 //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 } else { let cursor_point = DisplayCursor::from(cursor.point, *display_offset); diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 21e055319a..ad0538e2ea 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -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 gpui::{ actions, @@ -14,10 +13,17 @@ use gpui::{ use serde::Deserialize; use settings::{Settings, TerminalBlink}; use smol::Timer; +use terminal::{ + alacritty_terminal::{ + index::Point, + term::{search::RegexSearch, TermMode}, + }, + Terminal, +}; use util::ResultExt; 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); @@ -95,6 +101,22 @@ impl TerminalView { cx.emit(Event::Wakeup); } 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), }) .detach(); @@ -246,8 +268,14 @@ impl TerminalView { query: project::search::SearchQuery, cx: &mut ViewContext, ) -> Task>> { - self.terminal - .update(cx, |term, cx| term.find_matches(query, cx)) + let searcher = regex_search_for_query(query); + + 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 { @@ -302,6 +330,14 @@ impl TerminalView { } } +pub fn regex_search_for_query(query: project::search::SearchQuery) -> Option { + 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 { fn ui_name() -> &'static str { "Terminal" diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 4163841d45..1b41613937 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -32,7 +32,7 @@ use settings::{ use smol::process::Command; use std::fs::OpenOptions; 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 settings::watched_json::{watch_keymap_file, watch_settings_file, WatchedJsonFile}; @@ -119,7 +119,7 @@ fn main() { diagnostics::init(cx); search::init(cx); vim::init(cx); - terminal::init(cx); + terminal_view::init(cx); theme_testbench::init(cx); cx.spawn(|cx| watch_themes(fs.clone(), themes.clone(), cx))