Skip to content

Commit 2e7e8cc

Browse files
committed
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
2 parents 1a5cee1 + 0647b64 commit 2e7e8cc

File tree

2 files changed

+44
-5
lines changed

2 files changed

+44
-5
lines changed

crates/rust-analyzer/src/lsp/to_proto.rs

+18-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Conversion of rust-analyzer specific types to lsp_types equivalents.
22
use std::{
33
iter::once,
4-
path,
4+
mem, path,
55
sync::atomic::{AtomicU32, Ordering},
66
};
77

@@ -1123,13 +1123,20 @@ pub(crate) fn snippet_text_document_ops(
11231123

11241124
pub(crate) fn snippet_workspace_edit(
11251125
snap: &GlobalStateSnapshot,
1126-
source_change: SourceChange,
1126+
mut source_change: SourceChange,
11271127
) -> Cancellable<lsp_ext::SnippetWorkspaceEdit> {
11281128
let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new();
11291129

1130-
for op in source_change.file_system_edits {
1131-
let ops = snippet_text_document_ops(snap, op)?;
1132-
document_changes.extend_from_slice(&ops);
1130+
for op in &mut source_change.file_system_edits {
1131+
if let FileSystemEdit::CreateFile { dst, initial_contents } = op {
1132+
// replace with a placeholder to avoid cloneing the edit
1133+
let op = FileSystemEdit::CreateFile {
1134+
dst: dst.clone(),
1135+
initial_contents: mem::take(initial_contents),
1136+
};
1137+
let ops = snippet_text_document_ops(snap, op)?;
1138+
document_changes.extend_from_slice(&ops);
1139+
}
11331140
}
11341141
for (file_id, (edit, snippet_edit)) in source_change.source_file_edits {
11351142
let edit = snippet_text_document_edit(
@@ -1141,6 +1148,12 @@ pub(crate) fn snippet_workspace_edit(
11411148
)?;
11421149
document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit));
11431150
}
1151+
for op in source_change.file_system_edits {
1152+
if !matches!(op, FileSystemEdit::CreateFile { .. }) {
1153+
let ops = snippet_text_document_ops(snap, op)?;
1154+
document_changes.extend_from_slice(&ops);
1155+
}
1156+
}
11441157
let mut workspace_edit = lsp_ext::SnippetWorkspaceEdit {
11451158
changes: None,
11461159
document_changes: Some(document_changes),

crates/rust-analyzer/tests/slow-tests/main.rs

+26
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,11 @@ fn main() {}
984984
//- /src/old_file.rs
985985
986986
//- /src/old_folder/mod.rs
987+
mod nested;
988+
989+
//- /src/old_folder/nested.rs
990+
struct foo;
991+
use crate::old_folder::nested::foo as bar;
987992
988993
//- /src/from_mod/mod.rs
989994
@@ -1080,6 +1085,27 @@ fn main() {}
10801085
"newText": "new_folder"
10811086
}
10821087
]
1088+
},
1089+
{
1090+
"textDocument": {
1091+
"uri": format!("file://{}", tmp_dir_path.join("src").join("old_folder").join("nested.rs").to_str().unwrap().to_string().replace("C:\\", "/c:/").replace('\\', "/")),
1092+
"version": null
1093+
},
1094+
"edits": [
1095+
{
1096+
"range": {
1097+
"start": {
1098+
"line": 1,
1099+
"character": 11
1100+
},
1101+
"end": {
1102+
"line": 1,
1103+
"character": 21
1104+
}
1105+
},
1106+
"newText": "new_folder"
1107+
}
1108+
]
10831109
}
10841110
]
10851111
}),

0 commit comments

Comments
 (0)