Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #15940 - pascalkuthe:fix_rename, r=Veykril
ensure renames happen after edit This is a bugfix for an issue I fould while working on helix. Rust-analyzer currently always sends any filesystem edits (rename/file creation) before any other edits. When renaming a file that is also being edited that would mean that the edit would be discarded and therefore an incomplete/incorrect refactor (or even cause the creation of a new file in helix altough that is probably a pub on our side). Example: * create a module: `mod foo` containing a `pub sturct Bar;` * reexport the struct uneder a different name in the `foo` module using a *fully qualified path*: `pub use crate::foo::Bar as Bar2`. * rename the `foo` module to `foo2` using rust-analyzer * obsereve that the path is not correctly updated (rust-analyer first sends a rename `foo.rs` to `foo2.rs` and then edits `foo.rs` after) This PR fixes that issue by simply executing all rename operations after all edit operations (while still executing file creation operations first). I also added a testcase similar to the example above. Relevent excerpt from the LSP standard: > Since version 3.13.0 a workspace edit can contain resource operations (create, delete or rename files and folders) as well. If resource operations are present clients need to execute the operations in the order in which they are provided. So a workspace edit for example can consist of the following two changes: (1) create file a.txt and (2) a text document edit which insert text into file a.txt. An invalid sequence (e.g. (1) delete file a.txt and (2) insert text into file a.txt) will cause failure of the operation. How the client recovers from the failure is described by the client capability: workspace.workspaceEdit.failureHandling
bors 2023-11-21
parent 1a5cee1 · parent 0647b64 · commit 2e7e8cc
-rw-r--r--crates/rust-analyzer/src/lsp/to_proto.rs23
-rw-r--r--crates/rust-analyzer/tests/slow-tests/main.rs26
2 files changed, 44 insertions, 5 deletions
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs
index aca91570f7..4e3dce35ec 100644
--- a/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -1,7 +1,7 @@
//! Conversion of rust-analyzer specific types to lsp_types equivalents.
use std::{
iter::once,
- path,
+ mem, path,
sync::atomic::{AtomicU32, Ordering},
};
@@ -1123,13 +1123,20 @@ pub(crate) fn snippet_text_document_ops(
pub(crate) fn snippet_workspace_edit(
snap: &GlobalStateSnapshot,
- source_change: SourceChange,
+ mut source_change: SourceChange,
) -> Cancellable<lsp_ext::SnippetWorkspaceEdit> {
let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new();
- for op in source_change.file_system_edits {
- let ops = snippet_text_document_ops(snap, op)?;
- document_changes.extend_from_slice(&ops);
+ for op in &mut source_change.file_system_edits {
+ if let FileSystemEdit::CreateFile { dst, initial_contents } = op {
+ // replace with a placeholder to avoid cloneing the edit
+ let op = FileSystemEdit::CreateFile {
+ dst: dst.clone(),
+ initial_contents: mem::take(initial_contents),
+ };
+ let ops = snippet_text_document_ops(snap, op)?;
+ document_changes.extend_from_slice(&ops);
+ }
}
for (file_id, (edit, snippet_edit)) in source_change.source_file_edits {
let edit = snippet_text_document_edit(
@@ -1141,6 +1148,12 @@ pub(crate) fn snippet_workspace_edit(
)?;
document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit));
}
+ for op in source_change.file_system_edits {
+ if !matches!(op, FileSystemEdit::CreateFile { .. }) {
+ let ops = snippet_text_document_ops(snap, op)?;
+ document_changes.extend_from_slice(&ops);
+ }
+ }
let mut workspace_edit = lsp_ext::SnippetWorkspaceEdit {
changes: None,
document_changes: Some(document_changes),
diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs
index d599142989..5cd02f7840 100644
--- a/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -984,6 +984,11 @@ fn main() {}
//- /src/old_file.rs
//- /src/old_folder/mod.rs
+mod nested;
+
+//- /src/old_folder/nested.rs
+struct foo;
+use crate::old_folder::nested::foo as bar;
//- /src/from_mod/mod.rs
@@ -1080,6 +1085,27 @@ fn main() {}
"newText": "new_folder"
}
]
+ },
+ {
+ "textDocument": {
+ "uri": format!("file://{}", tmp_dir_path.join("src").join("old_folder").join("nested.rs").to_str().unwrap().to_string().replace("C:\\", "/c:/").replace('\\', "/")),
+ "version": null
+ },
+ "edits": [
+ {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 11
+ },
+ "end": {
+ "line": 1,
+ "character": 21
+ }
+ },
+ "newText": "new_folder"
+ }
+ ]
}
]
}),