diff options
Diffstat (limited to 'crates/ra_analysis/src')
-rw-r--r-- | crates/ra_analysis/src/completion/complete_path.rs | 39 | ||||
-rw-r--r-- | crates/ra_analysis/src/completion/completion_item.rs | 1 | ||||
-rw-r--r-- | crates/ra_analysis/src/extend_selection.rs | 44 | ||||
-rw-r--r-- | crates/ra_analysis/src/macros.rs | 11 | ||||
-rw-r--r-- | crates/ra_analysis/src/mock_analysis.rs | 17 |
5 files changed, 99 insertions, 13 deletions
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 @@ | |||
1 | use crate::{ | 1 | use crate::{ |
2 | Cancelable, | 2 | Cancelable, |
3 | completion::{CompletionItem, Completions, CompletionKind, CompletionContext}, | 3 | completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext}, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> { | 6 | 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 | |||
12 | Some(it) => it, | 12 | Some(it) => it, |
13 | None => return Ok(()), | 13 | None => return Ok(()), |
14 | }; | 14 | }; |
15 | let target_module = match def_id.resolve(ctx.db)? { | 15 | match def_id.resolve(ctx.db)? { |
16 | hir::Def::Module(it) => it, | 16 | hir::Def::Module(module) => { |
17 | let module_scope = module.scope(ctx.db)?; | ||
18 | module_scope.entries().for_each(|(name, res)| { | ||
19 | CompletionItem::new(CompletionKind::Reference, name.to_string()) | ||
20 | .from_resolution(ctx.db, res) | ||
21 | .add_to(acc) | ||
22 | }); | ||
23 | } | ||
24 | hir::Def::Enum(e) => e | ||
25 | .variants(ctx.db)? | ||
26 | .into_iter() | ||
27 | .for_each(|(name, _variant)| { | ||
28 | CompletionItem::new(CompletionKind::Reference, name.to_string()) | ||
29 | .kind(CompletionItemKind::EnumVariant) | ||
30 | .add_to(acc) | ||
31 | }), | ||
17 | _ => return Ok(()), | 32 | _ => return Ok(()), |
18 | }; | 33 | }; |
19 | let module_scope = target_module.scope(ctx.db)?; | ||
20 | module_scope.entries().for_each(|(name, res)| { | ||
21 | CompletionItem::new(CompletionKind::Reference, name.to_string()) | ||
22 | .from_resolution(ctx.db, res) | ||
23 | .add_to(acc) | ||
24 | }); | ||
25 | Ok(()) | 34 | Ok(()) |
26 | } | 35 | } |
27 | 36 | ||
@@ -92,4 +101,16 @@ mod tests { | |||
92 | "Spam", | 101 | "Spam", |
93 | ); | 102 | ); |
94 | } | 103 | } |
104 | |||
105 | #[test] | ||
106 | fn completes_enum_variant() { | ||
107 | check_reference_completion( | ||
108 | " | ||
109 | //- /lib.rs | ||
110 | enum E { Foo, Bar(i32) } | ||
111 | fn foo() { let _ = E::<|> } | ||
112 | ", | ||
113 | "Foo;Bar", | ||
114 | ); | ||
115 | } | ||
95 | } | 116 | } |
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 { | |||
29 | Function, | 29 | Function, |
30 | Struct, | 30 | Struct, |
31 | Enum, | 31 | Enum, |
32 | EnumVariant, | ||
32 | Binding, | 33 | Binding, |
33 | Field, | 34 | Field, |
34 | } | 35 | } |
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 @@ | |||
1 | use ra_db::SyntaxDatabase; | 1 | use ra_db::SyntaxDatabase; |
2 | use ra_syntax::{ | ||
3 | SyntaxNodeRef, AstNode, | ||
4 | ast, algo::find_covering_node, | ||
5 | }; | ||
2 | 6 | ||
3 | use crate::{ | 7 | use crate::{ |
4 | TextRange, FileRange, | 8 | TextRange, FileRange, |
@@ -6,6 +10,42 @@ use crate::{ | |||
6 | }; | 10 | }; |
7 | 11 | ||
8 | pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { | 12 | pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { |
9 | let file = db.source_file(frange.file_id); | 13 | let source_file = db.source_file(frange.file_id); |
10 | ra_editor::extend_selection(&file, frange.range).unwrap_or(frange.range) | 14 | if let Some(macro_call) = find_macro_call(source_file.syntax(), frange.range) { |
15 | if let Some(exp) = crate::macros::expand(db, frange.file_id, macro_call) { | ||
16 | if let Some(dst_range) = exp.map_range_forward(frange.range) { | ||
17 | if let Some(dst_range) = ra_editor::extend_selection(exp.source_file(), dst_range) { | ||
18 | if let Some(src_range) = exp.map_range_back(dst_range) { | ||
19 | return src_range; | ||
20 | } | ||
21 | } | ||
22 | } | ||
23 | } | ||
24 | } | ||
25 | ra_editor::extend_selection(&source_file, frange.range).unwrap_or(frange.range) | ||
26 | } | ||
27 | |||
28 | fn find_macro_call(node: SyntaxNodeRef, range: TextRange) -> Option<ast::MacroCall> { | ||
29 | find_covering_node(node, range) | ||
30 | .ancestors() | ||
31 | .find_map(ast::MacroCall::cast) | ||
32 | } | ||
33 | |||
34 | #[cfg(test)] | ||
35 | mod tests { | ||
36 | use crate::mock_analysis::single_file_with_range; | ||
37 | use test_utils::assert_eq_dbg; | ||
38 | |||
39 | #[test] | ||
40 | fn extend_selection_inside_macros() { | ||
41 | let (analysis, frange) = single_file_with_range( | ||
42 | " | ||
43 | fn main() { | ||
44 | ctry!(foo(|x| <|>x<|>)); | ||
45 | } | ||
46 | ", | ||
47 | ); | ||
48 | let r = analysis.extend_selection(frange); | ||
49 | assert_eq_dbg("[51; 56)", &r); | ||
50 | } | ||
11 | } | 51 | } |
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 { | |||
61 | } | 61 | } |
62 | None | 62 | None |
63 | } | 63 | } |
64 | pub(crate) fn map_range_forward(&self, src_range: TextRange) -> Option<TextRange> { | ||
65 | for (s_range, t_range) in self.ranges_map.iter() { | ||
66 | if src_range.is_subrange(&s_range) { | ||
67 | let src_at_zero_range = src_range - src_range.start(); | ||
68 | let src_range_offset = src_range.start() - s_range.start(); | ||
69 | let src_range = src_at_zero_range + src_range_offset + t_range.start(); | ||
70 | return Some(src_range); | ||
71 | } | ||
72 | } | ||
73 | None | ||
74 | } | ||
64 | } | 75 | } |
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 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use relative_path::RelativePathBuf; | 3 | use relative_path::RelativePathBuf; |
4 | use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; | 4 | use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; |
5 | use ra_db::mock::FileMap; | 5 | use ra_db::mock::FileMap; |
6 | 6 | ||
7 | use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FilePosition, SourceRootId}; | 7 | use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FilePosition, FileRange, SourceRootId}; |
8 | 8 | ||
9 | /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis | 9 | /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis |
10 | /// from a set of in-memory files. | 10 | /// from a set of in-memory files. |
@@ -66,6 +66,12 @@ impl MockAnalysis { | |||
66 | self.files.push((path.to_string(), text.to_string())); | 66 | self.files.push((path.to_string(), text.to_string())); |
67 | FilePosition { file_id, offset } | 67 | FilePosition { file_id, offset } |
68 | } | 68 | } |
69 | pub fn add_file_with_range(&mut self, path: &str, text: &str) -> FileRange { | ||
70 | let (range, text) = extract_range(text); | ||
71 | let file_id = FileId((self.files.len() + 1) as u32); | ||
72 | self.files.push((path.to_string(), text.to_string())); | ||
73 | FileRange { file_id, range } | ||
74 | } | ||
69 | pub fn id_of(&self, path: &str) -> FileId { | 75 | pub fn id_of(&self, path: &str) -> FileId { |
70 | let (idx, _) = self | 76 | let (idx, _) = self |
71 | .files | 77 | .files |
@@ -115,3 +121,10 @@ pub fn single_file_with_position(code: &str) -> (Analysis, FilePosition) { | |||
115 | let pos = mock.add_file_with_position("/main.rs", code); | 121 | let pos = mock.add_file_with_position("/main.rs", code); |
116 | (mock.analysis(), pos) | 122 | (mock.analysis(), pos) |
117 | } | 123 | } |
124 | |||
125 | /// Creates analysis for a single file, returns range marked with a pair of <|>. | ||
126 | pub fn single_file_with_range(code: &str) -> (Analysis, FileRange) { | ||
127 | let mut mock = MockAnalysis::new(); | ||
128 | let pos = mock.add_file_with_range("/main.rs", code); | ||
129 | (mock.analysis(), pos) | ||
130 | } | ||