]> git.lizzy.rs Git - rust.git/commitdiff
Implement workspace/willRenameFiles for single-level file moves
authorJeremy Kolb <kjeremy@gmail.com>
Tue, 22 Dec 2020 19:19:51 +0000 (14:19 -0500)
committerJeremy Kolb <kjeremy@gmail.com>
Wed, 23 Dec 2020 12:47:31 +0000 (07:47 -0500)
Renames modules during file rename if they're in the same directory.

crates/ide/src/lib.rs
crates/ide/src/references/rename.rs
crates/rust-analyzer/src/caps.rs
crates/rust-analyzer/src/handlers.rs
crates/rust-analyzer/src/main_loop.rs

index dbad9a84f6a0244764fca82e100006acd314bca2..52c7f97759ffb5a1364106209cd3a6152f3aa716 100644 (file)
@@ -535,6 +535,14 @@ pub fn prepare_rename(
         self.with_db(|db| references::rename::prepare_rename(db, position))
     }
 
+    pub fn will_rename_file(
+        &self,
+        file_id: FileId,
+        new_name_stem: &str,
+    ) -> Cancelable<Option<SourceChange>> {
+        self.with_db(|db| references::rename::will_rename_file(db, file_id, new_name_stem))
+    }
+
     pub fn structural_search_replace(
         &self,
         query: &str,
index cd721b7ebcb382442d871037c702e297423fbd9f..15c95f2398cd7bb098d6d23437a1d7c2991767a1 100644 (file)
@@ -6,7 +6,7 @@
 };
 
 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<SourceChange> {
+    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<RootDatabase>,
     position: FilePosition,
index de5eb93b50683a9cda82988db35f6e143b90abb0..80e46bf7f15edc545cbefd2ff76d0f2987086de3 100644 (file)
@@ -5,12 +5,14 @@
 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 {
index 55bc2bceca1460608b3580d3ef3062e3060692fe..25692793b9764d7f34254f55eb8b5d485ff99ae0 100644 (file)
@@ -11,7 +11,7 @@
 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 @@ fn exec_query(snap: &GlobalStateSnapshot, query: Query) -> Result<Vec<SymbolInfo
     }
 }
 
+pub(crate) fn handle_will_rename_files(
+    snap: GlobalStateSnapshot,
+    params: lsp_types::RenameFilesParams,
+) -> Result<Option<lsp_types::WorkspaceEdit>> {
+    let _p = profile::span("handle_will_rename_files");
+
+    let source_changes: Vec<SourceChange> = 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,
index ec3d5e0600ffdab656cbe66861d26894c4eb7222..5d55dc96e2a18e687d6144a579595b4e2c7bbfc6 100644 (file)
@@ -485,6 +485,7 @@ fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()>
             .on::<lsp_types::request::SemanticTokensRangeRequest>(
                 handlers::handle_semantic_tokens_range,
             )
+            .on::<lsp_types::request::WillRenameFiles>(handlers::handle_will_rename_files)
             .on::<lsp_ext::Ssr>(handlers::handle_ssr)
             .finish();
         Ok(())