From ea3504057e73f541af64451a1b5d2c691d5c01bc Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 5 Jan 2019 13:45:18 +0300 Subject: split import assist --- crates/ra_analysis/src/goto_defenition.rs | 73 ++++++++++++++++++++++++++++ crates/ra_editor/src/assists.rs | 3 ++ crates/ra_editor/src/assists/split_import.rs | 44 +++++++++++++++++ crates/ra_syntax/src/ast.rs | 6 +++ 4 files changed, 126 insertions(+) create mode 100644 crates/ra_analysis/src/goto_defenition.rs create mode 100644 crates/ra_editor/src/assists/split_import.rs diff --git a/crates/ra_analysis/src/goto_defenition.rs b/crates/ra_analysis/src/goto_defenition.rs new file mode 100644 index 000000000..607a25115 --- /dev/null +++ b/crates/ra_analysis/src/goto_defenition.rs @@ -0,0 +1,73 @@ +use ra_db::FileId; +use ra_syntax::ast; + +use crate::db::RootDatabase; + +pub fn goto_defenition(db: &RootDatabase, position: FilePosition, +) -> Cancelable>> { + let file = db.source_file(position.file_id); + let syntax = file.syntax(); + if let Some(name_ref) = find_node_at_offset::(syntax, position.offset) { + return Ok(Some(reference_defenition(db, position.file_id, name_ref))); + } + if let Some(name) = find_node_at_offset::(syntax, position.offset) { + return Ok(Some(name_defenition(db, position.file_idname))); + } + Ok(None) +} + +fn reference_defenition(db: &RootDatabase, file_id: FileId, name_ref: ast::NameRef) -> Cancelable> { + if let Some(name_ref) = find_node_at_offset::(syntax, position.offset) { + let mut rr = ReferenceResolution::new(name_ref.syntax().range()); + if let Some(fn_descr) = + source_binder::function_from_child_node(self, position.file_id, name_ref.syntax())? + { + let scope = fn_descr.scopes(self); + // First try to resolve the symbol locally + if let Some(entry) = scope.resolve_local_name(name_ref) { + rr.resolves_to.push(NavigationTarget { + file_id: position.file_id, + name: entry.name().to_string().into(), + range: entry.ptr().range(), + kind: NAME, + ptr: None, + }); + return Ok(Some(rr)); + }; + } + // If that fails try the index based approach. + rr.resolves_to.extend( + self.index_resolve(name_ref)? + .into_iter() + .map(NavigationTarget::from_symbol), + ); + return Ok(Some(rr)); + } + if let Some(name) = find_node_at_offset::(syntax, position.offset) { + let mut rr = ReferenceResolution::new(name.syntax().range()); + if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { + if module.has_semi() { + if let Some(child_module) = + source_binder::module_from_declaration(self, position.file_id, module)? + { + let file_id = child_module.file_id(); + let name = match child_module.name() { + Some(name) => name.to_string().into(), + None => "".into(), + }; + let symbol = NavigationTarget { + file_id, + name, + range: TextRange::offset_len(0.into(), 0.into()), + kind: MODULE, + ptr: None, + }; + rr.resolves_to.push(symbol); + return Ok(Some(rr)); + } + } + } + } + Ok(None) + +} diff --git a/crates/ra_editor/src/assists.rs b/crates/ra_editor/src/assists.rs index cc40ee4c8..57b78342a 100644 --- a/crates/ra_editor/src/assists.rs +++ b/crates/ra_editor/src/assists.rs @@ -8,6 +8,7 @@ mod add_derive; mod add_impl; mod introduce_variable; mod change_visibility; +mod split_import; use ra_text_edit::{TextEdit, TextEditBuilder}; use ra_syntax::{ @@ -23,6 +24,7 @@ pub use self::{ add_impl::add_impl, introduce_variable::introduce_variable, change_visibility::change_visibility, + split_import::split_import, }; /// Return all the assists applicable at the given position. @@ -34,6 +36,7 @@ pub fn assists(file: &SourceFileNode, range: TextRange) -> Vec { add_impl, introduce_variable, change_visibility, + split_import, ] .iter() .filter_map(|&assist| ctx.clone().apply(assist)) diff --git a/crates/ra_editor/src/assists/split_import.rs b/crates/ra_editor/src/assists/split_import.rs new file mode 100644 index 000000000..75f9e8dfb --- /dev/null +++ b/crates/ra_editor/src/assists/split_import.rs @@ -0,0 +1,44 @@ +use ra_syntax::{ + TextUnit, AstNode, SyntaxKind::COLONCOLON, + ast, + algo::generate, +}; + +use crate::assists::{AssistCtx, Assist}; + +pub fn split_import(ctx: AssistCtx) -> Option { + let colon_colon = ctx + .leaf_at_offset() + .find(|leaf| leaf.kind() == COLONCOLON)?; + let path = colon_colon.parent().and_then(ast::Path::cast)?; + let top_path = generate(Some(path), |it| it.parent_path()).last()?; + + let use_tree = top_path.syntax().ancestors().find_map(ast::UseTree::cast); + if use_tree.is_none() { + return None; + } + + let l_curly = colon_colon.range().end(); + let r_curly = top_path.syntax().range().end(); + + ctx.build("split import", |edit| { + edit.insert(l_curly, "{"); + edit.insert(r_curly, "}"); + edit.set_cursor(l_curly + TextUnit::of_str("{")); + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::assists::check_assist; + + #[test] + fn test_split_import() { + check_assist( + split_import, + "use crate::<|>db::RootDatabase;", + "use crate::{<|>db::RootDatabase};", + ) + } +} diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 2a3bd27e2..c10169d90 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -363,6 +363,12 @@ impl<'a> PathSegment<'a> { } } +impl<'a> Path<'a> { + pub fn parent_path(self) -> Option> { + self.syntax().parent().and_then(Path::cast) + } +} + impl<'a> UseTree<'a> { pub fn has_star(self) -> bool { self.syntax().children().any(|it| it.kind() == STAR) -- cgit v1.2.3