From 5299a35e3dc484ea2e7d42cfeed89aee806425d3 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 28 Dec 2018 20:33:39 +0300 Subject: extend selection works with macros --- crates/ra_analysis/src/extend_selection.rs | 44 ++++++++++++++++++++++++++++-- crates/ra_analysis/src/macros.rs | 11 ++++++++ crates/ra_analysis/src/mock_analysis.rs | 17 ++++++++++-- 3 files changed, 68 insertions(+), 4 deletions(-) (limited to 'crates/ra_analysis') diff --git a/crates/ra_analysis/src/extend_selection.rs b/crates/ra_analysis/src/extend_selection.rs index 5e1fbee18..cde6ee101 100644 --- a/crates/ra_analysis/src/extend_selection.rs +++ b/crates/ra_analysis/src/extend_selection.rs @@ -1,4 +1,8 @@ use ra_db::SyntaxDatabase; +use ra_syntax::{ + SyntaxNodeRef, AstNode, + ast, algo::find_covering_node, +}; use crate::{ TextRange, FileRange, @@ -6,6 +10,42 @@ use crate::{ }; pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { - let file = db.source_file(frange.file_id); - ra_editor::extend_selection(&file, frange.range).unwrap_or(frange.range) + let source_file = db.source_file(frange.file_id); + if let Some(macro_call) = find_macro_call(source_file.syntax(), frange.range) { + if let Some(exp) = crate::macros::expand(db, frange.file_id, macro_call) { + if let Some(dst_range) = exp.map_range_forward(frange.range) { + if let Some(dst_range) = ra_editor::extend_selection(exp.source_file(), dst_range) { + if let Some(src_range) = exp.map_range_back(dst_range) { + return src_range; + } + } + } + } + } + ra_editor::extend_selection(&source_file, frange.range).unwrap_or(frange.range) +} + +fn find_macro_call(node: SyntaxNodeRef, range: TextRange) -> Option { + find_covering_node(node, range) + .ancestors() + .find_map(ast::MacroCall::cast) +} + +#[cfg(test)] +mod tests { + use crate::mock_analysis::single_file_with_range; + use test_utils::assert_eq_dbg; + + #[test] + fn extend_selection_inside_macros() { + let (analysis, frange) = single_file_with_range( + " + fn main() { + ctry!(foo(|x| <|>x<|>)); + } + ", + ); + let r = analysis.extend_selection(frange); + assert_eq_dbg("[51; 56)", &r); + } } diff --git a/crates/ra_analysis/src/macros.rs b/crates/ra_analysis/src/macros.rs index c0dd49dc8..b9feb7fad 100644 --- a/crates/ra_analysis/src/macros.rs +++ b/crates/ra_analysis/src/macros.rs @@ -61,4 +61,15 @@ impl MacroExpansion { } None } + pub(crate) fn map_range_forward(&self, src_range: TextRange) -> Option { + for (s_range, t_range) in self.ranges_map.iter() { + if src_range.is_subrange(&s_range) { + let src_at_zero_range = src_range - src_range.start(); + let src_range_offset = src_range.start() - s_range.start(); + let src_range = src_at_zero_range + src_range_offset + t_range.start(); + return Some(src_range); + } + } + None + } } diff --git a/crates/ra_analysis/src/mock_analysis.rs b/crates/ra_analysis/src/mock_analysis.rs index 5ce2aa2b4..960529404 100644 --- a/crates/ra_analysis/src/mock_analysis.rs +++ b/crates/ra_analysis/src/mock_analysis.rs @@ -1,10 +1,10 @@ use std::sync::Arc; use relative_path::RelativePathBuf; -use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; +use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; use ra_db::mock::FileMap; -use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FilePosition, SourceRootId}; +use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FilePosition, FileRange, SourceRootId}; /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis /// from a set of in-memory files. @@ -66,6 +66,12 @@ impl MockAnalysis { self.files.push((path.to_string(), text.to_string())); FilePosition { file_id, offset } } + pub fn add_file_with_range(&mut self, path: &str, text: &str) -> FileRange { + let (range, text) = extract_range(text); + let file_id = FileId((self.files.len() + 1) as u32); + self.files.push((path.to_string(), text.to_string())); + FileRange { file_id, range } + } pub fn id_of(&self, path: &str) -> FileId { let (idx, _) = self .files @@ -115,3 +121,10 @@ pub fn single_file_with_position(code: &str) -> (Analysis, FilePosition) { let pos = mock.add_file_with_position("/main.rs", code); (mock.analysis(), pos) } + +/// Creates analysis for a single file, returns range marked with a pair of <|>. +pub fn single_file_with_range(code: &str) -> (Analysis, FileRange) { + let mut mock = MockAnalysis::new(); + let pos = mock.add_file_with_range("/main.rs", code); + (mock.analysis(), pos) +} -- cgit v1.2.3 From 11122e29b7ec5bc2e08822deaa6fdf9a1cc8ffca Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 28 Dec 2018 21:06:08 +0300 Subject: completion for enum variants --- crates/ra_analysis/src/completion/complete_path.rs | 39 +++++++++++++++++----- .../ra_analysis/src/completion/completion_item.rs | 1 + 2 files changed, 31 insertions(+), 9 deletions(-) (limited to 'crates/ra_analysis') diff --git a/crates/ra_analysis/src/completion/complete_path.rs b/crates/ra_analysis/src/completion/complete_path.rs index aaa2c7cee..c73a083a4 100644 --- a/crates/ra_analysis/src/completion/complete_path.rs +++ b/crates/ra_analysis/src/completion/complete_path.rs @@ -1,6 +1,6 @@ use crate::{ Cancelable, - completion::{CompletionItem, Completions, CompletionKind, CompletionContext}, + completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext}, }; pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> { @@ -12,16 +12,25 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> C Some(it) => it, None => return Ok(()), }; - let target_module = match def_id.resolve(ctx.db)? { - hir::Def::Module(it) => it, + match def_id.resolve(ctx.db)? { + hir::Def::Module(module) => { + let module_scope = module.scope(ctx.db)?; + module_scope.entries().for_each(|(name, res)| { + CompletionItem::new(CompletionKind::Reference, name.to_string()) + .from_resolution(ctx.db, res) + .add_to(acc) + }); + } + hir::Def::Enum(e) => e + .variants(ctx.db)? + .into_iter() + .for_each(|(name, _variant)| { + CompletionItem::new(CompletionKind::Reference, name.to_string()) + .kind(CompletionItemKind::EnumVariant) + .add_to(acc) + }), _ => return Ok(()), }; - let module_scope = target_module.scope(ctx.db)?; - module_scope.entries().for_each(|(name, res)| { - CompletionItem::new(CompletionKind::Reference, name.to_string()) - .from_resolution(ctx.db, res) - .add_to(acc) - }); Ok(()) } @@ -92,4 +101,16 @@ mod tests { "Spam", ); } + + #[test] + fn completes_enum_variant() { + check_reference_completion( + " + //- /lib.rs + enum E { Foo, Bar(i32) } + fn foo() { let _ = E::<|> } + ", + "Foo;Bar", + ); + } } diff --git a/crates/ra_analysis/src/completion/completion_item.rs b/crates/ra_analysis/src/completion/completion_item.rs index c9f9f495d..1d294c553 100644 --- a/crates/ra_analysis/src/completion/completion_item.rs +++ b/crates/ra_analysis/src/completion/completion_item.rs @@ -29,6 +29,7 @@ pub enum CompletionItemKind { Function, Struct, Enum, + EnumVariant, Binding, Field, } -- cgit v1.2.3