From 4f989cfa08e9a9f4abf8dc6136c9802bc7ad3a4c Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Tue, 22 Dec 2020 14:19:51 -0500 Subject: Implement workspace/willRenameFiles for single-level file moves Renames modules during file rename if they're in the same directory. --- crates/ide/src/lib.rs | 8 +++++++ crates/ide/src/references/rename.rs | 19 +++++++++++++++- crates/rust-analyzer/src/caps.rs | 29 +++++++++++++++++++++---- crates/rust-analyzer/src/handlers.rs | 41 ++++++++++++++++++++++++++++++++++- crates/rust-analyzer/src/main_loop.rs | 1 + 5 files changed, 92 insertions(+), 6 deletions(-) diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index dbad9a84f..52c7f9775 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -535,6 +535,14 @@ impl Analysis { self.with_db(|db| references::rename::prepare_rename(db, position)) } + pub fn will_rename_file( + &self, + file_id: FileId, + new_name_stem: &str, + ) -> Cancelable> { + self.with_db(|db| references::rename::will_rename_file(db, file_id, new_name_stem)) + } + pub fn structural_search_replace( &self, query: &str, diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index cd721b7eb..15c95f239 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs @@ -6,7 +6,7 @@ use std::{ }; use hir::{Module, ModuleDef, ModuleSource, Semantics}; -use ide_db::base_db::{AnchoredPathBuf, FileRange, SourceDatabaseExt}; +use ide_db::base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt}; use ide_db::{ defs::{Definition, NameClass, NameRefClass}, RootDatabase, @@ -110,6 +110,23 @@ pub(crate) fn rename_with_semantics( } } +pub(crate) fn will_rename_file( + db: &RootDatabase, + file_id: FileId, + new_name_stem: &str, +) -> Option { + let sema = Semantics::new(db); + let module = sema.to_module_def(file_id)?; + + let decl = module.declaration_source(db)?; + let range = decl.value.name()?.syntax().text_range(); + + let position = FilePosition { file_id: decl.file_id.original_file(db), offset: range.start() }; + let mut change = rename_mod(&sema, position, module, new_name_stem).ok()?.info; + change.file_system_edits.clear(); + Some(change) +} + fn find_module_at_offset( sema: &Semantics, position: FilePosition, diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index de5eb93b5..80e46bf7f 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs @@ -5,12 +5,14 @@ use ide::CompletionResolveCapability; use lsp_types::{ CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions, CodeActionProviderCapability, CodeLensOptions, CompletionOptions, - DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, HoverProviderCapability, - ImplementationProviderCapability, OneOf, RenameOptions, SaveOptions, + DocumentOnTypeFormattingOptions, FileOperationFilter, FileOperationPattern, + FileOperationPatternKind, FileOperationRegistrationOptions, FoldingRangeProviderCapability, + HoverProviderCapability, ImplementationProviderCapability, OneOf, RenameOptions, SaveOptions, SelectionRangeProviderCapability, SemanticTokensFullOptions, SemanticTokensLegend, SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability, - WorkDoneProgressOptions, + WorkDoneProgressOptions, WorkspaceFileOperationsServerCapabilities, + WorkspaceServerCapabilities, }; use rustc_hash::FxHashSet; use serde_json::json; @@ -68,7 +70,26 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti document_link_provider: None, color_provider: None, execute_command_provider: None, - workspace: None, + workspace: Some(WorkspaceServerCapabilities { + workspace_folders: None, + file_operations: Some(WorkspaceFileOperationsServerCapabilities { + did_create: None, + will_create: None, + did_rename: None, + will_rename: Some(FileOperationRegistrationOptions { + filters: vec![FileOperationFilter { + scheme: Some(String::from("file")), + pattern: FileOperationPattern { + glob: String::from("**/*.rs"), + matches: Some(FileOperationPatternKind::File), + options: None, + }, + }], + }), + did_delete: None, + will_delete: None, + }), + }), call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)), semantic_tokens_provider: Some( SemanticTokensOptions { diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 55bc2bcec..25692793b 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -11,7 +11,7 @@ use std::{ use ide::{ AssistConfig, CompletionResolveCapability, FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, LineIndex, NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, - SearchScope, SymbolKind, TextEdit, + SearchScope, SourceChange, SymbolKind, TextEdit, }; use itertools::Itertools; use lsp_server::ErrorCode; @@ -402,6 +402,45 @@ pub(crate) fn handle_workspace_symbol( } } +pub(crate) fn handle_will_rename_files( + snap: GlobalStateSnapshot, + params: lsp_types::RenameFilesParams, +) -> Result> { + let _p = profile::span("handle_will_rename_files"); + + let source_changes: Vec = params + .files + .into_iter() + .filter_map(|file_rename| { + let from = Url::parse(&file_rename.old_uri).ok()?; + let to = Url::parse(&file_rename.new_uri).ok()?; + + let from_path = from.to_file_path().ok()?; + let to_path = to.to_file_path().ok()?; + + // Limit to single-level moves for now. + match (from_path.parent(), to_path.parent()) { + (Some(p1), Some(p2)) if p1 == p2 => { + let new_name = to_path.file_stem()?; + let new_name = new_name.to_str()?; + Some((snap.url_to_file_id(&from).ok()?, new_name.to_string())) + } + _ => None, + } + }) + .filter_map(|(file_id, new_name)| { + snap.analysis.will_rename_file(file_id, &new_name).ok()? + }) + .collect(); + + // Drop file system edits since we're just renaming things on the same level + let edits = source_changes.into_iter().map(|it| it.source_file_edits).flatten().collect(); + let source_change = SourceChange::from_edits(edits, Vec::new()); + + let workspace_edit = to_proto::workspace_edit(&snap, source_change)?; + Ok(Some(workspace_edit)) +} + pub(crate) fn handle_goto_definition( snap: GlobalStateSnapshot, params: lsp_types::GotoDefinitionParams, diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index ec3d5e060..5d55dc96e 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -485,6 +485,7 @@ impl GlobalState { .on::( handlers::handle_semantic_tokens_range, ) + .on::(handlers::handle_will_rename_files) .on::(handlers::handle_ssr) .finish(); Ok(()) -- cgit v1.2.3