rename mod

This commit is contained in:
gfreezy 2019-01-15 00:09:03 +08:00 committed by Aleksey Kladov
parent b82fe73d1a
commit bc0f79f74a
4 changed files with 148 additions and 35 deletions

View File

@ -1,7 +1,10 @@
use std::sync::Arc; use std::sync::Arc;
use hir::{ use hir::{
self, Problem, source_binder, self, Problem, source_binder::{
self,
module_from_declaration
}, ModuleSource,
}; };
use ra_db::{ use ra_db::{
FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase, FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase,
@ -9,16 +12,16 @@ use ra_db::{
}; };
use ra_ide_api_light::{self, assists, LocalEdit, Severity}; use ra_ide_api_light::{self, assists, LocalEdit, Severity};
use ra_syntax::{ use ra_syntax::{
TextRange, AstNode, SourceFile, algo::find_node_at_offset, ast::{self, NameOwner}, AstNode,
ast::{self, NameOwner}, SourceFile,
algo::find_node_at_offset, TextRange,
}; };
use crate::{ use crate::{
AnalysisChange, AnalysisChange,
CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit, CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit,
Query, RootChange, SourceChange, SourceFileEdit, Query, RootChange, SourceChange, SourceFileEdit,
symbol_index::{LibrarySymbolsQuery, FileSymbol}, symbol_index::{FileSymbol, LibrarySymbolsQuery},
}; };
impl db::RootDatabase { impl db::RootDatabase {
@ -110,6 +113,7 @@ impl db::RootDatabase {
}; };
vec![krate.crate_id()] vec![krate.crate_id()]
} }
pub(crate) fn find_all_refs(&self, position: FilePosition) -> Vec<(FileId, TextRange)> { pub(crate) fn find_all_refs(&self, position: FilePosition) -> Vec<(FileId, TextRange)> {
let file = self.source_file(position.file_id); let file = self.source_file(position.file_id);
// Find the binding associated with the offset // Find the binding associated with the offset
@ -230,20 +234,94 @@ impl db::RootDatabase {
.collect() .collect()
} }
<<<<<<< HEAD
pub(crate) fn rename(&self, position: FilePosition, new_name: &str) -> Vec<SourceFileEdit> { pub(crate) fn rename(&self, position: FilePosition, new_name: &str) -> Vec<SourceFileEdit> {
self.find_all_refs(position) self.find_all_refs(position)
.iter() .iter()
.map(|(file_id, text_range)| SourceFileEdit { .map(|(file_id, text_range)| SourceFileEdit {
file_id: *file_id, file_id: *file_id,
=======
pub(crate) fn rename(
&self,
position: FilePosition,
new_name: &str,
) -> Cancelable<Option<SourceChange>> {
let mut source_file_edits = Vec::new();
let mut file_system_edits = Vec::new();
let source_file = self.source_file(position.file_id);
let syntax = source_file.syntax();
// We are rename a mod
if let (Some(ast_module), Some(name)) = (
find_node_at_offset::<ast::Module>(syntax, position.offset),
find_node_at_offset::<ast::Name>(syntax, position.offset),
) {
if let Some(module) = module_from_declaration(self, position.file_id, &ast_module)? {
let (file_id, module_source) = module.definition_source(self)?;
match module_source {
ModuleSource::SourceFile(..) => {
let move_file = FileSystemEdit::MoveFile {
src: file_id,
dst_source_root: self.file_source_root(position.file_id),
dst_path: self
.file_relative_path(file_id)
.with_file_name(new_name)
.with_extension("rs"),
};
file_system_edits.push(move_file);
}
ModuleSource::Module(..) => {}
}
}
let edit = SourceFileEdit {
file_id: position.file_id,
>>>>>>> rename mod
edit: { edit: {
let mut builder = ra_text_edit::TextEditBuilder::default(); let mut builder = ra_text_edit::TextEditBuilder::default();
builder.replace(*text_range, new_name.into()); builder.replace(name.syntax().range(), new_name.into());
builder.finish() builder.finish()
}, },
<<<<<<< HEAD
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
} }
pub(crate) fn index_resolve(&self, name_ref: &ast::NameRef) -> Vec<FileSymbol> { pub(crate) fn index_resolve(&self, name_ref: &ast::NameRef) -> Vec<FileSymbol> {
=======
};
source_file_edits.push(edit);
}
// rename references
else {
let edit = self
.find_all_refs(position)?
.iter()
.map(|(file_id, text_range)| SourceFileEdit {
file_id: *file_id,
edit: {
let mut builder = ra_text_edit::TextEditBuilder::default();
builder.replace(*text_range, new_name.into());
builder.finish()
},
})
.collect::<Vec<_>>();
if edit.is_empty() {
return Ok(None);
}
source_file_edits = edit;
}
return Ok(Some(SourceChange {
label: "rename".to_string(),
source_file_edits,
file_system_edits,
cursor_position: None,
}));
}
pub(crate) fn index_resolve(&self, name_ref: &ast::NameRef) -> Cancelable<Vec<FileSymbol>> {
>>>>>>> rename mod
let name = name_ref.text(); let name = name_ref.text();
let mut query = Query::new(name.to_string()); let mut query = Query::new(name.to_string());
query.exact(); query.exact();

View File

@ -1,11 +1,13 @@
use ra_ide_api::{
AnalysisChange,
CrateGraph, FileId, mock_analysis::{MockAnalysis, single_file, single_file_with_position}, Query,
};
use ra_ide_api::mock_analysis::analysis_and_position;
use ra_syntax::TextRange; use ra_syntax::TextRange;
use test_utils::assert_eq_text; use test_utils::assert_eq_text;
use insta::assert_debug_snapshot_matches; use insta::assert_debug_snapshot_matches;
use ra_ide_api::{ mod runnables;
mock_analysis::{single_file, single_file_with_position, MockAnalysis},
AnalysisChange, CrateGraph, FileId, Query
};
#[test] #[test]
fn test_unresolved_module_diagnostic() { fn test_unresolved_module_diagnostic() {
@ -91,6 +93,7 @@ fn test_find_all_refs_for_fn_param() {
let refs = get_all_refs(code); let refs = get_all_refs(code);
assert_eq!(refs.len(), 2); assert_eq!(refs.len(), 2);
} }
#[test] #[test]
fn test_rename_for_local() { fn test_rename_for_local() {
test_rename( test_rename(
@ -167,15 +170,35 @@ fn test_rename_for_mut_param() {
); );
} }
#[test]
fn test_rename_mod() {
let (analysis, position) = analysis_and_position(
"
//- /bar.rs
mod fo<|>o;
//- /bar/foo.rs
// emtpy
",
);
let new_name = "foo2";
let source_change = analysis.rename(position, new_name).unwrap();
assert_eq_dbg(
r#"Some(SourceChange { label: "rename", source_file_edits: [SourceFileEdit { file_id: FileId(1), edit: TextEdit { atoms: [AtomTextEdit { delete: [4; 7), insert: "foo2" }] } }], file_system_edits: [MoveFile { src: FileId(2), dst_source_root: SourceRootId(0), dst_path: "bar/foo2.rs" }], cursor_position: None })"#,
&source_change,
);
}
fn test_rename(text: &str, new_name: &str, expected: &str) { fn test_rename(text: &str, new_name: &str, expected: &str) {
let (analysis, position) = single_file_with_position(text); let (analysis, position) = single_file_with_position(text);
let edits = analysis.rename(position, new_name).unwrap(); let source_change = analysis.rename(position, new_name).unwrap();
let mut text_edit_bulder = ra_text_edit::TextEditBuilder::default(); let mut text_edit_bulder = ra_text_edit::TextEditBuilder::default();
let mut file_id: Option<FileId> = None; let mut file_id: Option<FileId> = None;
for edit in edits { if let Some(change) = source_change {
file_id = Some(edit.file_id); for edit in change.source_file_edits {
for atom in edit.edit.as_atoms() { file_id = Some(edit.file_id);
text_edit_bulder.replace(atom.delete, atom.insert.clone()); for atom in edit.edit.as_atoms() {
text_edit_bulder.replace(atom.delete, atom.insert.clone());
}
} }
} }
let result = text_edit_bulder let result = text_edit_bulder

View File

@ -411,10 +411,7 @@ struct PoolDispatcher<'a> {
} }
impl<'a> PoolDispatcher<'a> { impl<'a> PoolDispatcher<'a> {
fn on<'b, R>( fn on<R>(&mut self, f: fn(ServerWorld, R::Params) -> Result<R::Result>) -> Result<&mut Self>
&'b mut self,
f: fn(ServerWorld, R::Params) -> Result<R::Result>,
) -> Result<&'b mut Self>
where where
R: req::Request, R: req::Request,
R::Params: DeserializeOwned + Send + 'static, R::Params: DeserializeOwned + Send + 'static,

View File

@ -1,5 +1,3 @@
use std::collections::HashMap;
use gen_lsp_server::ErrorCode; use gen_lsp_server::ErrorCode;
use lsp_types::{ use lsp_types::{
CodeActionResponse, CodeLens, Command, Diagnostic, DiagnosticSeverity, CodeActionResponse, CodeLens, Command, Diagnostic, DiagnosticSeverity,
@ -7,7 +5,7 @@ use lsp_types::{
FoldingRangeKind, FoldingRangeParams, Hover, HoverContents, Location, MarkupContent, FoldingRangeKind, FoldingRangeParams, Hover, HoverContents, Location, MarkupContent,
MarkupKind, ParameterInformation, ParameterLabel, Position, PrepareRenameResponse, Range, MarkupKind, ParameterInformation, ParameterLabel, Position, PrepareRenameResponse, Range,
RenameParams, SignatureInformation, SymbolInformation, TextDocumentIdentifier, TextEdit, RenameParams, SignatureInformation, SymbolInformation, TextDocumentIdentifier, TextEdit,
WorkspaceEdit, WorkspaceEdit, DocumentChanges, TextDocumentEdit, DocumentChangeOperation, ResourceOp
}; };
use ra_ide_api::{ use ra_ide_api::{
FileId, FilePosition, FileRange, FoldKind, Query, RangeInfo, RunnableKind, Severity, FileId, FilePosition, FileRange, FoldKind, Query, RangeInfo, RunnableKind, Severity,
@ -467,26 +465,43 @@ pub fn handle_rename(world: ServerWorld, params: RenameParams) -> Result<Option<
.into()); .into());
} }
let renames = world let change = world
.analysis() .analysis()
.rename(FilePosition { file_id, offset }, &*params.new_name)?; .rename(FilePosition { file_id, offset }, &*params.new_name)?;
if renames.is_empty() { if change.is_none() {
return Ok(None); return Ok(None);
} }
let mut changes = HashMap::new(); let mut source_change = change.unwrap();
for edit in renames { let text_document_edits = source_change
changes .source_file_edits
.entry(file_id.try_conv_with(&world)?) .drain(..)
.or_insert_with(Vec::new) .into_iter()
.extend(edit.edit.conv_with(&line_index)); .map(|e| e.try_conv_with(&world))
} .collect::<Result<Vec<TextDocumentEdit>>>();
let text_document_ops = source_change
.file_system_edits
.drain(..)
.into_iter()
.map(|e| e.try_conv_with(&world))
.collect::<Result<Vec<ResourceOp>>>();
let mut document_changes = Vec::new();
document_changes.extend(
text_document_edits?
.into_iter()
.map(DocumentChangeOperation::Edit),
);
document_changes.extend(
text_document_ops?
.into_iter()
.map(DocumentChangeOperation::Op),
);
Ok(Some(WorkspaceEdit { Ok(Some(WorkspaceEdit {
changes: Some(changes), changes: None,
document_changes: Some(DocumentChanges::Operations(document_changes)),
// TODO: return this instead if client/server support it. See #144
document_changes: None,
})) }))
} }