mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-07 20:39:04 +03:00
Rearrange the terminal code to not have a cyclic dependency with the project
This commit is contained in:
parent
1b8763d0cf
commit
83aefffa38
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -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",
|
||||
|
@ -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::<usize>(transaction)
|
||||
.all(|range| {
|
||||
excerpt_range.start <= range.start
|
||||
&& excerpt_range.end >= range.end
|
||||
|
@ -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<Self>,
|
||||
) -> Result<ModelHandle<TerminalConnection>> {
|
||||
_cx: &mut ModelContext<Self>,
|
||||
) -> Result<ModelHandle<Terminal>> {
|
||||
if self.is_remote() {
|
||||
return Err(anyhow!(
|
||||
"creating terminals as a guest is not supported yet"
|
||||
));
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"] }
|
||||
@ -28,3 +29,6 @@ anyhow = "1"
|
||||
thiserror = "1.0"
|
||||
lazy_static = "1.4.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.8.5"
|
||||
|
@ -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<TerminalBlink>,
|
||||
alternate_scroll: &AlternateScroll,
|
||||
window_id: usize,
|
||||
item_id: ItemId,
|
||||
workspace_id: WorkspaceId,
|
||||
) -> Result<TerminalBuilder> {
|
||||
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<IndexedCell>,
|
||||
mode: TermMode,
|
||||
display_offset: usize,
|
||||
selection_text: Option<String>,
|
||||
selection: Option<SelectionRange>,
|
||||
cursor: RenderableCursor,
|
||||
cursor_char: char,
|
||||
size: TerminalSize,
|
||||
last_hovered_hyperlink: Option<(String, RangeInclusive<Point>, usize)>,
|
||||
pub cells: Vec<IndexedCell>,
|
||||
pub mode: TermMode,
|
||||
pub display_offset: usize,
|
||||
pub selection_text: Option<String>,
|
||||
pub selection: Option<SelectionRange>,
|
||||
pub cursor: RenderableCursor,
|
||||
pub cursor_char: char,
|
||||
pub size: TerminalSize,
|
||||
pub last_hovered_hyperlink: Option<(String, RangeInclusive<Point>, usize)>,
|
||||
}
|
||||
|
||||
impl Default for TerminalContent {
|
||||
@ -521,19 +510,17 @@ pub struct Terminal {
|
||||
/// This is only used for terminal hyperlink checking
|
||||
last_mouse_position: Option<Vector2F>,
|
||||
pub matches: Vec<RangeInclusive<Point>>,
|
||||
last_content: TerminalContent,
|
||||
pub last_content: TerminalContent,
|
||||
last_synced: Instant,
|
||||
sync_task: Option<Task<()>>,
|
||||
selection_head: Option<Point>,
|
||||
breadcrumb_text: String,
|
||||
pub selection_head: Option<Point>,
|
||||
pub breadcrumb_text: String,
|
||||
shell_pid: u32,
|
||||
shell_fd: u32,
|
||||
foreground_process_info: Option<LocalProcessInfo>,
|
||||
pub foreground_process_info: Option<LocalProcessInfo>,
|
||||
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<Self>,
|
||||
) -> Task<Vec<RangeInclusive<Point>>> {
|
||||
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<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" }
|
||||
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"
|
||||
|
@ -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<WorkspaceDb> =
|
||||
&[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)
|
@ -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::<TerminalContainer>(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<PathBuf>,
|
||||
modal: bool,
|
||||
workspace_id: WorkspaceId,
|
||||
_workspace_id: WorkspaceId,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let settings = cx.global::<Settings>();
|
||||
@ -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<Pane>,
|
||||
) -> 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| {
|
||||
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<Self>) {
|
||||
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<Self>) {
|
||||
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<Vec<Self::Match>> {
|
||||
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<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 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);
|
||||
|
@ -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<Self>,
|
||||
) -> Task<Vec<RangeInclusive<Point>>> {
|
||||
let searcher = regex_search_for_query(query);
|
||||
|
||||
if let Some(searcher) = searcher {
|
||||
self.terminal
|
||||
.update(cx, |term, cx| term.find_matches(query, cx))
|
||||
.update(cx, |term, cx| term.find_matches(searcher, cx))
|
||||
} else {
|
||||
cx.background().spawn(async { Vec::new() })
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
fn ui_name() -> &'static str {
|
||||
"Terminal"
|
||||
|
@ -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))
|
||||
|
Loading…
Reference in New Issue
Block a user