fix: correct rename on unix platforms caused by pathdiff#8 (#1587)

* fix: correct rename on unix platforms caused by pathdiff#8

* fix: ensure all calls to pathdiff

* fix: names

* fix: file path on windows
This commit is contained in:
Myriad-Dreamin 2025-03-26 12:46:33 +08:00 committed by GitHub
parent c102ace9ab
commit e4a4fc568f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 44 additions and 28 deletions

5
Cargo.lock generated
View file

@ -4044,7 +4044,6 @@ dependencies = [
"open", "open",
"parking_lot", "parking_lot",
"paste", "paste",
"pathdiff",
"rayon", "rayon",
"reflexo", "reflexo",
"reflexo-typst", "reflexo-typst",
@ -4203,7 +4202,6 @@ dependencies = [
"log", "log",
"notify", "notify",
"parking_lot", "parking_lot",
"pathdiff",
"rayon", "rayon",
"rpds", "rpds",
"semver", "semver",
@ -4243,7 +4241,6 @@ dependencies = [
"lsp-types", "lsp-types",
"once_cell", "once_cell",
"parking_lot", "parking_lot",
"pathdiff",
"percent-encoding", "percent-encoding",
"rayon", "rayon",
"regex", "regex",
@ -4307,6 +4304,7 @@ dependencies = [
"lsp-types", "lsp-types",
"parking_lot", "parking_lot",
"path-clean", "path-clean",
"pathdiff",
"rkyv", "rkyv",
"rustc-hash 2.1.1", "rustc-hash 2.1.1",
"same-file", "same-file",
@ -4337,7 +4335,6 @@ dependencies = [
"log", "log",
"notify", "notify",
"parking_lot", "parking_lot",
"pathdiff",
"rayon", "rayon",
"rpds", "rpds",
"semver", "semver",

View file

@ -21,7 +21,6 @@ dirs.workspace = true
ecow.workspace = true ecow.workspace = true
log.workspace = true log.workspace = true
parking_lot.workspace = true parking_lot.workspace = true
pathdiff.workspace = true
tokio = { workspace = true, features = ["sync"] } tokio = { workspace = true, features = ["sync"] }
rayon.workspace = true rayon.workspace = true
rpds.workspace = true rpds.workspace = true

View file

@ -45,7 +45,6 @@ lsp-types.workspace = true
if_chain.workspace = true if_chain.workspace = true
percent-encoding.workspace = true percent-encoding.workspace = true
unscanny.workspace = true unscanny.workspace = true
pathdiff.workspace = true
ttf-parser.workspace = true ttf-parser.workspace = true
rust_iso639.workspace = true rust_iso639.workspace = true
rust_iso3166.workspace = true rust_iso3166.workspace = true

View file

@ -78,7 +78,7 @@ impl CompletionPair<'_, '_, '_> {
.parent() .parent()
.unwrap_or(Path::new("/")); .unwrap_or(Path::new("/"));
let path = path.vpath().as_rooted_path(); let path = path.vpath().as_rooted_path();
let w = pathdiff::diff_paths(path, base)?; let w = tinymist_std::path::diff(path, base)?;
unix_slash(&w).into() unix_slash(&w).into()
}; };
crate::log_debug_ct!("compl_label: {label:?}"); crate::log_debug_ct!("compl_label: {label:?}");

View file

@ -9,6 +9,6 @@ snapshot_kind: text
"originSelectionRange": "1:20:1:27", "originSelectionRange": "1:20:1:27",
"targetRange": "0:0:0:0", "targetRange": "0:0:0:0",
"targetSelectionRange": "0:0:0:0", "targetSelectionRange": "0:0:0:0",
"targetUri": "lib.typ" "targetUri": "-/lib.typ"
} }
] ]

View file

@ -9,6 +9,6 @@ snapshot_kind: text
"originSelectionRange": "1:20:1:27", "originSelectionRange": "1:20:1:27",
"targetRange": "0:0:0:0", "targetRange": "0:0:0:0",
"targetSelectionRange": "0:0:0:0", "targetSelectionRange": "0:0:0:0",
"targetUri": "lib.typ" "targetUri": "-/lib.typ"
} }
] ]

View file

