Cargo.lock 🔗
@@ -12871,6 +12871,7 @@ dependencies = [
"prost-build 0.9.0",
"serde",
"typed-path",
+ "util",
"workspace-hack",
]
Max Brunsfeld created
Cargo.lock | 1
crates/git/src/commit.rs | 0
crates/git/src/repository.rs | 82 ++-----
crates/project/src/git_store.rs | 2
crates/proto/Cargo.toml | 1
crates/proto/src/typed_envelope.rs | 7
crates/util/src/rel_path.rs | 171 ++++++++++++++++
crates/util/src/util.rs | 1
crates/worktree/src/worktree.rs | 83 +++----
crates/worktree/src/worktree_tests.rs | 293 ++++++++++++++--------------
10 files changed, 398 insertions(+), 243 deletions(-)
@@ -12871,6 +12871,7 @@ dependencies = [
"prost-build 0.9.0",
"serde",
"typed-path",
+ "util",
"workspace-hack",
]
@@ -27,6 +27,7 @@ use std::{
use sum_tree::MapSeekTarget;
use thiserror::Error;
use util::command::{new_smol_command, new_std_command};
+use util::rel_path::RelPath;
use util::{ResultExt, paths};
use uuid::Uuid;
@@ -662,14 +663,22 @@ impl GitRepository for RealGitRepository {
for (path, status_code) in changes {
match status_code {
StatusCode::Modified => {
- writeln!(&mut stdin, "{commit}:{}", path.display())?;
- writeln!(&mut stdin, "{parent_sha}:{}", path.display())?;
+ write!(&mut stdin, "{commit}:")?;
+ stdin.write_all(path.as_bytes())?;
+ stdin.write_all(b"\n")?;
+ write!(&mut stdin, "{parent_sha}:")?;
+ stdin.write_all(path.as_bytes())?;
+ stdin.write_all(b"\n")?;
}
StatusCode::Added => {
- writeln!(&mut stdin, "{commit}:{}", path.display())?;
+ write!(&mut stdin, "{commit}:")?;
+ stdin.write_all(path.as_bytes())?;
+ stdin.write_all(b"\n")?;
}
StatusCode::Deleted => {
- writeln!(&mut stdin, "{parent_sha}:{}", path.display())?;
+ write!(&mut stdin, "{parent_sha}:")?;
+ stdin.write_all(path.as_bytes())?;
+ stdin.write_all(b"\n")?;
}
_ => continue,
}
@@ -1652,7 +1661,7 @@ fn git_status_args(path_prefixes: &[RepoPath]) -> Vec<OsString> {
OsString::from("-z"),
];
args.extend(path_prefixes.iter().map(|path_prefix| {
- if path_prefix.0.as_ref() == Path::new("") {
+ if path_prefix.0.as_ref() == RelPath::new("") {
Path::new(".").into()
} else {
path_prefix.as_os_str().into()
@@ -1905,64 +1914,33 @@ async fn run_askpass_command(
}
pub static WORK_DIRECTORY_REPO_PATH: LazyLock<RepoPath> =
- LazyLock::new(|| RepoPath(Path::new("").into()));
+ LazyLock::new(|| RepoPath(RelPath::new("").into()));
#[derive(Clone, Debug, Ord, Hash, PartialOrd, Eq, PartialEq)]
-pub struct RepoPath(pub Arc<Path>);
+pub struct RepoPath(pub Arc<RelPath>);
impl RepoPath {
- pub fn new(path: PathBuf) -> Self {
- debug_assert!(path.is_relative(), "Repo paths must be relative");
-
- RepoPath(path.into())
- }
-
pub fn from_str(path: &str) -> Self {
- let path = Path::new(path);
- debug_assert!(path.is_relative(), "Repo paths must be relative");
-
- RepoPath(path.into())
+ RepoPath(RelPath::new(path).into())
}
pub fn to_unix_style(&self) -> Cow<'_, OsStr> {
- #[cfg(target_os = "windows")]
- {
- use std::ffi::OsString;
-
- let path = self.0.as_os_str().to_string_lossy().replace("\\", "/");
- Cow::Owned(OsString::from(path))
- }
- #[cfg(not(target_os = "windows"))]
- {
- Cow::Borrowed(self.0.as_os_str())
- }
+ self.0.as_os_str()
}
}
-impl std::fmt::Display for RepoPath {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- self.0.to_string_lossy().fmt(f)
- }
-}
-
-impl From<&Path> for RepoPath {
- fn from(value: &Path) -> Self {
- RepoPath::new(value.into())
+impl From<&RelPath> for RepoPath {
+ fn from(value: &RelPath) -> Self {
+ RepoPath(value.into())
}
}
-impl From<Arc<Path>> for RepoPath {
- fn from(value: Arc<Path>) -> Self {
+impl From<Arc<RelPath>> for RepoPath {
+ fn from(value: Arc<RelPath>) -> Self {
RepoPath(value)
}
}
-impl From<PathBuf> for RepoPath {
- fn from(value: PathBuf) -> Self {
- RepoPath::new(value)
- }
-}
-
impl From<&str> for RepoPath {
fn from(value: &str) -> Self {
Self::from_str(value)
@@ -1971,32 +1949,32 @@ impl From<&str> for RepoPath {
impl Default for RepoPath {
fn default() -> Self {
- RepoPath(Path::new("").into())
+ RepoPath(RelPath::new("").into())
}
}
-impl AsRef<Path> for RepoPath {
- fn as_ref(&self) -> &Path {
+impl AsRef<RelPath> for RepoPath {
+ fn as_ref(&self) -> &RelPath {
self.0.as_ref()
}
}
impl std::ops::Deref for RepoPath {
- type Target = Path;
+ type Target = RelPath;
fn deref(&self) -> &Self::Target {
&self.0
}
}
-impl Borrow<Path> for RepoPath {
- fn borrow(&self) -> &Path {
+impl Borrow<RelPath> for RepoPath {
+ fn borrow(&self) -> &RelPath {
self.0.as_ref()
}
}
#[derive(Debug)]
-pub struct RepoPathDescendants<'a>(pub &'a Path);
+pub struct RepoPathDescendants<'a>(pub &'a RelPath);
impl MapSeekTarget<RepoPath> for RepoPathDescendants<'_> {
fn cmp_cursor(&self, key: &RepoPath) -> Ordering {
@@ -1663,7 +1663,7 @@ impl GitStore {
.payload
.paths
.into_iter()
- .map(PathBuf::from)
+ .map(RelPath::new)
.map(RepoPath::new)
.collect();
@@ -20,6 +20,7 @@ doctest = false
anyhow.workspace = true
prost.workspace = true
serde.workspace = true
+util.workspace = true
workspace-hack.workspace = true
[build-dependencies]
@@ -9,6 +9,7 @@ use std::{
sync::Arc,
};
use std::{marker::PhantomData, time::Instant};
+use util::rel_path::RelPath;
pub trait EnvelopedMessage: Clone + Debug + Serialize + Sized + Send + Sync + 'static {
const NAME: &'static str;
@@ -158,6 +159,12 @@ impl FromProto for Arc<Path> {
}
}
+impl FromProto for Arc<RelPath> {
+ fn from_proto(proto: String) -> Self {
+ RelPath::new(proto.as_bytes()).into()
+ }
+}
+
impl ToProto for PathBuf {
fn to_proto(self) -> String {
to_proto_path(&self)
@@ -0,0 +1,171 @@
+use std::{
+ borrow::Cow,
+ ffi::OsStr,
+ path::{Display, Path, PathBuf},
+ sync::Arc,
+};
+
+#[repr(transparent)]
+#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct RelPath([u8]);
+
+impl RelPath {
+ pub fn new<S: AsRef<[u8]> + ?Sized>(s: &S) -> &Self {
+ unsafe { &*(s.as_ref() as *const [u8] as *const Self) }
+ }
+
+ pub fn components(&self) -> RelPathComponents {
+ RelPathComponents(&self.0)
+ }
+
+ pub fn file_name(&self) -> Option<&[u8]> {
+ self.components().next_back()
+ }
+
+ pub fn parent(&self) -> Option<&Self> {
+ let mut components = self.components();
+ components.next_back()?;
+ Some(Self::new(components.0))
+ }
+
+ pub fn starts_with(&self, other: &Self) -> bool {
+ let mut components = self.components();
+ other.components().all(|other_component| {
+ components
+ .next()
+ .map_or(false, |component| component == other_component)
+ })
+ }
+
+ pub fn strip_prefix(&self, other: &Self) -> Result<&Self, ()> {
+ let mut components = self.components();
+ other
+ .components()
+ .all(|other_component| {
+ components
+ .next()
+ .map_or(false, |component| component == other_component)
+ })
+ .then(|| Self::new(components.0))
+ .ok_or_else(|| ())
+ }
+
+ pub fn append_to_abs_path(&self, abs_path: &Path) -> PathBuf {
+ // TODO: implement this differently
+ let mut result = abs_path.to_path_buf();
+ for component in self.components() {
+ result.push(String::from_utf8_lossy(component).as_ref());
+ }
+ result
+ }
+
+ pub fn to_proto(&self) -> String {
+ String::from_utf8_lossy(&self.0).to_string()
+ }
+
+ pub fn as_bytes(&self) -> &[u8] {
+ &self.0
+ }
+
+ pub fn as_os_str(&self) -> Cow<'_, OsStr> {
+ #[cfg(target_os = "windows")]
+ {
+ use std::ffi::OsString;
+ let path = String::from_utf8_lossy(&self.0);
+ match path {
+ Cow::Borrowed(s) => Cow::Borrowed(OsStr::new(s)),
+ Cow::Owned(s) => Cow::Owned(OsString::from(s)),
+ }
+ }
+ #[cfg(not(target_os = "windows"))]
+ {
+ Cow::Borrowed(self.0.as_os_str())
+ }
+ }
+}
+
+impl From<&RelPath> for Arc<RelPath> {
+ fn from(rel_path: &RelPath) -> Self {
+ let bytes: Arc<[u8]> = Arc::from(&rel_path.0);
+ unsafe { Arc::from_raw(Arc::into_raw(bytes) as *const RelPath) }
+ }
+}
+
+impl AsRef<RelPath> for &str {
+ fn as_ref(&self) -> &RelPath {
+ RelPath::new(self)
+ }
+}
+
+impl std::fmt::Debug for RelPath {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ if let Ok(str) = std::str::from_utf8(&self.0) {
+ write!(f, "RelPath({})", str)
+ } else {
+ write!(f, "RelPath({:?})", &self.0)
+ }
+ }
+}
+
+pub struct RelPathComponents<'a>(&'a [u8]);
+
+const SEPARATOR: u8 = b'/';
+
+impl<'a> Iterator for RelPathComponents<'a> {
+ type Item = &'a [u8];
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if let Some(sep_ix) = self.0.iter().position(|&byte| byte == SEPARATOR) {
+ let (head, tail) = self.0.split_at(sep_ix);
+ self.0 = &tail[1..];
+ Some(head)
+ } else if self.0.is_empty() {
+ None
+ } else {
+ let result = self.0;
+ self.0 = &[];
+ Some(result)
+ }
+ }
+}
+
+impl<'a> DoubleEndedIterator for RelPathComponents<'a> {
+ fn next_back(&mut self) -> Option<Self::Item> {
+ if let Some(sep_ix) = self.0.iter().rposition(|&byte| byte == SEPARATOR) {
+ let (head, tail) = self.0.split_at(sep_ix);
+ self.0 = head;
+ Some(&tail[1..])
+ } else if self.0.is_empty() {
+ None
+ } else {
+ let result = self.0;
+ self.0 = &[];
+ Some(result)
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_rel_path_components() {
+ let path = RelPath::new("foo/bar/baz");
+ let mut components = path.components();
+ assert_eq!(components.next(), Some("foo".as_bytes()));
+ assert_eq!(components.next(), Some("bar".as_bytes()));
+ assert_eq!(components.next(), Some("baz".as_bytes()));
+ assert_eq!(components.next(), None);
+ }
+
+ #[test]
+ fn test_rel_path_parent() {
+ assert_eq!(
+ RelPath::new("foo/bar/baz").parent().unwrap(),
+ RelPath::new("foo/bar")
+ );
+ assert_eq!(RelPath::new("foo").parent().unwrap(), RelPath::new(""));
+ assert_eq!(RelPath::new("").parent(), None);
+ }
+}
@@ -5,6 +5,7 @@ pub mod fs;
pub mod markdown;
pub mod paths;
pub mod redact;
+pub mod rel_path;
pub mod schemars;
pub mod serde;
pub mod shell_env;
@@ -67,6 +67,7 @@ use text::{LineEnding, Rope};
use util::{
ResultExt,
paths::{PathMatcher, SanitizedPath, home_dir},
+ rel_path::RelPath,
};
pub use worktree_settings::WorktreeSettings;
@@ -132,12 +133,12 @@ pub struct LocalWorktree {
}
pub struct PathPrefixScanRequest {
- path: Arc<Path>,
+ path: Arc<RelPath>,
done: SmallVec<[barrier::Sender; 1]>,
}
struct ScanRequest {
- relative_paths: Vec<Arc<Path>>,
+ relative_paths: Vec<Arc<RelPath>>,
done: SmallVec<[barrier::Sender; 1]>,
}
@@ -186,7 +187,7 @@ pub struct Snapshot {
#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum WorkDirectory {
InProject {
- relative_path: Arc<Path>,
+ relative_path: Arc<RelPath>,
},
AboveProject {
absolute_path: Arc<Path>,
@@ -197,7 +198,7 @@ pub enum WorkDirectory {
impl WorkDirectory {
#[cfg(test)]
fn in_project(path: &str) -> Self {
- let path = Path::new(path);
+ let path = RelPath::new(path);
Self::InProject {
relative_path: path.into(),
}
@@ -232,9 +233,8 @@ impl WorkDirectory {
/// is a repository in a directory between these two paths
/// external .git folder in a parent folder of the project root.
#[track_caller]
- pub fn directory_contains(&self, path: impl AsRef<Path>) -> bool {
+ pub fn directory_contains(&self, path: impl AsRef<RelPath>) -> bool {
let path = path.as_ref();
- debug_assert!(path.is_relative());
match self {
WorkDirectory::InProject { relative_path } => path.starts_with(relative_path),
WorkDirectory::AboveProject { .. } => true,
@@ -246,9 +246,8 @@ impl WorkDirectory {
/// If the root of the repository (and its .git folder) are located in a parent folder
/// of the project root folder, then the returned RepoPath is relative to the root
/// of the repository and not a valid path inside the project.
- pub fn relativize(&self, path: &Path) -> Result<RepoPath> {
+ pub fn relativize(&self, path: &RelPath) -> Result<RepoPath> {
// path is assumed to be relative to worktree root.
- debug_assert!(path.is_relative());
match self {
WorkDirectory::InProject { relative_path } => Ok(path
.strip_prefix(relative_path)
@@ -842,12 +841,12 @@ impl Worktree {
pub fn create_entry(
&mut self,
- path: impl Into<Arc<Path>>,
+ path: impl Into<Arc<RelPath>>,
is_directory: bool,
content: Option<Vec<u8>>,
cx: &Context<Worktree>,
) -> Task<Result<CreatedEntry>> {
- let path: Arc<Path> = path.into();
+ let path: Arc<RelPath> = path.into();
let worktree_id = self.id();
match self {
Worktree::Local(this) => this.create_entry(path, is_directory, content, cx),
@@ -914,7 +913,7 @@ impl Worktree {
Some(task)
}
- fn get_children_ids_recursive(&self, path: &Path, ids: &mut Vec<ProjectEntryId>) {
+ fn get_children_ids_recursive(&self, path: &RelPath, ids: &mut Vec<ProjectEntryId>) {
let children_iter = self.child_entries(path);
for child in children_iter {
ids.push(child.id);
@@ -1575,7 +1574,7 @@ impl LocalWorktree {
fn create_entry(
&self,
- path: impl Into<Arc<Path>>,
+ path: impl Into<Arc<RelPath>>,
is_dir: bool,
content: Option<Vec<u8>>,
cx: &Context<Worktree>,
@@ -1975,7 +1974,7 @@ impl LocalWorktree {
}))
}
- fn refresh_entries_for_paths(&self, paths: Vec<Arc<Path>>) -> barrier::Receiver {
+ fn refresh_entries_for_paths(&self, paths: Vec<Arc<RelPath>>) -> barrier::Receiver {
let (tx, rx) = barrier::channel();
self.scan_requests_tx
.try_send(ScanRequest {
@@ -1987,11 +1986,14 @@ impl LocalWorktree {
}
#[cfg(feature = "test-support")]
- pub fn manually_refresh_entries_for_paths(&self, paths: Vec<Arc<Path>>) -> barrier::Receiver {
+ pub fn manually_refresh_entries_for_paths(
+ &self,
+ paths: Vec<Arc<RelPath>>,
+ ) -> barrier::Receiver {
self.refresh_entries_for_paths(paths)
}
- pub fn add_path_prefix_to_scan(&self, path_prefix: Arc<Path>) -> barrier::Receiver {
+ pub fn add_path_prefix_to_scan(&self, path_prefix: Arc<RelPath>) -> barrier::Receiver {
let (tx, rx) = barrier::channel();
self.path_prefixes_to_scan_tx
.try_send(PathPrefixScanRequest {
@@ -2004,8 +2006,8 @@ impl LocalWorktree {
fn refresh_entry(
&self,
- path: Arc<Path>,
- old_path: Option<Arc<Path>>,
+ path: Arc<RelPath>,
+ old_path: Option<Arc<RelPath>>,
cx: &Context<Worktree>,
) -> Task<Result<Option<Entry>>> {
if self.settings.is_path_excluded(&path) {
@@ -2403,18 +2405,8 @@ impl Snapshot {
}
}
- pub fn absolutize(&self, path: &Path) -> Result<PathBuf> {
- if path
- .components()
- .any(|component| !matches!(component, std::path::Component::Normal(_)))
- {
- anyhow::bail!("invalid path");
- }
- if path.file_name().is_some() {
- Ok(self.abs_path.as_path().join(path))
- } else {
- Ok(self.abs_path.as_path().to_path_buf())
- }
+ pub fn absolutize(&self, path: &RelPath) -> Result<PathBuf> {
+ Ok(path.append_to_abs_path(&self.abs_path.0))
}
pub fn contains_entry(&self, entry_id: ProjectEntryId) -> bool {
@@ -2585,7 +2577,7 @@ impl Snapshot {
include_files: bool,
include_dirs: bool,
include_ignored: bool,
- path: &Path,
+ path: &RelPath,
) -> Traversal<'_> {
Traversal::new(self, include_files, include_dirs, include_ignored, path)
}
@@ -2602,15 +2594,15 @@ impl Snapshot {
self.traverse_from_offset(true, true, include_ignored, start)
}
- pub fn paths(&self) -> impl Iterator<Item = &Arc<Path>> {
- let empty_path = Path::new("");
+ pub fn paths(&self) -> impl Iterator<Item = &Arc<RelPath>> {
+ let empty_path = RelPath::new("");
self.entries_by_path
.cursor::<()>(&())
.filter(move |entry| entry.path.as_ref() != empty_path)
.map(|entry| &entry.path)
}
- pub fn child_entries<'a>(&'a self, parent_path: &'a Path) -> ChildEntriesIter<'a> {
+ pub fn child_entries<'a>(&'a self, parent_path: &'a RelPath) -> ChildEntriesIter<'a> {
let options = ChildEntriesOptions {
include_files: true,
include_dirs: true,
@@ -2621,7 +2613,7 @@ impl Snapshot {
pub fn child_entries_with_options<'a>(
&'a self,
- parent_path: &'a Path,
+ parent_path: &'a RelPath,
options: ChildEntriesOptions,
) -> ChildEntriesIter<'a> {
let mut cursor = self.entries_by_path.cursor(&());
@@ -2659,9 +2651,8 @@ impl Snapshot {
self.scan_id
}
- pub fn entry_for_path(&self, path: impl AsRef<Path>) -> Option<&Entry> {
+ pub fn entry_for_path(&self, path: impl AsRef<RelPath>) -> Option<&Entry> {
let path = path.as_ref();
- debug_assert!(path.is_relative());
self.traverse_from_path(true, true, true, path)
.entry()
.and_then(|entry| {
@@ -3436,7 +3427,7 @@ impl File {
pub struct Entry {
pub id: ProjectEntryId,
pub kind: EntryKind,
- pub path: Arc<Path>,
+ pub path: Arc<RelPath>,
pub inode: u64,
pub mtime: Option<MTime>,
@@ -3510,7 +3501,7 @@ pub struct UpdatedGitRepository {
pub common_dir_abs_path: Option<Arc<Path>>,
}
-pub type UpdatedEntriesSet = Arc<[(Arc<Path>, ProjectEntryId, PathChange)]>;
+pub type UpdatedEntriesSet = Arc<[(Arc<RelPath>, ProjectEntryId, PathChange)]>;
pub type UpdatedGitRepositoriesSet = Arc<[UpdatedGitRepository]>;
#[derive(Clone, Debug)]
@@ -3786,7 +3777,7 @@ impl<'a> sum_tree::Dimension<'a, PathEntrySummary> for ProjectEntryId {
}
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
-pub struct PathKey(pub Arc<Path>);
+pub struct PathKey(pub Arc<RelPath>);
impl Default for PathKey {
fn default() -> Self {
@@ -5188,7 +5179,7 @@ impl WorktreeModelHandle for Entity<Worktree> {
#[derive(Clone, Debug)]
struct TraversalProgress<'a> {
- max_path: &'a Path,
+ max_path: &'a RelPath,
count: usize,
non_ignored_count: usize,
file_count: usize,
@@ -5250,7 +5241,7 @@ impl<'a> Traversal<'a> {
include_files: bool,
include_dirs: bool,
include_ignored: bool,
- start_path: &Path,
+ start_path: &RelPath,
) -> Self {
let mut cursor = snapshot.entries_by_path.cursor(&());
cursor.seek(&TraversalTarget::path(start_path), Bias::Left);
@@ -5343,12 +5334,12 @@ impl<'a> Iterator for Traversal<'a> {
#[derive(Debug, Clone, Copy)]
pub enum PathTarget<'a> {
- Path(&'a Path),
- Successor(&'a Path),
+ Path(&'a RelPath),
+ Successor(&'a RelPath),
}
impl PathTarget<'_> {
- fn cmp_path(&self, other: &Path) -> Ordering {
+ fn cmp_path(&self, other: &RelPath) -> Ordering {
match self {
PathTarget::Path(path) => path.cmp(&other),
PathTarget::Successor(path) => {
@@ -5386,11 +5377,11 @@ enum TraversalTarget<'a> {
}
impl<'a> TraversalTarget<'a> {
- fn path(path: &'a Path) -> Self {
+ fn path(path: &'a RelPath) -> Self {
Self::Path(PathTarget::Path(path))
}
- fn successor(path: &'a Path) -> Self {
+ fn successor(path: &'a RelPath) -> Self {
Self::Path(PathTarget::Successor(path))
}
@@ -20,7 +20,7 @@ use std::{
path::{Path, PathBuf},
sync::Arc,
};
-use util::{ResultExt, path, test::TempTree};
+use util::{ResultExt, path, rel_path::RelPath, test::TempTree};
#[gpui::test]
async fn test_traversal(cx: &mut TestAppContext) {
@@ -56,10 +56,10 @@ async fn test_traversal(cx: &mut TestAppContext) {
.map(|entry| entry.path.as_ref())
.collect::<Vec<_>>(),
vec![
- Path::new(""),
- Path::new(".gitignore"),
- Path::new("a"),
- Path::new("a/c"),
+ RelPath::new(""),
+ RelPath::new(".gitignore"),
+ RelPath::new("a"),
+ RelPath::new("a/c"),
]
);
assert_eq!(
@@ -67,11 +67,11 @@ async fn test_traversal(cx: &mut TestAppContext) {
.map(|entry| entry.path.as_ref())
.collect::<Vec<_>>(),
vec![
- Path::new(""),
- Path::new(".gitignore"),
- Path::new("a"),
- Path::new("a/b"),
- Path::new("a/c"),
+ RelPath::new(""),
+ RelPath::new(".gitignore"),
+ RelPath::new("a"),
+ RelPath::new("a/b"),
+ RelPath::new("a/c"),
]
);
})
@@ -121,14 +121,14 @@ async fn test_circular_symlinks(cx: &mut TestAppContext) {
.map(|entry| entry.path.as_ref())
.collect::<Vec<_>>(),
vec![
- Path::new(""),
- Path::new("lib"),
- Path::new("lib/a"),
- Path::new("lib/a/a.txt"),
- Path::new("lib/a/lib"),
- Path::new("lib/b"),
- Path::new("lib/b/b.txt"),
- Path::new("lib/b/lib"),
+ RelPath::new(""),
+ RelPath::new("lib"),
+ RelPath::new("lib/a"),
+ RelPath::new("lib/a/a.txt"),
+ RelPath::new("lib/a/lib"),
+ RelPath::new("lib/b"),
+ RelPath::new("lib/b/b.txt"),
+ RelPath::new("lib/b/lib"),
]
);
});
@@ -147,14 +147,14 @@ async fn test_circular_symlinks(cx: &mut TestAppContext) {
.map(|entry| entry.path.as_ref())
.collect::<Vec<_>>(),
vec![
- Path::new(""),
- Path::new("lib"),
- Path::new("lib/a"),
- Path::new("lib/a/a.txt"),
- Path::new("lib/a/lib-2"),
- Path::new("lib/b"),
- Path::new("lib/b/b.txt"),
- Path::new("lib/b/lib"),
+ RelPath::new(""),
+ RelPath::new("lib"),
+ RelPath::new("lib/a"),
+ RelPath::new("lib/a/a.txt"),
+ RelPath::new("lib/a/lib-2"),
+ RelPath::new("lib/b"),
+ RelPath::new("lib/b/b.txt"),
+ RelPath::new("lib/b/lib"),
]
);
});
@@ -236,18 +236,20 @@ async fn test_symlinks_pointing_outside(cx: &mut TestAppContext) {
.map(|entry| (entry.path.as_ref(), entry.is_external))
.collect::<Vec<_>>(),
vec![
- (Path::new(""), false),
- (Path::new("deps"), false),
- (Path::new("deps/dep-dir2"), true),
- (Path::new("deps/dep-dir3"), true),
- (Path::new("src"), false),
- (Path::new("src/a.rs"), false),
- (Path::new("src/b.rs"), false),
+ (RelPath::new(""), false),
+ (RelPath::new("deps"), false),
+ (RelPath::new("deps/dep-dir2"), true),
+ (RelPath::new("deps/dep-dir3"), true),
+ (RelPath::new("src"), false),
+ (RelPath::new("src/a.rs"), false),
+ (RelPath::new("src/b.rs"), false),
]
);
assert_eq!(
- tree.entry_for_path("deps/dep-dir2").unwrap().kind,
+ tree.entry_for_path(RelPath::new("deps/dep-dir2"))
+ .unwrap()
+ .kind,
EntryKind::UnloadedDir
);
});
@@ -256,7 +258,7 @@ async fn test_symlinks_pointing_outside(cx: &mut TestAppContext) {
tree.read_with(cx, |tree, _| {
tree.as_local()
.unwrap()
- .refresh_entries_for_paths(vec![Path::new("deps/dep-dir3").into()])
+ .refresh_entries_for_paths(vec![RelPath::new("deps/dep-dir3").into()])
})
.recv()
.await;
@@ -269,24 +271,27 @@ async fn test_symlinks_pointing_outside(cx: &mut TestAppContext) {
.map(|entry| (entry.path.as_ref(), entry.is_external))
.collect::<Vec<_>>(),
vec![
- (Path::new(""), false),
- (Path::new("deps"), false),
- (Path::new("deps/dep-dir2"), true),
- (Path::new("deps/dep-dir3"), true),
- (Path::new("deps/dep-dir3/deps"), true),
- (Path::new("deps/dep-dir3/src"), true),
- (Path::new("src"), false),
- (Path::new("src/a.rs"), false),
- (Path::new("src/b.rs"), false),
+ (RelPath::new(""), false),
+ (RelPath::new("deps"), false),
+ (RelPath::new("deps/dep-dir2"), true),
+ (RelPath::new("deps/dep-dir3"), true),
+ (RelPath::new("deps/dep-dir3/deps"), true),
+ (RelPath::new("deps/dep-dir3/src"), true),
+ (RelPath::new("src"), false),
+ (RelPath::new("src/a.rs"), false),
+ (RelPath::new("src/b.rs"), false),
]
);
});
assert_eq!(
mem::take(&mut *tree_updates.lock()),
&[
- (Path::new("deps/dep-dir3").into(), PathChange::Loaded),
- (Path::new("deps/dep-dir3/deps").into(), PathChange::Loaded),
- (Path::new("deps/dep-dir3/src").into(), PathChange::Loaded)
+ (RelPath::new("deps/dep-dir3").into(), PathChange::Loaded),
+ (
+ RelPath::new("deps/dep-dir3/deps").into(),
+ PathChange::Loaded
+ ),
+ (RelPath::new("deps/dep-dir3/src").into(), PathChange::Loaded)
]
);
@@ -294,7 +299,7 @@ async fn test_symlinks_pointing_outside(cx: &mut TestAppContext) {
tree.read_with(cx, |tree, _| {
tree.as_local()
.unwrap()
- .refresh_entries_for_paths(vec![Path::new("deps/dep-dir3/src").into()])
+ .refresh_entries_for_paths(vec![RelPath::new("deps/dep-dir3/src").into()])
})
.recv()
.await;
@@ -306,17 +311,17 @@ async fn test_symlinks_pointing_outside(cx: &mut TestAppContext) {
.map(|entry| (entry.path.as_ref(), entry.is_external))
.collect::<Vec<_>>(),
vec![
- (Path::new(""), false),
- (Path::new("deps"), false),
- (Path::new("deps/dep-dir2"), true),
- (Path::new("deps/dep-dir3"), true),
- (Path::new("deps/dep-dir3/deps"), true),
- (Path::new("deps/dep-dir3/src"), true),
- (Path::new("deps/dep-dir3/src/e.rs"), true),
- (Path::new("deps/dep-dir3/src/f.rs"), true),
- (Path::new("src"), false),
- (Path::new("src/a.rs"), false),
- (Path::new("src/b.rs"), false),
+ (RelPath::new(""), false),
+ (RelPath::new("deps"), false),
+ (RelPath::new("deps/dep-dir2"), true),
+ (RelPath::new("deps/dep-dir3"), true),
+ (RelPath::new("deps/dep-dir3/deps"), true),
+ (RelPath::new("deps/dep-dir3/src"), true),
+ (RelPath::new("deps/dep-dir3/src/e.rs"), true),
+ (RelPath::new("deps/dep-dir3/src/f.rs"), true),
+ (RelPath::new("src"), false),
+ (RelPath::new("src/a.rs"), false),
+ (RelPath::new("src/b.rs"), false),
]
);
});
@@ -324,13 +329,13 @@ async fn test_symlinks_pointing_outside(cx: &mut TestAppContext) {
assert_eq!(
mem::take(&mut *tree_updates.lock()),
&[
- (Path::new("deps/dep-dir3/src").into(), PathChange::Loaded),
+ (RelPath::new("deps/dep-dir3/src").into(), PathChange::Loaded),
(
- Path::new("deps/dep-dir3/src/e.rs").into(),
+ RelPath::new("deps/dep-dir3/src/e.rs").into(),
PathChange::Loaded
),
(
- Path::new("deps/dep-dir3/src/f.rs").into(),
+ RelPath::new("deps/dep-dir3/src/f.rs").into(),
PathChange::Loaded
)
]
@@ -368,7 +373,7 @@ async fn test_renaming_case_only(cx: &mut TestAppContext) {
tree.entries(true, 0)
.map(|entry| entry.path.as_ref())
.collect::<Vec<_>>(),
- vec![Path::new(""), Path::new(OLD_NAME)]
+ vec![RelPath::new(""), RelPath::new(OLD_NAME)]
);
});
@@ -390,7 +395,7 @@ async fn test_renaming_case_only(cx: &mut TestAppContext) {
tree.entries(true, 0)
.map(|entry| entry.path.as_ref())
.collect::<Vec<_>>(),
- vec![Path::new(""), Path::new(NEW_NAME)]
+ vec![RelPath::new(""), RelPath::new(NEW_NAME)]
);
});
}
@@ -446,13 +451,13 @@ async fn test_open_gitignored_files(cx: &mut TestAppContext) {
.map(|entry| (entry.path.as_ref(), entry.is_ignored))
.collect::<Vec<_>>(),
vec![
- (Path::new(""), false),
- (Path::new(".gitignore"), false),
- (Path::new("one"), false),
- (Path::new("one/node_modules"), true),
- (Path::new("two"), false),
- (Path::new("two/x.js"), false),
- (Path::new("two/y.js"), false),
+ (RelPath::new(""), false),
+ (RelPath::new(".gitignore"), false),
+ (RelPath::new("one"), false),
+ (RelPath::new("one/node_modules"), true),
+ (RelPath::new("two"), false),
+ (RelPath::new("two/x.js"), false),
+ (RelPath::new("two/y.js"), false),
]
);
});
@@ -473,24 +478,24 @@ async fn test_open_gitignored_files(cx: &mut TestAppContext) {
.map(|entry| (entry.path.as_ref(), entry.is_ignored))
.collect::<Vec<_>>(),
vec![
- (Path::new(""), false),
- (Path::new(".gitignore"), false),
- (Path::new("one"), false),
- (Path::new("one/node_modules"), true),
- (Path::new("one/node_modules/a"), true),
- (Path::new("one/node_modules/b"), true),
- (Path::new("one/node_modules/b/b1.js"), true),
- (Path::new("one/node_modules/b/b2.js"), true),
- (Path::new("one/node_modules/c"), true),
- (Path::new("two"), false),
- (Path::new("two/x.js"), false),
- (Path::new("two/y.js"), false),
+ (RelPath::new(""), false),
+ (RelPath::new(".gitignore"), false),
+ (RelPath::new("one"), false),
+ (RelPath::new("one/node_modules"), true),
+ (RelPath::new("one/node_modules/a"), true),
+ (RelPath::new("one/node_modules/b"), true),
+ (RelPath::new("one/node_modules/b/b1.js"), true),
+ (RelPath::new("one/node_modules/b/b2.js"), true),
+ (RelPath::new("one/node_modules/c"), true),
+ (RelPath::new("two"), false),
+ (RelPath::new("two/x.js"), false),
+ (RelPath::new("two/y.js"), false),
]
);
assert_eq!(
loaded.file.path.as_ref(),
- Path::new("one/node_modules/b/b1.js")
+ RelPath::new("one/node_modules/b/b1.js")
);
// Only the newly-expanded directories are scanned.
@@ -513,26 +518,26 @@ async fn test_open_gitignored_files(cx: &mut TestAppContext) {
.map(|entry| (entry.path.as_ref(), entry.is_ignored))
.collect::<Vec<_>>(),
vec![
- (Path::new(""), false),
- (Path::new(".gitignore"), false),
- (Path::new("one"), false),
- (Path::new("one/node_modules"), true),
- (Path::new("one/node_modules/a"), true),
- (Path::new("one/node_modules/a/a1.js"), true),
- (Path::new("one/node_modules/a/a2.js"), true),
- (Path::new("one/node_modules/b"), true),
- (Path::new("one/node_modules/b/b1.js"), true),
- (Path::new("one/node_modules/b/b2.js"), true),
- (Path::new("one/node_modules/c"), true),
- (Path::new("two"), false),
- (Path::new("two/x.js"), false),
- (Path::new("two/y.js"), false),
+ (RelPath::new(""), false),
+ (RelPath::new(".gitignore"), false),
+ (RelPath::new("one"), false),
+ (RelPath::new("one/node_modules"), true),
+ (RelPath::new("one/node_modules/a"), true),
+ (RelPath::new("one/node_modules/a/a1.js"), true),
+ (RelPath::new("one/node_modules/a/a2.js"), true),
+ (RelPath::new("one/node_modules/b"), true),
+ (RelPath::new("one/node_modules/b/b1.js"), true),
+ (RelPath::new("one/node_modules/b/b2.js"), true),
+ (RelPath::new("one/node_modules/c"), true),
+ (RelPath::new("two"), false),
+ (RelPath::new("two/x.js"), false),
+ (RelPath::new("two/y.js"), false),
]
);
assert_eq!(
loaded.file.path.as_ref(),
- Path::new("one/node_modules/a/a2.js")
+ RelPath::new("one/node_modules/a/a2.js")
);
// Only the newly-expanded directory is scanned.
@@ -592,7 +597,7 @@ async fn test_dirs_no_longer_ignored(cx: &mut TestAppContext) {
.await;
let tree = Worktree::local(
- Path::new("/root"),
+ RelPath::new("/root"),
true,
fs.clone(),
Default::default(),
@@ -610,7 +615,7 @@ async fn test_dirs_no_longer_ignored(cx: &mut TestAppContext) {
tree.read_with(cx, |tree, _| {
tree.as_local()
.unwrap()
- .refresh_entries_for_paths(vec![Path::new("node_modules/d/d.js").into()])
+ .refresh_entries_for_paths(vec![RelPath::new("node_modules/d/d.js").into()])
})
.recv()
.await;
@@ -622,18 +627,18 @@ async fn test_dirs_no_longer_ignored(cx: &mut TestAppContext) {
.map(|e| (e.path.as_ref(), e.is_ignored))
.collect::<Vec<_>>(),
&[
- (Path::new(""), false),
- (Path::new(".gitignore"), false),
- (Path::new("a"), false),
- (Path::new("a/a.js"), false),
- (Path::new("b"), false),
- (Path::new("b/b.js"), false),
- (Path::new("node_modules"), true),
- (Path::new("node_modules/c"), true),
- (Path::new("node_modules/d"), true),
- (Path::new("node_modules/d/d.js"), true),
- (Path::new("node_modules/d/e"), true),
- (Path::new("node_modules/d/f"), true),
+ (RelPath::new(""), false),
+ (RelPath::new(".gitignore"), false),
+ (RelPath::new("a"), false),
+ (RelPath::new("a/a.js"), false),
+ (RelPath::new("b"), false),
+ (RelPath::new("b/b.js"), false),
+ (RelPath::new("node_modules"), true),
+ (RelPath::new("node_modules/c"), true),
+ (RelPath::new("node_modules/d"), true),
+ (RelPath::new("node_modules/d/d.js"), true),
+ (RelPath::new("node_modules/d/e"), true),
+ (RelPath::new("node_modules/d/f"), true),
]
);
});
@@ -654,23 +659,23 @@ async fn test_dirs_no_longer_ignored(cx: &mut TestAppContext) {
.map(|e| (e.path.as_ref(), e.is_ignored))
.collect::<Vec<_>>(),
&[
- (Path::new(""), false),
- (Path::new(".gitignore"), false),
- (Path::new("a"), false),
- (Path::new("a/a.js"), false),
- (Path::new("b"), false),
- (Path::new("b/b.js"), false),
+ (RelPath::new(""), false),
+ (RelPath::new(".gitignore"), false),
+ (RelPath::new("a"), false),
+ (RelPath::new("a/a.js"), false),
+ (RelPath::new("b"), false),
+ (RelPath::new("b/b.js"), false),
// This directory is no longer ignored
- (Path::new("node_modules"), false),
- (Path::new("node_modules/c"), false),
- (Path::new("node_modules/c/c.js"), false),
- (Path::new("node_modules/d"), false),
- (Path::new("node_modules/d/d.js"), false),
+ (RelPath::new("node_modules"), false),
+ (RelPath::new("node_modules/c"), false),
+ (RelPath::new("node_modules/c/c.js"), false),
+ (RelPath::new("node_modules/d"), false),
+ (RelPath::new("node_modules/d/d.js"), false),
// This subdirectory is now ignored
- (Path::new("node_modules/d/e"), true),
- (Path::new("node_modules/d/f"), false),
- (Path::new("node_modules/d/f/f1.js"), false),
- (Path::new("node_modules/d/f/f2.js"), false),
+ (RelPath::new("node_modules/d/e"), true),
+ (RelPath::new("node_modules/d/f"), false),
+ (RelPath::new("node_modules/d/f/f1.js"), false),
+ (RelPath::new("node_modules/d/f/f2.js"), false),
]
);
});
@@ -711,7 +716,7 @@ async fn test_write_file(cx: &mut TestAppContext) {
worktree
.update(cx, |tree, cx| {
tree.write_file(
- Path::new("tracked-dir/file.txt"),
+ RelPath::new("tracked-dir/file.txt"),
"hello".into(),
Default::default(),
cx,
@@ -722,7 +727,7 @@ async fn test_write_file(cx: &mut TestAppContext) {
worktree
.update(cx, |tree, cx| {
tree.write_file(
- Path::new("ignored-dir/file.txt"),
+ RelPath::new("ignored-dir/file.txt"),
"world".into(),
Default::default(),
cx,
@@ -1421,7 +1426,7 @@ async fn test_random_worktree_operations_during_initial_scan(
.map(|o| o.parse().unwrap())
.unwrap_or(20);
- let root_dir = Path::new(path!("/test"));
+ let root_dir = RelPath::new(path!("/test"));
let fs = FakeFs::new(cx.background_executor.clone()) as Arc<dyn Fs>;
fs.as_fake().insert_tree(root_dir, json!({})).await;
for _ in 0..initial_entries {
@@ -1512,7 +1517,7 @@ async fn test_random_worktree_changes(cx: &mut TestAppContext, mut rng: StdRng)
.map(|o| o.parse().unwrap())
.unwrap_or(20);
- let root_dir = Path::new(path!("/test"));
+ let root_dir = RelPath::new(path!("/test"));
let fs = FakeFs::new(cx.background_executor.clone()) as Arc<dyn Fs>;
fs.as_fake().insert_tree(root_dir, json!({})).await;
for _ in 0..initial_entries {
@@ -1702,11 +1707,11 @@ fn randomly_mutate_worktree(
let entry = snapshot.entries(false, 0).choose(rng).unwrap();
match rng.gen_range(0_u32..100) {
- 0..=33 if entry.path.as_ref() != Path::new("") => {
+ 0..=33 if entry.path.as_ref() != RelPath::new("") => {
log::info!("deleting entry {:?} ({})", entry.path, entry.id.0);
worktree.delete_entry(entry.id, false, cx).unwrap()
}
- ..=66 if entry.path.as_ref() != Path::new("") => {
+ ..=66 if entry.path.as_ref() != RelPath::new("") => {
let other_entry = snapshot.entries(false, 0).choose(rng).unwrap();
let new_parent_path = if other_entry.is_dir() {
other_entry.path.clone()
@@ -1759,7 +1764,7 @@ fn randomly_mutate_worktree(
async fn randomly_mutate_fs(
fs: &Arc<dyn Fs>,
- root_path: &Path,
+ root_path: &RelPath,
insertion_probability: f64,
rng: &mut impl Rng,
) {
@@ -1931,7 +1936,7 @@ async fn test_private_single_file_worktree(cx: &mut TestAppContext) {
fs.insert_tree("/", json!({".env": "PRIVATE=secret\n"}))
.await;
let tree = Worktree::local(
- Path::new("/.env"),
+ RelPath::new("/.env"),
true,
fs.clone(),
Default::default(),
@@ -1952,18 +1957,18 @@ fn test_unrelativize() {
let work_directory = WorkDirectory::in_project("");
pretty_assertions::assert_eq!(
work_directory.try_unrelativize(&"crates/gpui/gpui.rs".into()),
- Some(Path::new("crates/gpui/gpui.rs").into())
+ Some(RelPath::new("crates/gpui/gpui.rs").into())
);
let work_directory = WorkDirectory::in_project("vendor/some-submodule");
pretty_assertions::assert_eq!(
work_directory.try_unrelativize(&"src/thing.c".into()),
- Some(Path::new("vendor/some-submodule/src/thing.c").into())
+ Some(RelPath::new("vendor/some-submodule/src/thing.c").into())
);
let work_directory = WorkDirectory::AboveProject {
- absolute_path: Path::new("/projects/zed").into(),
- location_in_repo: Path::new("crates/gpui").into(),
+ absolute_path: RelPath::new("/projects/zed").into(),
+ location_in_repo: RelPath::new("crates/gpui").into(),
};
pretty_assertions::assert_eq!(
@@ -1973,14 +1978,14 @@ fn test_unrelativize() {
pretty_assertions::assert_eq!(
work_directory.unrelativize(&"crates/util/util.rs".into()),
- Path::new("../util/util.rs").into()
+ RelPath::new("../util/util.rs").into()
);
pretty_assertions::assert_eq!(work_directory.try_unrelativize(&"README.md".into()), None,);
pretty_assertions::assert_eq!(
work_directory.unrelativize(&"README.md".into()),
- Path::new("../../README.md").into()
+ RelPath::new("../../README.md").into()
);
}
@@ -2023,7 +2028,7 @@ async fn test_repository_above_root(executor: BackgroundExecutor, cx: &mut TestA
.map(|entry| entry.work_directory_abs_path.clone())
.collect::<Vec<_>>()
});
- pretty_assertions::assert_eq!(repos, [Path::new(path!("/root")).into()]);
+ pretty_assertions::assert_eq!(repos, [RelPath::new(path!("/root")).into()]);
eprintln!(">>>>>>>>>> touch");
fs.touch_path(path!("/root/subproject")).await;
@@ -2043,7 +2048,7 @@ async fn test_repository_above_root(executor: BackgroundExecutor, cx: &mut TestA
.map(|entry| entry.work_directory_abs_path.clone())
.collect::<Vec<_>>()
});
- pretty_assertions::assert_eq!(repos, [Path::new(path!("/root")).into()]);
+ pretty_assertions::assert_eq!(repos, [RelPath::new(path!("/root")).into()]);
}
#[track_caller]