diff options
Diffstat (limited to 'crates/ra_analysis')
-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 |
3 files changed, 68 insertions, 4 deletions
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 | } | ||