Renames modules during file rename if they're in the same directory.
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,
};
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,
}
}
+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,
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;
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 {
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;
}
}
+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,
.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(())