diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 09b49716e03b69c178ad8b19b5eb8a8fcd72bacd..b2e79f820d3109589a38a1d7baf0ec64bcdce027 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -8,7 +8,7 @@ use collections::{BTreeMap, HashSet}; use futures::StreamExt; use gpui::{AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task}; use project::Project; -use std::sync::Arc; +use std::{os::unix::prelude::OsStrExt, sync::Arc}; use util::ResultExt; #[derive(Clone, Debug, PartialEq, Eq)] @@ -389,6 +389,7 @@ impl Room { id: worktree.id().to_proto(), root_name: worktree.root_name().into(), visible: worktree.is_visible(), + abs_path: worktree.abs_path().as_os_str().as_bytes().to_vec(), } }) .collect(), diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 90d7b6d4b582f7254ba80a132c8fea530ad479dd..e715a995a7c9ce4d331eb4bc9a1e237eead0fa56 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -15,7 +15,7 @@ use editor::{ self, ConfirmCodeAction, ConfirmCompletion, ConfirmRename, Editor, Redo, Rename, ToOffset, ToggleCodeActions, Undo, }; -use fs::{FakeFs, Fs as _, LineEnding}; +use fs::{FakeFs, Fs as _, HomeDir, LineEnding}; use futures::{channel::mpsc, Future, StreamExt as _}; use gpui::{ executor::{self, Deterministic}, @@ -3038,7 +3038,7 @@ async fn test_references(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { assert_eq!(references[1].buffer, references[0].buffer); assert_eq!( three_buffer.file().unwrap().full_path(cx), - Path::new("three.rs") + Path::new("/root/dir-2/three.rs") ); assert_eq!(references[0].range.to_offset(two_buffer), 24..27); @@ -6130,6 +6130,8 @@ impl TestServer { async fn create_client(&mut self, cx: &mut TestAppContext, name: &str) -> TestClient { cx.update(|cx| { + cx.set_global(HomeDir(Path::new("/tmp/").to_path_buf())); + let mut settings = Settings::test(cx); settings.projects_online_by_default = false; cx.set_global(settings); diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 564e173fec3430ab27f7d52d2b9e90960cdb76d7..bb8d7c2325680682eed81b2b119f76c5ba2d4119 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -42,6 +42,7 @@ use std::{ marker::PhantomData, net::SocketAddr, ops::{Deref, DerefMut}, + os::unix::prelude::OsStrExt, rc::Rc, sync::{ atomic::{AtomicBool, Ordering::SeqCst}, @@ -941,6 +942,7 @@ impl Server { id: *id, root_name: worktree.root_name.clone(), visible: worktree.visible, + abs_path: worktree.abs_path.as_os_str().as_bytes().to_vec(), }) .collect::>(); @@ -989,6 +991,7 @@ impl Server { let message = proto::UpdateWorktree { project_id: project_id.to_proto(), worktree_id: *worktree_id, + abs_path: worktree.abs_path.as_os_str().as_bytes().to_vec(), root_name: worktree.root_name.clone(), updated_entries: worktree.entries.values().cloned().collect(), removed_entries: Default::default(), diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index b7dd39cff17528172086faa385c9972858d1f874..fb21538d60b8f1c45ebfb3cdc4ca206c1b715f47 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -66,6 +66,7 @@ pub struct Collaborator { #[derive(Default, Serialize)] pub struct Worktree { + pub abs_path: PathBuf, pub root_name: String, pub visible: bool, #[serde(skip)] diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 57eee994057c67c5ca47bd56b4b3ee000021cd39..ba84559e5efefa030f46b992afb8b13aba3f9402 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1324,6 +1324,7 @@ impl EditorElement { line_height: f32, style: &EditorStyle, line_layouts: &[text_layout::Line], + include_root: bool, cx: &mut LayoutContext, ) -> (f32, Vec) { let editor = if let Some(editor) = self.view.upgrade(cx) { @@ -1427,10 +1428,11 @@ impl EditorElement { let font_size = (style.text_scale_factor * self.style.text.font_size).round(); + let path = buffer.resolve_file_path(cx, include_root); let mut filename = None; let mut parent_path = None; - if let Some(file) = buffer.file() { - let path = file.path(); + // Can't use .and_then() because `.file_name()` and `.parent()` return references :( + if let Some(path) = path { filename = path.file_name().map(|f| f.to_string_lossy().to_string()); parent_path = path.parent().map(|p| p.to_string_lossy().to_string() + "/"); @@ -1634,6 +1636,7 @@ impl Element for EditorElement { let mut highlighted_rows = None; let mut highlighted_ranges = Vec::new(); let mut show_scrollbars = false; + let mut include_root = false; self.update_view(cx.app, |view, cx| { let display_map = view.display_map.update(cx, |map, cx| map.snapshot(cx)); @@ -1706,6 +1709,11 @@ impl Element for EditorElement { } show_scrollbars = view.show_scrollbars(); + include_root = view + .project + .as_ref() + .map(|project| project.read(cx).visible_worktrees(cx).count() > 1) + .unwrap_or_default() }); let line_number_layouts = @@ -1745,6 +1753,7 @@ impl Element for EditorElement { line_height, &style, &line_layouts, + include_root, cx, ); diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index cf69d2a9228637296d536b3e3e76c87187c9fbdf..47e06fc545a419e8c2a25af430340e61bd6f6a6c 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -532,21 +532,17 @@ impl Item for Editor { let buffer = multibuffer.buffer(buffer_id)?; let buffer = buffer.read(cx); - let filename = if let Some(file) = buffer.file() { - if file.path().file_name().is_none() - || self - .project + let filename = buffer + .snapshot() + .resolve_file_path( + cx, + self.project .as_ref() .map(|project| project.read(cx).visible_worktrees(cx).count() > 1) - .unwrap_or_default() - { - file.full_path(cx).to_string_lossy().to_string() - } else { - file.path().to_string_lossy().to_string() - } - } else { - "untitled".to_string() - }; + .unwrap_or_default(), + ) + .map(|path| path.to_string_lossy().to_string()) + .unwrap_or_else(|| "untitled".to_string()); let mut breadcrumbs = vec![Label::new(filename, theme.breadcrumbs.text.clone()).boxed()]; breadcrumbs.extend(symbols.into_iter().map(|symbol| { diff --git a/crates/fs/src/fs.rs b/crates/fs/src/fs.rs index 2061d3734bd386901a97441fa34ea71c5b33f5a6..7bd7346b4b30ef2e1a99707e194e575f6cbc2f47 100644 --- a/crates/fs/src/fs.rs +++ b/crates/fs/src/fs.rs @@ -13,6 +13,7 @@ use smol::io::{AsyncReadExt, AsyncWriteExt}; use std::borrow::Cow; use std::cmp; use std::io::Write; +use std::ops::Deref; use std::sync::Arc; use std::{ io, @@ -92,6 +93,17 @@ impl LineEnding { } } } + +pub struct HomeDir(pub PathBuf); + +impl Deref for HomeDir { + type Target = PathBuf; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + #[async_trait::async_trait] pub trait Fs: Send + Sync { async fn create_dir(&self, path: &Path) -> Result<()>; diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index da7bd5332487f8fc07c68bdf42e9b6ba547ec7a4..5a75f1a56f53c4a621679649ee37fb7fafdb7611 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -2334,6 +2334,18 @@ impl BufferSnapshot { self.file.as_deref() } + pub fn resolve_file_path(&self, cx: &AppContext, include_root: bool) -> Option { + if let Some(file) = self.file() { + if file.path().file_name().is_none() || include_root { + Some(file.full_path(cx)) + } else { + Some(file.path().to_path_buf()) + } + } else { + None + } + } + pub fn file_update_count(&self) -> usize { self.file_update_count } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index f964726c4ceb3e250bd662acca72ecd90d2710dc..f05b7e6728a3273f77705daa69a28941e7d198c1 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -583,7 +583,10 @@ impl Project { cx: &mut gpui::TestAppContext, ) -> ModelHandle { if !cx.read(|cx| cx.has_global::()) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + cx.update(|cx| { + cx.set_global(Settings::test(cx)); + cx.set_global(HomeDir(Path::new("/tmp/").to_path_buf())) + }); } let languages = Arc::new(LanguageRegistry::test()); diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 1b0294c4d12ed83ab2586a5ffd5ee3f53089faa0..a9ac5f4411469e6466ded12b6762526dbc783658 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -2164,6 +2164,7 @@ async fn test_rescan_and_remote_updates( proto::WorktreeMetadata { id: initial_snapshot.id().to_proto(), root_name: initial_snapshot.root_name().into(), + abs_path: initial_snapshot.abs_path().as_os_str().as_bytes().to_vec(), visible: true, }, rpc.clone(), diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 383c9ac35b34c38d02e208cef1cb2b2c5a61e45a..9f0ebe32f7a10af0e1b547fd4ae0e51ce2d78134 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -5,8 +5,8 @@ use anyhow::{anyhow, Context, Result}; use client::{proto, Client}; use clock::ReplicaId; use collections::{HashMap, VecDeque}; -use fs::LineEnding; use fs::{repository::GitRepository, Fs}; +use fs::{HomeDir, LineEnding}; use futures::{ channel::{ mpsc::{self, UnboundedSender}, @@ -87,6 +87,7 @@ pub struct RemoteWorktree { #[derive(Clone)] pub struct Snapshot { id: WorktreeId, + abs_path: Arc, root_name: String, root_char_bag: CharBag, entries_by_path: SumTree, @@ -118,7 +119,6 @@ impl std::fmt::Debug for GitRepositoryEntry { } pub struct LocalSnapshot { - abs_path: Arc, ignores_by_parent_abs_path: HashMap, (Arc, usize)>, git_repositories: Vec, removed_entry_ids: HashMap, @@ -130,7 +130,6 @@ pub struct LocalSnapshot { impl Clone for LocalSnapshot { fn clone(&self) -> Self { Self { - abs_path: self.abs_path.clone(), ignores_by_parent_abs_path: self.ignores_by_parent_abs_path.clone(), git_repositories: self.git_repositories.iter().cloned().collect(), removed_entry_ids: self.removed_entry_ids.clone(), @@ -221,8 +220,11 @@ impl Worktree { .collect(); let root_name = worktree.root_name.clone(); let visible = worktree.visible; + + let abs_path = PathBuf::from(OsString::from_vec(worktree.abs_path)); let snapshot = Snapshot { id: WorktreeId(remote_id as usize), + abs_path: Arc::from(abs_path.deref()), root_name, root_char_bag, entries_by_path: Default::default(), @@ -372,6 +374,13 @@ impl Worktree { Self::Remote(worktree) => worktree.poll_snapshot(cx), }; } + + pub fn abs_path(&self) -> Arc { + match self { + Worktree::Local(worktree) => worktree.abs_path.clone(), + Worktree::Remote(worktree) => worktree.abs_path.clone(), + } + } } impl LocalWorktree { @@ -402,13 +411,13 @@ impl LocalWorktree { watch::channel_with(ScanState::Initializing); let tree = cx.add_model(move |cx: &mut ModelContext| { let mut snapshot = LocalSnapshot { - abs_path, ignores_by_parent_abs_path: Default::default(), git_repositories: Default::default(), removed_entry_ids: Default::default(), next_entry_id, snapshot: Snapshot { id: WorktreeId::from_usize(cx.model_id()), + abs_path, root_name: root_name.clone(), root_char_bag, entries_by_path: Default::default(), @@ -647,6 +656,7 @@ impl LocalWorktree { id: self.id().to_proto(), root_name: self.root_name().to_string(), visible: self.visible, + abs_path: self.abs_path().as_os_str().as_bytes().to_vec(), } } @@ -980,6 +990,7 @@ impl LocalWorktree { let update = proto::UpdateWorktree { project_id, worktree_id, + abs_path: snapshot.abs_path().as_os_str().as_bytes().to_vec(), root_name: snapshot.root_name().to_string(), updated_entries: snapshot .entries_by_path @@ -1389,6 +1400,7 @@ impl LocalSnapshot { proto::UpdateWorktree { project_id, worktree_id: self.id().to_proto(), + abs_path: self.abs_path().as_os_str().as_bytes().to_vec(), root_name, updated_entries: self.entries_by_path.iter().map(Into::into).collect(), removed_entries: Default::default(), @@ -1456,6 +1468,7 @@ impl LocalSnapshot { proto::UpdateWorktree { project_id, worktree_id, + abs_path: self.abs_path().as_os_str().as_bytes().to_vec(), root_name: self.root_name().to_string(), updated_entries, removed_entries, @@ -1839,10 +1852,25 @@ impl language::File for File { fn full_path(&self, cx: &AppContext) -> PathBuf { let mut full_path = PathBuf::new(); - full_path.push(self.worktree.read(cx).root_name()); + let worktree = self.worktree.read(cx); + + if worktree.is_visible() { + full_path.push(worktree.root_name()); + } else { + let path = worktree.abs_path(); + + if worktree.is_local() && path.starts_with(cx.global::().as_path()) { + full_path.push("~"); + full_path.push(path.strip_prefix(cx.global::().as_path()).unwrap()); + } else { + full_path.push(path) + } + } + if self.path.components().next().is_some() { full_path.push(&self.path); } + full_path } @@ -3455,7 +3483,6 @@ mod tests { let fs = Arc::new(RealFs); let next_entry_id = Arc::new(AtomicUsize::new(0)); let mut initial_snapshot = LocalSnapshot { - abs_path: root_dir.path().into(), removed_entry_ids: Default::default(), ignores_by_parent_abs_path: Default::default(), git_repositories: Default::default(), @@ -3464,6 +3491,7 @@ mod tests { id: WorktreeId::from_usize(0), entries_by_path: Default::default(), entries_by_id: Default::default(), + abs_path: root_dir.path().into(), root_name: Default::default(), root_char_bag: Default::default(), scan_id: 0, diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 380b83ae8d3c8a09db3296b086a81e5a5b48c7e5..67f6c1350781ad9f68e3d223678bef208f74f169 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -266,6 +266,7 @@ message UpdateWorktree { repeated uint64 removed_entries = 5; uint64 scan_id = 6; bool is_last_update = 7; + bytes abs_path = 8; } message UpdateWorktreeExtensions { @@ -1061,6 +1062,7 @@ message WorktreeMetadata { uint64 id = 1; string root_name = 2; bool visible = 3; + bytes abs_path = 4; } message UpdateDiffBase { diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 069fde4e59638d25673b6c517c6e9b7c776d8a38..827a8ff1a8530342fe887dbe1b6ace85d960ed50 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -435,6 +435,7 @@ pub fn split_worktree_update( project_id: message.project_id, worktree_id: message.worktree_id, root_name: message.root_name.clone(), + abs_path: message.abs_path.clone(), updated_entries, removed_entries: mem::take(&mut message.removed_entries), scan_id: message.scan_id, diff --git a/crates/rpc/src/rpc.rs b/crates/rpc/src/rpc.rs index 89963eb0627f27b39030fc24d7692a97d873209a..9193eceb700dde9c6a861c537d53ed88c69bc353 100644 --- a/crates/rpc/src/rpc.rs +++ b/crates/rpc/src/rpc.rs @@ -6,4 +6,4 @@ pub use conn::Connection; pub use peer::*; mod macros; -pub const PROTOCOL_VERSION: u32 = 36; +pub const PROTOCOL_VERSION: u32 = 37; \ No newline at end of file diff --git a/crates/terminal/src/terminal_view.rs b/crates/terminal/src/terminal_view.rs index 732c0a717edddab1d42532252692aa57c814477c..5ca5eb6dceab57c52076609128775e53b364908e 100644 --- a/crates/terminal/src/terminal_view.rs +++ b/crates/terminal/src/terminal_view.rs @@ -38,18 +38,7 @@ pub struct SendKeystroke(String); actions!( terminal, - [ - Up, - Down, - CtrlC, - Escape, - Enter, - Clear, - Copy, - Paste, - ShowCharacterPalette, - SearchTest - ] + [Clear, Copy, Paste, ShowCharacterPalette, SearchTest] ); impl_actions!(terminal, [SendText, SendKeystroke]); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index ece8cedfb1e516f8ff66fc8c76b3814c0fabd85b..04a59c47b1f45e66d1f4490d6dc0c124bcd9385d 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -927,6 +927,9 @@ impl From<&dyn NotificationHandle> for AnyViewHandle { impl AppState { #[cfg(any(test, feature = "test-support"))] pub fn test(cx: &mut MutableAppContext) -> Arc { + use fs::HomeDir; + + cx.set_global(HomeDir(Path::new("/tmp/").to_path_buf())); let settings = Settings::test(cx); cx.set_global(settings); diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index a921bc2680271f195aa985b751fc98f45df0a9e9..e62317146cadae2b1df060b078e5b5aebab3ae8a 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -23,7 +23,7 @@ use isahc::{config::Configurable, Request}; use language::LanguageRegistry; use log::LevelFilter; use parking_lot::Mutex; -use project::{Fs, ProjectStore}; +use project::{Fs, HomeDir, ProjectStore}; use serde_json::json; use settings::{ self, settings_file::SettingsFile, KeymapFileContent, Settings, SettingsFileContent, @@ -99,6 +99,8 @@ fn main() { let (settings_file_content, keymap_file) = cx.background().block(config_files).unwrap(); + cx.set_global(HomeDir(zed::paths::HOME.to_path_buf())); + //Setup settings global before binding actions cx.set_global(SettingsFile::new( &*zed::paths::SETTINGS, diff --git a/crates/zed/src/paths.rs b/crates/zed/src/paths.rs index d6d99288c771f5260d5cd452fc97a04f15c87969..02db8ab55e7a53b70767eb04b54ed255bcc3bf1d 100644 --- a/crates/zed/src/paths.rs +++ b/crates/zed/src/paths.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; lazy_static::lazy_static! { - static ref HOME: PathBuf = dirs::home_dir().expect("failed to determine home directory"); + pub static ref HOME: PathBuf = dirs::home_dir().expect("failed to determine home directory"); pub static ref CONFIG_DIR: PathBuf = HOME.join(".config").join("zed"); pub static ref LOGS_DIR: PathBuf = HOME.join("Library/Logs/Zed"); pub static ref LANGUAGES_DIR: PathBuf = HOME.join("Library/Application Support/Zed/languages");