Compare commits
2 commits
main
...
rel-path-t
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cee726f86b | ||
![]() |
d8b791d3a6 |
13 changed files with 452 additions and 285 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -12871,6 +12871,7 @@ dependencies = [
|
|||
"prost-build 0.9.0",
|
||||
"serde",
|
||||
"typed-path",
|
||||
"util",
|
||||
"workspace-hack",
|
||||
]
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ use ignore::gitignore::GitignoreBuilder;
|
|||
use rope::Rope;
|
||||
use smol::future::FutureExt as _;
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
use util::rel_path::RelPath;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FakeGitRepository {
|
||||
|
@ -222,7 +223,10 @@ impl GitRepository for FakeGitRepository {
|
|||
.read_file_sync(path)
|
||||
.ok()
|
||||
.map(|content| String::from_utf8(content).unwrap())?;
|
||||
Some((repo_path.into(), (content, is_ignored)))
|
||||
Some((
|
||||
RepoPath::from(&RelPath::new(repo_path)),
|
||||
(content, is_ignored),
|
||||
))
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::commit::get_messages;
|
||||
use crate::repository::RepoPath;
|
||||
use crate::{GitRemote, Oid};
|
||||
use anyhow::{Context as _, Result};
|
||||
use collections::{HashMap, HashSet};
|
||||
|
@ -33,7 +34,7 @@ impl Blame {
|
|||
pub async fn for_path(
|
||||
git_binary: &Path,
|
||||
working_directory: &Path,
|
||||
path: &Path,
|
||||
path: &RepoPath,
|
||||
content: &Rope,
|
||||
remote_url: Option<String>,
|
||||
) -> Result<Self> {
|
||||
|
@ -66,7 +67,7 @@ const GIT_BLAME_NO_PATH: &str = "fatal: no such path";
|
|||
async fn run_git_blame(
|
||||
git_binary: &Path,
|
||||
working_directory: &Path,
|
||||
path: &Path,
|
||||
path: &RepoPath,
|
||||
contents: &Rope,
|
||||
) -> Result<String> {
|
||||
let mut child = util::command::new_smol_command(git_binary)
|
||||
|
|
Binary file not shown.
|
@ -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,
|
||||
}
|
||||
|
@ -765,7 +774,7 @@ impl GitRepository for RealGitRepository {
|
|||
.current_dir(&working_directory?)
|
||||
.envs(env.iter())
|
||||
.args(["checkout", &commit, "--"])
|
||||
.args(paths.iter().map(|path| path.as_ref()))
|
||||
.args(paths.iter().map(|path| path.to_unix_style()))
|
||||
.output()
|
||||
.await?;
|
||||
anyhow::ensure!(
|
||||
|
@ -787,13 +796,14 @@ impl GitRepository for RealGitRepository {
|
|||
.spawn(async move {
|
||||
fn logic(repo: &git2::Repository, path: &RepoPath) -> Result<Option<String>> {
|
||||
// This check is required because index.get_path() unwraps internally :(
|
||||
check_path_to_repo_path_errors(path)?;
|
||||
// TODO: move this function to where we instantiate the repopaths
|
||||
// check_path_to_repo_path_errors(path)?;
|
||||
|
||||
let mut index = repo.index()?;
|
||||
index.read(false)?;
|
||||
|
||||
const STAGE_NORMAL: i32 = 0;
|
||||
let oid = match index.get_path(path, STAGE_NORMAL) {
|
||||
let oid = match index.get_path(Path::new(&path.to_unix_style()), STAGE_NORMAL) {
|
||||
Some(entry) if entry.mode != GIT_MODE_SYMLINK => entry.id,
|
||||
_ => return Ok(None),
|
||||
};
|
||||
|
@ -817,7 +827,7 @@ impl GitRepository for RealGitRepository {
|
|||
.spawn(async move {
|
||||
let repo = repo.lock();
|
||||
let head = repo.head().ok()?.peel_to_tree().log_err()?;
|
||||
let entry = head.get_path(&path).ok()?;
|
||||
let entry = head.get_path(Path::new(&path.as_os_str())).ok()?;
|
||||
if entry.filemode() == i32::from(git2::FileMode::Link) {
|
||||
return None;
|
||||
}
|
||||
|
@ -1184,7 +1194,7 @@ impl GitRepository for RealGitRepository {
|
|||
.current_dir(&working_directory?)
|
||||
.envs(env.iter())
|
||||
.args(["reset", "--quiet", "--"])
|
||||
.args(paths.iter().map(|p| p.as_ref()))
|
||||
.args(paths.iter().map(|p| p.to_unix_style()))
|
||||
.output()
|
||||
.await?;
|
||||
|
||||
|
@ -1213,7 +1223,7 @@ impl GitRepository for RealGitRepository {
|
|||
.args(["stash", "push", "--quiet"])
|
||||
.arg("--include-untracked");
|
||||
|
||||
cmd.args(paths.iter().map(|p| p.as_ref()));
|
||||
cmd.args(paths.iter().map(|p| p.to_unix_style()));
|
||||
|
||||
let output = cmd.output().await?;
|
||||
|
||||
|
@ -1652,7 +1662,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 +1915,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<&RelPath> for RepoPath {
|
||||
fn from(value: &RelPath) -> Self {
|
||||
RepoPath(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Path> for RepoPath {
|
||||
fn from(value: &Path) -> Self {
|
||||
RepoPath::new(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 +1950,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 {
|
||||
|
@ -2080,35 +2059,6 @@ fn parse_upstream_track(upstream_track: &str) -> Result<UpstreamTracking> {
|
|||
}))
|
||||
}
|
||||
|
||||
fn check_path_to_repo_path_errors(relative_file_path: &Path) -> Result<()> {
|
||||
match relative_file_path.components().next() {
|
||||
None => anyhow::bail!("repo path should not be empty"),
|
||||
Some(Component::Prefix(_)) => anyhow::bail!(
|
||||
"repo path `{}` should be relative, not a windows prefix",
|
||||
relative_file_path.to_string_lossy()
|
||||
),
|
||||
Some(Component::RootDir) => {
|
||||
anyhow::bail!(
|
||||
"repo path `{}` should be relative",
|
||||
relative_file_path.to_string_lossy()
|
||||
)
|
||||
}
|
||||
Some(Component::CurDir) => {
|
||||
anyhow::bail!(
|
||||
"repo path `{}` should not start with `.`",
|
||||
relative_file_path.to_string_lossy()
|
||||
)
|
||||
}
|
||||
Some(Component::ParentDir) => {
|
||||
anyhow::bail!(
|
||||
"repo path `{}` should not start with `..`",
|
||||
relative_file_path.to_string_lossy()
|
||||
)
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn checkpoint_author_envs() -> HashMap<String, String> {
|
||||
HashMap::from_iter([
|
||||
("GIT_AUTHOR_NAME".to_string(), "Zed".to_string()),
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::repository::RepoPath;
|
||||
use anyhow::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{path::Path, str::FromStr, sync::Arc};
|
||||
use util::ResultExt;
|
||||
use std::{str::FromStr, sync::Arc};
|
||||
use util::{ResultExt, rel_path::RelPath};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum FileStatus {
|
||||
|
@ -464,7 +464,7 @@ impl FromStr for GitStatus {
|
|||
}
|
||||
let status = entry.as_bytes()[0..2].try_into().unwrap();
|
||||
let status = FileStatus::from_bytes(status).log_err()?;
|
||||
let path = RepoPath(Path::new(path).into());
|
||||
let path = RepoPath(RelPath::new(path.as_bytes()).into());
|
||||
Some((path, status))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
|
|
@ -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)
|
||||
|
|
206
crates/util/src/rel_path.rs
Normal file
206
crates/util/src/rel_path.rs
Normal file
|
@ -0,0 +1,206 @@
|
|||
use std::{
|
||||
borrow::Cow,
|
||||
ffi::OsStr,
|
||||
os::unix::ffi::OsStrExt,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
|
||||
#[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 from_path(relative_path: &Path) -> Result<&Self> {
|
||||
use std::path::Component;
|
||||
match relative_path.components().next() {
|
||||
Some(Component::Prefix(_)) => bail!(
|
||||
"path `{}` should be relative, not a windows prefix",
|
||||
relative_path.to_string_lossy()
|
||||
),
|
||||
Some(Component::RootDir) => {
|
||||
bail!(
|
||||
"path `{}` should be relative",
|
||||
relative_path.to_string_lossy()
|
||||
)
|
||||
}
|
||||
Some(Component::CurDir) => {
|
||||
bail!(
|
||||
"path `{}` should not start with `.`",
|
||||
relative_path.to_string_lossy()
|
||||
)
|
||||
}
|
||||
Some(Component::ParentDir) => {
|
||||
bail!(
|
||||
"path `{}` should not start with `..`",
|
||||
relative_path.to_string_lossy()
|
||||
)
|
||||
}
|
||||
None => bail!("relative path should not be empty"),
|
||||
_ => Ok(Self::new(relative_path.as_os_str().as_bytes())),
|
||||
}
|
||||
}
|
||||
|
||||
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"))]
|
||||
{
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
Cow::Borrowed(OsStr::from_bytes(&self.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -5226,7 +5217,7 @@ impl<'a> sum_tree::Dimension<'a, EntrySummary> for TraversalProgress<'a> {
|
|||
impl Default for TraversalProgress<'_> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
max_path: Path::new(""),
|
||||
max_path: RelPath::new(""),
|
||||
count: 0,
|
||||
non_ignored_count: 0,
|
||||
file_count: 0,
|
||||
|
@ -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]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue