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 hir::{
self, Problem, source_binder,
self, Problem, source_binder::{
self,
module_from_declaration
}, ModuleSource,
};
use ra_db::{
FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase,
@ -9,16 +12,16 @@
};
use ra_ide_api_light::{self, assists, LocalEdit, Severity};
use ra_syntax::{
TextRange, AstNode, SourceFile,
ast::{self, NameOwner},
algo::find_node_at_offset,
algo::find_node_at_offset, ast::{self, NameOwner}, AstNode,
SourceFile,
TextRange,
};
use crate::{
AnalysisChange,
CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit,
Query, RootChange, SourceChange, SourceFileEdit,
symbol_index::{LibrarySymbolsQuery, FileSymbol},
symbol_index::{FileSymbol, LibrarySymbolsQuery},
};
impl db::RootDatabase {
@ -110,6 +113,7 @@ pub(crate) fn crate_for(&self, file_id: FileId) -> Vec<CrateId> {
};
vec![krate.crate_id()]
}
pub(crate) fn find_all_refs(&self, position: FilePosition) -> Vec<(FileId, TextRange)> {
let file = self.source_file(position.file_id);
// Find the binding associated with the offset
@ -230,20 +234,94 @@ pub(crate) fn assists(&self, frange: FileRange) -> Vec<SourceChange> {
.collect()
}
<<<<<<< HEAD
pub(crate) fn rename(&self, position: FilePosition, new_name: &str) -> Vec<SourceFileEdit> {
self.find_all_refs(position)
.iter()
.map(|(file_id, text_range)| SourceFileEdit {
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: {
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()
},
<<<<<<< HEAD
})
.collect::<Vec<_>>()
}
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 mut query = Query::new(name.to_string());
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 test_utils::assert_eq_text;
use insta::assert_debug_snapshot_matches;
use ra_ide_api::{
mock_analysis::{single_file, single_file_with_position, MockAnalysis},
AnalysisChange, CrateGraph, FileId, Query
};
mod runnables;
#[test]
fn test_unresolved_module_diagnostic() {
@ -91,6 +93,7 @@ fn foo(i<|> : u32) -> u32 {
let refs = get_all_refs(code);
assert_eq!(refs.len(), 2);
}
#[test]
fn test_rename_for_local() {
test_rename(
@ -167,15 +170,35 @@ fn foo(mut new_name : u32) -> u32 {
);
}
#[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) {
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 file_id: Option<FileId> = None;
for edit in edits {
file_id = Some(edit.file_id);
for atom in edit.edit.as_atoms() {
text_edit_bulder.replace(atom.delete, atom.insert.clone());
if let Some(change) = source_change {
for edit in change.source_file_edits {
file_id = Some(edit.file_id);
for atom in edit.edit.as_atoms() {
text_edit_bulder.replace(atom.delete, atom.insert.clone());
}
}
}
let result = text_edit_bulder

View File

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

View File

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