mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-15 06:33:58 +00:00
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 <kjeremy@gmail.com>
This commit is contained in:
commit
3d5d21b602
5 changed files with 92 additions and 6 deletions
|
@ -535,6 +535,14 @@ impl Analysis {
|
||||||
self.with_db(|db| references::rename::prepare_rename(db, position))
|
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(
|
pub fn structural_search_replace(
|
||||||
&self,
|
&self,
|
||||||
query: &str,
|
query: &str,
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use hir::{Module, ModuleDef, ModuleSource, Semantics};
|
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::{
|
use ide_db::{
|
||||||
defs::{Definition, NameClass, NameRefClass},
|
defs::{Definition, NameClass, NameRefClass},
|
||||||
RootDatabase,
|
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(
|
fn find_module_at_offset(
|
||||||
sema: &Semantics<RootDatabase>,
|
sema: &Semantics<RootDatabase>,
|
||||||
position: FilePosition,
|
position: FilePosition,
|
||||||
|
|
|
@ -5,12 +5,14 @@ use ide::CompletionResolveCapability;
|
||||||
use lsp_types::{
|
use lsp_types::{
|
||||||
CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions,
|
CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions,
|
||||||
CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
|
CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
|
||||||
DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, HoverProviderCapability,
|
DocumentOnTypeFormattingOptions, FileOperationFilter, FileOperationPattern,
|
||||||
ImplementationProviderCapability, OneOf, RenameOptions, SaveOptions,
|
FileOperationPatternKind, FileOperationRegistrationOptions, FoldingRangeProviderCapability,
|
||||||
|
HoverProviderCapability, ImplementationProviderCapability, OneOf, RenameOptions, SaveOptions,
|
||||||
SelectionRangeProviderCapability, SemanticTokensFullOptions, SemanticTokensLegend,
|
SelectionRangeProviderCapability, SemanticTokensFullOptions, SemanticTokensLegend,
|
||||||
SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability,
|
SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability,
|
||||||
TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability,
|
TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability,
|
||||||
WorkDoneProgressOptions,
|
WorkDoneProgressOptions, WorkspaceFileOperationsServerCapabilities,
|
||||||
|
WorkspaceServerCapabilities,
|
||||||
};
|
};
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
@ -68,7 +70,26 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
|
||||||
document_link_provider: None,
|
document_link_provider: None,
|
||||||
color_provider: None,
|
color_provider: None,
|
||||||
execute_command_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)),
|
call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)),
|
||||||
semantic_tokens_provider: Some(
|
semantic_tokens_provider: Some(
|
||||||
SemanticTokensOptions {
|
SemanticTokensOptions {
|
||||||
|
|
|
@ -11,7 +11,7 @@ use std::{
|
||||||
use ide::{
|
use ide::{
|
||||||
AssistConfig, CompletionResolveCapability, FileId, FilePosition, FileRange, HoverAction,
|
AssistConfig, CompletionResolveCapability, FileId, FilePosition, FileRange, HoverAction,
|
||||||
HoverGotoTypeData, LineIndex, NavigationTarget, Query, RangeInfo, Runnable, RunnableKind,
|
HoverGotoTypeData, LineIndex, NavigationTarget, Query, RangeInfo, Runnable, RunnableKind,
|
||||||
SearchScope, SymbolKind, TextEdit,
|
SearchScope, SourceChange, SymbolKind, TextEdit,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use lsp_server::ErrorCode;
|
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<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(
|
pub(crate) fn handle_goto_definition(
|
||||||
snap: GlobalStateSnapshot,
|
snap: GlobalStateSnapshot,
|
||||||
params: lsp_types::GotoDefinitionParams,
|
params: lsp_types::GotoDefinitionParams,
|
||||||
|
|
|
@ -485,6 +485,7 @@ impl GlobalState {
|
||||||
.on::<lsp_types::request::SemanticTokensRangeRequest>(
|
.on::<lsp_types::request::SemanticTokensRangeRequest>(
|
||||||
handlers::handle_semantic_tokens_range,
|
handlers::handle_semantic_tokens_range,
|
||||||
)
|
)
|
||||||
|
.on::<lsp_types::request::WillRenameFiles>(handlers::handle_will_rename_files)
|
||||||
.on::<lsp_ext::Ssr>(handlers::handle_ssr)
|
.on::<lsp_ext::Ssr>(handlers::handle_ssr)
|
||||||
.finish();
|
.finish();
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
Loading…
Reference in a new issue