@ -19,7 +19,7 @@ input_file: crates/tinymist-query/src/fixtures/rename/module_path.typ
}, },
{ {
"kind": "rename", "kind": "rename",
"newUri": "variable.typ/../new_name.typ", "newUri": "new_name.typ",
"oldUri": "variable.typ" "oldUri": "variable.typ"
} }
] ]

View file

@ -19,7 +19,7 @@ input_file: crates/tinymist-query/src/fixtures/rename/module_path_alias.typ
}, },
{ {
"kind": "rename", "kind": "rename",
"newUri": "variable.typ/../new_name.typ", "newUri": "new_name.typ",
"oldUri": "variable.typ" "oldUri": "variable.typ"
} }
] ]

View file

@ -8,7 +8,7 @@ input_file: crates/tinymist-query/src/fixtures/rename/module_path_non_cano.typ
{ {
"edits": [ "edits": [
{ {
"newText": "\"../../new_name.typ\" as variable", "newText": "\"new_name.typ\" as variable",
"range": "0:29:0:51" "range": "0:29:0:51"
} }
], ],
@ -19,7 +19,7 @@ input_file: crates/tinymist-query/src/fixtures/rename/module_path_non_cano.typ
}, },
{ {
"kind": "rename", "kind": "rename",
"newUri": "variable.typ/../../../new_name.typ", "newUri": "new_name.typ",
"oldUri": "variable.typ" "oldUri": "variable.typ"
} }
] ]

View file

@ -19,7 +19,7 @@ input_file: crates/tinymist-query/src/fixtures/rename/module_path_star.typ
}, },
{ {
"kind": "rename", "kind": "rename",
"newUri": "variable.typ/../new_name.typ", "newUri": "new_name.typ",
"oldUri": "variable.typ" "oldUri": "variable.typ"
} }
] ]

View file

@ -23,7 +23,7 @@ input_file: crates/tinymist-query/src/fixtures/rename/resources.typ
}, },
{ {
"kind": "rename", "kind": "rename",
"newUri": "lib.typ/../new_name.typ", "newUri": "new_name.typ",
"oldUri": "lib.typ" "oldUri": "lib.typ"
} }
] ]

View file

@ -60,14 +60,15 @@ impl StatefulRequest for RenameRequest {
// todo: rename in untitled files // todo: rename in untitled files
let old_path = ctx.path_for_id(def_fid).ok()?.to_err().ok()?; let old_path = ctx.path_for_id(def_fid).ok()?.to_err().ok()?;
let new_path = Path::new(new_path_str.as_str());
let rename_loc = Path::new(ref_path_str.as_str()); let rename_loc = Path::new(ref_path_str.as_str());
let diff = pathdiff::diff_paths(Path::new(&new_path_str), rename_loc)?; let diff = tinymist_std::path::diff(new_path, rename_loc)?;
if diff.is_absolute() { if diff.is_absolute() {
log::info!("bad rename: absolute path, base: {rename_loc:?}, new: {new_path_str}, diff: {diff:?}"); log::info!("bad rename: absolute path, base: {rename_loc:?}, new: {new_path:?}, diff: {diff:?}");
return None; return None;
} }
let new_path = old_path.join(&diff); let new_path = old_path.join(&diff).clean();
let old_uri = path_to_url(&old_path).ok()?; let old_uri = path_to_url(&old_path).ok()?;
let new_uri = path_to_url(&new_path).ok()?; let new_uri = path_to_url(&new_path).ok()?;

View file

@ -465,10 +465,12 @@ pub(crate) fn file_path(uri: &str) -> String {
} else { } else {
PathBuf::from("/root") PathBuf::from("/root")
}; };
let uri = uri.replace("file://", ""); let uri = lsp_types::Url::parse(uri).unwrap().to_file_path().unwrap();
let abs_path = Path::new(&uri).strip_prefix(root).map(|s| s.as_os_str()); let abs_path = Path::new(&uri).strip_prefix(root).map(|p| p.to_owned());
let rel_path = abs_path.unwrap_or_else(|_| Path::new(&uri).file_name().unwrap()); let rel_path =
unix_slash(Path::new(rel_path.to_str().unwrap())) abs_path.unwrap_or_else(|_| Path::new("-").join(Path::new(&uri).iter().last().unwrap()));
unix_slash(&rel_path)
} }
pub struct HashRepr<T>(pub T); pub struct HashRepr<T>(pub T);

