-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
make path changes LSP spec conform (#8949)
Currently, helix implements operations which change the paths of files incorrectly and inconsistently. This PR ensures that we do the following whenever a buffer is renamed (`:move` and workspace edits) * always send did_open/did_close notifications * send will_rename/did_rename requests correctly * send them to all LSP servers not just those that are active for a buffer * also send these requests for paths that are not yet open in a buffer (if triggered from workspace edit). * only send these if the server registered interests in the path * autodetect language, indent, line ending, .. This PR also centralizes the infrastructure for path setting and therefore `:w <path>` benefits from similar fixed (but without didRename)
- Loading branch information
1 parent
f5b67d9
commit 87a720c
Showing
9 changed files
with
483 additions
and
319 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
use std::path::Path; | ||
|
||
use globset::{GlobBuilder, GlobSet}; | ||
|
||
use crate::lsp; | ||
|
||
#[derive(Default, Debug)] | ||
pub(crate) struct FileOperationFilter { | ||
dir_globs: GlobSet, | ||
file_globs: GlobSet, | ||
} | ||
|
||
impl FileOperationFilter { | ||
fn new(capability: Option<&lsp::FileOperationRegistrationOptions>) -> FileOperationFilter { | ||
let Some(cap) = capability else { | ||
return FileOperationFilter::default(); | ||
}; | ||
let mut dir_globs = GlobSet::builder(); | ||
let mut file_globs = GlobSet::builder(); | ||
for filter in &cap.filters { | ||
// TODO: support other url schemes | ||
let is_non_file_schema = filter | ||
.scheme | ||
.as_ref() | ||
.is_some_and(|schema| schema != "file"); | ||
if is_non_file_schema { | ||
continue; | ||
} | ||
let ignore_case = filter | ||
.pattern | ||
.options | ||
.as_ref() | ||
.and_then(|opts| opts.ignore_case) | ||
.unwrap_or(false); | ||
let mut glob_builder = GlobBuilder::new(&filter.pattern.glob); | ||
glob_builder.case_insensitive(!ignore_case); | ||
let glob = match glob_builder.build() { | ||
Ok(glob) => glob, | ||
Err(err) => { | ||
log::error!("invalid glob send by LS: {err}"); | ||
continue; | ||
} | ||
}; | ||
match filter.pattern.matches { | ||
Some(lsp::FileOperationPatternKind::File) => { | ||
file_globs.add(glob); | ||
} | ||
Some(lsp::FileOperationPatternKind::Folder) => { | ||
dir_globs.add(glob); | ||
} | ||
None => { | ||
file_globs.add(glob.clone()); | ||
dir_globs.add(glob); | ||
} | ||
}; | ||
} | ||
let file_globs = file_globs.build().unwrap_or_else(|err| { | ||
log::error!("invalid globs send by LS: {err}"); | ||
GlobSet::empty() | ||
}); | ||
let dir_globs = dir_globs.build().unwrap_or_else(|err| { | ||
log::error!("invalid globs send by LS: {err}"); | ||
GlobSet::empty() | ||
}); | ||
FileOperationFilter { | ||
dir_globs, | ||
file_globs, | ||
} | ||
} | ||
|
||
pub(crate) fn has_interest(&self, path: &Path, is_dir: bool) -> bool { | ||
if is_dir { | ||
self.dir_globs.is_match(path) | ||
} else { | ||
self.file_globs.is_match(path) | ||
} | ||
} | ||
} | ||
|
||
#[derive(Default, Debug)] | ||
pub(crate) struct FileOperationsInterest { | ||
// TODO: support other notifications | ||
// did_create: FileOperationFilter, | ||
// will_create: FileOperationFilter, | ||
pub did_rename: FileOperationFilter, | ||
pub will_rename: FileOperationFilter, | ||
// did_delete: FileOperationFilter, | ||
// will_delete: FileOperationFilter, | ||
} | ||
|
||
impl FileOperationsInterest { | ||
pub fn new(capabilities: &lsp::ServerCapabilities) -> FileOperationsInterest { | ||
let capabilities = capabilities | ||
.workspace | ||
.as_ref() | ||
.and_then(|capabilities| capabilities.file_operations.as_ref()); | ||
let Some(capabilities) = capabilities else { | ||
return FileOperationsInterest::default(); | ||
}; | ||
FileOperationsInterest { | ||
did_rename: FileOperationFilter::new(capabilities.did_rename.as_ref()), | ||
will_rename: FileOperationFilter::new(capabilities.will_rename.as_ref()), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
mod client; | ||
pub mod file_event; | ||
mod file_operations; | ||
pub mod jsonrpc; | ||
pub mod snippet; | ||
mod transport; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.