Skip to content

Commit 3d5d21b

Browse files
bors[bot]kjeremy
andauthored
Merge #7009
7009: Implement workspace/willRenameFiles for single-level file moves r=matklad a=kjeremy Automatically rename modules during file rename if they're in the same directory. Fixes #6780 Co-authored-by: Jeremy Kolb <[email protected]>
2 parents 27ccde9 + 4f989cf commit 3d5d21b

File tree

5 files changed

+92
-6
lines changed

5 files changed

+92
-6
lines changed

crates/ide/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,14 @@ impl Analysis {
535535
self.with_db(|db| references::rename::prepare_rename(db, position))
536536
}
537537

538+
pub fn will_rename_file(
539+
&self,
540+
file_id: FileId,
541+
new_name_stem: &str,
542+
) -> Cancelable<Option<SourceChange>> {
543+
self.with_db(|db| references::rename::will_rename_file(db, file_id, new_name_stem))
544+
}
545+
538546
pub fn structural_search_replace(
539547
&self,
540548
query: &str,

crates/ide/src/references/rename.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::{
66
};
77

88
use hir::{Module, ModuleDef, ModuleSource, Semantics};
9-
use ide_db::base_db::{AnchoredPathBuf, FileRange, SourceDatabaseExt};
9+
use ide_db::base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt};
1010
use ide_db::{
1111
defs::{Definition, NameClass, NameRefClass},
1212
RootDatabase,
@@ -110,6 +110,23 @@ pub(crate) fn rename_with_semantics(
110110
}
111111
}
112112

113+
pub(crate) fn will_rename_file(
114+
db: &RootDatabase,
115+
file_id: FileId,
116+
new_name_stem: &str,
117+
) -> Option<SourceChange> {
118+
let sema = Semantics::new(db);
119+
let module = sema.to_module_def(file_id)?;
120+
121+
let decl = module.declaration_source(db)?;
122+
let range = decl.value.name()?.syntax().text_range();
123+
124+
let position = FilePosition { file_id: decl.file_id.original_file(db), offset: range.start() };
125+
let mut change = rename_mod(&sema, position, module, new_name_stem).ok()?.info;
126+
change.file_system_edits.clear();
127+
Some(change)
128+
}
129+
113130
fn find_module_at_offset(
114131
sema: &Semantics<RootDatabase>,
115132
position: FilePosition,

crates/rust-analyzer/src/caps.rs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ use ide::CompletionResolveCapability;
55
use lsp_types::{
66
CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions,
77
CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
8-
DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, HoverProviderCapability,
9-
ImplementationProviderCapability, OneOf, RenameOptions, SaveOptions,
8+
DocumentOnTypeFormattingOptions, FileOperationFilter, FileOperationPattern,
9+
FileOperationPatternKind, FileOperationRegistrationOptions, FoldingRangeProviderCapability,
10+
HoverProviderCapability, ImplementationProviderCapability, OneOf, RenameOptions, SaveOptions,
1011
SelectionRangeProviderCapability, SemanticTokensFullOptions, SemanticTokensLegend,
1112
SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability,
1213
TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability,
13-
WorkDoneProgressOptions,
14+
WorkDoneProgressOptions, WorkspaceFileOperationsServerCapabilities,
15+
WorkspaceServerCapabilities,
1416
};
1517
use rustc_hash::FxHashSet;
1618
use serde_json::json;
@@ -68,7 +70,26 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
6870
document_link_provider: None,
6971
color_provider: None,
7072
execute_command_provider: None,
71-
workspace: None,
73+
workspace: Some(WorkspaceServerCapabilities {
74+
workspace_folders: None,
75+
file_operations: Some(WorkspaceFileOperationsServerCapabilities {
76+
did_create: None,
77+
will_create: None,
78+
did_rename: None,
79+
will_rename: Some(FileOperationRegistrationOptions {
80+
filters: vec![FileOperationFilter {
81+
scheme: Some(String::from("file")),
82+
pattern: FileOperationPattern {
83+
glob: String::from("**/*.rs"),
84+
matches: Some(FileOperationPatternKind::File),
85+
options: None,
86+
},
87+
}],
88+
}),
89+
did_delete: None,
90+
will_delete: None,
91+
}),
92+
}),
7293
call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)),
7394
semantic_tokens_provider: Some(
7495
SemanticTokensOptions {

crates/rust-analyzer/src/handlers.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::{
1111
use ide::{
1212
AssistConfig, CompletionResolveCapability, FileId, FilePosition, FileRange, HoverAction,
1313
HoverGotoTypeData, LineIndex, NavigationTarget, Query, RangeInfo, Runnable, RunnableKind,
14-
SearchScope, SymbolKind, TextEdit,
14+
SearchScope, SourceChange, SymbolKind, TextEdit,
1515
};
1616
use itertools::Itertools;
1717
use lsp_server::ErrorCode;
@@ -402,6 +402,45 @@ pub(crate) fn handle_workspace_symbol(
402402
}
403403
}
404404

405+
pub(crate) fn handle_will_rename_files(
406+
snap: GlobalStateSnapshot,
407+
params: lsp_types::RenameFilesParams,
408+
) -> Result<Option<lsp_types::WorkspaceEdit>> {
409+
let _p = profile::span("handle_will_rename_files");
410+
411+
let source_changes: Vec<SourceChange> = params
412+
.files
413+
.into_iter()
414+
.filter_map(|file_rename| {
415+
let from = Url::parse(&file_rename.old_uri).ok()?;
416+
let to = Url::parse(&file_rename.new_uri).ok()?;
417+
418+
let from_path = from.to_file_path().ok()?;
419+
let to_path = to.to_file_path().ok()?;
420+
421+
// Limit to single-level moves for now.
422+
match (from_path.parent(), to_path.parent()) {
423+
(Some(p1), Some(p2)) if p1 == p2 => {
424+
let new_name = to_path.file_stem()?;
425+
let new_name = new_name.to_str()?;
426+
Some((snap.url_to_file_id(&from).ok()?, new_name.to_string()))
427+
}
428+
_ => None,
429+
}
430+
})
431+
.filter_map(|(file_id, new_name)| {
432+
snap.analysis.will_rename_file(file_id, &new_name).ok()?
433+
})
434+
.collect();
435+
436+
// Drop file system edits since we're just renaming things on the same level
437+
let edits = source_changes.into_iter().map(|it| it.source_file_edits).flatten().collect();
438+
let source_change = SourceChange::from_edits(edits, Vec::new());
439+
440+
let workspace_edit = to_proto::workspace_edit(&snap, source_change)?;
441+
Ok(Some(workspace_edit))
442+
}
443+
405444
pub(crate) fn handle_goto_definition(
406445
snap: GlobalStateSnapshot,
407446
params: lsp_types::GotoDefinitionParams,

crates/rust-analyzer/src/main_loop.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,7 @@ impl GlobalState {
485485
.on::<lsp_types::request::SemanticTokensRangeRequest>(
486486
handlers::handle_semantic_tokens_range,
487487
)
488+
.on::<lsp_types::request::WillRenameFiles>(handlers::handle_will_rename_files)
488489
.on::<lsp_ext::Ssr>(handlers::handle_ssr)
489490
.finish();
490491
Ok(())

0 commit comments

Comments
 (0)