View file

@ -21,7 +21,7 @@ impl StatefulRequest for WillRenameFilesRequest {
self.paths self.paths
.into_iter() .into_iter()
.map(|(left, right)| { .map(|(left, right)| {
let diff = pathdiff::diff_paths(&right, &left)?; let diff = tinymist_std::path::diff(&right, &left)?;
log::info!("did rename diff: {diff:?}"); log::info!("did rename diff: {diff:?}");
if diff.is_absolute() { if diff.is_absolute() {
log::info!( log::info!(

View file

@ -21,6 +21,7 @@ fxhash.workspace = true
log.workspace = true log.workspace = true
tinymist-analysis.workspace = true tinymist-analysis.workspace = true
path-clean.workspace = true path-clean.workspace = true
pathdiff.workspace = true
parking_lot.workspace = true parking_lot.workspace = true
rustc-hash.workspace = true rustc-hash.workspace = true
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }

View file

@ -1,6 +1,7 @@
//! Path utilities. //! Path utilities.
use std::path::{Component, Path}; use std::borrow::Cow;
use std::path::{Component, Path, PathBuf};
pub use path_clean::PathClean; pub use path_clean::PathClean;
@ -48,6 +49,24 @@ pub fn unix_slash(root: &Path) -> String {
/// Get the path cleaned as a platform-style string. /// Get the path cleaned as a platform-style string.
pub use path_clean::clean; pub use path_clean::clean;
/// Construct a relative path from a provided base directory path to the
/// provided path.
pub fn diff(fr: &Path, to: &Path) -> Option<PathBuf> {
// Because of <https://github.com/Manishearth/pathdiff/issues/8>, we have to clean the path
// before diff.
fn clean_for_diff(p: &Path) -> Cow<'_, Path> {
if p.components()
.any(|c| matches!(c, Component::ParentDir | Component::CurDir))
{
Cow::Owned(p.clean())
} else {
Cow::Borrowed(p)
}
}
pathdiff::diff_paths(clean_for_diff(fr).as_ref(), clean_for_diff(to).as_ref())
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};

View file

@ -21,7 +21,6 @@ dirs.workspace = true
ecow.workspace = true ecow.workspace = true
log.workspace = true log.workspace = true
parking_lot.workspace = true parking_lot.workspace = true
pathdiff.workspace = true
tokio = { workspace = true, features = ["sync"] } tokio = { workspace = true, features = ["sync"] }
rayon.workspace = true rayon.workspace = true
rpds.workspace = true rpds.workspace = true

View file

@ -405,7 +405,7 @@ impl ResourcePath {
inp.to_path_buf() inp.to_path_buf()
} else { } else {
let cwd = std::env::current_dir().unwrap(); let cwd = std::env::current_dir().unwrap();
pathdiff::diff_paths(inp, &cwd).unwrap() tinymist_std::path::diff(inp, &cwd).unwrap()
}; };
let rel = unix_slash(&rel); let rel = unix_slash(&rel);
ResourcePath("file".into(), rel.to_string()) ResourcePath("file".into(), rel.to_string())
@ -431,7 +431,7 @@ impl ResourcePath {
if self.0 == "file" { if self.0 == "file" {
let path = Path::new(&self.1); let path = Path::new(&self.1);
if path.is_absolute() { if path.is_absolute() {
Some(pathdiff::diff_paths(path, base).unwrap_or_else(|| path.to_owned())) Some(tinymist_std::path::diff(path, base).unwrap_or_else(|| path.to_owned()))
} else { } else {
Some(path.to_owned()) Some(path.to_owned())
} }

View file

@ -44,7 +44,6 @@ lsp-types.workspace = true
log.workspace = true log.workspace = true
once_cell.workspace = true once_cell.workspace = true
open.workspace = true open.workspace = true
pathdiff.workspace = true
parking_lot.workspace = true parking_lot.workspace = true
paste.workspace = true paste.workspace = true
rayon.workspace = true rayon.workspace = true