diff options
Diffstat (limited to 'crates/ra_ssr/src/search.rs')
-rw-r--r-- | crates/ra_ssr/src/search.rs | 87 |
1 files changed, 64 insertions, 23 deletions
diff --git a/crates/ra_ssr/src/search.rs b/crates/ra_ssr/src/search.rs index bcf0f0468..0f512cb62 100644 --- a/crates/ra_ssr/src/search.rs +++ b/crates/ra_ssr/src/search.rs | |||
@@ -5,12 +5,13 @@ use crate::{ | |||
5 | resolving::{ResolvedPath, ResolvedPattern, ResolvedRule}, | 5 | resolving::{ResolvedPath, ResolvedPattern, ResolvedRule}, |
6 | Match, MatchFinder, | 6 | Match, MatchFinder, |
7 | }; | 7 | }; |
8 | use ra_db::FileRange; | 8 | use ra_db::{FileId, FileRange}; |
9 | use ra_ide_db::{ | 9 | use ra_ide_db::{ |
10 | defs::Definition, | 10 | defs::Definition, |
11 | search::{Reference, SearchScope}, | 11 | search::{Reference, SearchScope}, |
12 | }; | 12 | }; |
13 | use ra_syntax::{ast, AstNode, SyntaxKind, SyntaxNode}; | 13 | use ra_syntax::{ast, AstNode, SyntaxKind, SyntaxNode}; |
14 | use rustc_hash::FxHashSet; | ||
14 | use test_utils::mark; | 15 | use test_utils::mark; |
15 | 16 | ||
16 | /// A cache for the results of find_usages. This is for when we have multiple patterns that have the | 17 | /// A cache for the results of find_usages. This is for when we have multiple patterns that have the |
@@ -54,11 +55,7 @@ impl<'db> MatchFinder<'db> { | |||
54 | mark::hit!(use_declaration_with_braces); | 55 | mark::hit!(use_declaration_with_braces); |
55 | continue; | 56 | continue; |
56 | } | 57 | } |
57 | if let Ok(m) = | 58 | self.try_add_match(rule, &node_to_match, &None, matches_out); |
58 | matching::get_match(false, rule, &node_to_match, &None, &self.sema) | ||
59 | { | ||
60 | matches_out.push(m); | ||
61 | } | ||
62 | } | 59 | } |
63 | } | 60 | } |
64 | } | 61 | } |
@@ -121,25 +118,39 @@ impl<'db> MatchFinder<'db> { | |||
121 | // FIXME: We should ideally have a test that checks that we edit local roots and not library | 118 | // FIXME: We should ideally have a test that checks that we edit local roots and not library |
122 | // roots. This probably would require some changes to fixtures, since currently everything | 119 | // roots. This probably would require some changes to fixtures, since currently everything |
123 | // seems to get put into a single source root. | 120 | // seems to get put into a single source root. |
124 | use ra_db::SourceDatabaseExt; | ||
125 | use ra_ide_db::symbol_index::SymbolsDatabase; | ||
126 | let mut files = Vec::new(); | 121 | let mut files = Vec::new(); |
127 | for &root in self.sema.db.local_roots().iter() { | 122 | self.search_files_do(|file_id| { |
128 | let sr = self.sema.db.source_root(root); | 123 | files.push(file_id); |
129 | files.extend(sr.iter()); | 124 | }); |
130 | } | ||
131 | SearchScope::files(&files) | 125 | SearchScope::files(&files) |
132 | } | 126 | } |
133 | 127 | ||
134 | fn slow_scan(&self, rule: &ResolvedRule, matches_out: &mut Vec<Match>) { | 128 | fn slow_scan(&self, rule: &ResolvedRule, matches_out: &mut Vec<Match>) { |
135 | use ra_db::SourceDatabaseExt; | 129 | self.search_files_do(|file_id| { |
136 | use ra_ide_db::symbol_index::SymbolsDatabase; | 130 | let file = self.sema.parse(file_id); |
137 | for &root in self.sema.db.local_roots().iter() { | 131 | let code = file.syntax(); |
138 | let sr = self.sema.db.source_root(root); | 132 | self.slow_scan_node(code, rule, &None, matches_out); |
139 | for file_id in sr.iter() { | 133 | }) |
140 | let file = self.sema.parse(file_id); | 134 | } |
141 | let code = file.syntax(); | 135 | |
142 | self.slow_scan_node(code, rule, &None, matches_out); | 136 | fn search_files_do(&self, mut callback: impl FnMut(FileId)) { |
137 | if self.restrict_ranges.is_empty() { | ||
138 | // Unrestricted search. | ||
139 | use ra_db::SourceDatabaseExt; | ||
140 | use ra_ide_db::symbol_index::SymbolsDatabase; | ||
141 | for &root in self.sema.db.local_roots().iter() { | ||
142 | let sr = self.sema.db.source_root(root); | ||
143 | for file_id in sr.iter() { | ||
144 | callback(file_id); | ||
145 | } | ||
146 | } | ||
147 | } else { | ||
148 | // Search is restricted, deduplicate file IDs (generally only one). | ||
149 | let mut files = FxHashSet::default(); | ||
150 | for range in &self.restrict_ranges { | ||
151 | if files.insert(range.file_id) { | ||
152 | callback(range.file_id); | ||
153 | } | ||
143 | } | 154 | } |
144 | } | 155 | } |
145 | } | 156 | } |
@@ -154,9 +165,7 @@ impl<'db> MatchFinder<'db> { | |||
154 | if !is_search_permitted(code) { | 165 | if !is_search_permitted(code) { |
155 | return; | 166 | return; |
156 | } | 167 | } |
157 | if let Ok(m) = matching::get_match(false, rule, &code, restrict_range, &self.sema) { | 168 | self.try_add_match(rule, &code, restrict_range, matches_out); |
158 | matches_out.push(m); | ||
159 | } | ||
160 | // If we've got a macro call, we already tried matching it pre-expansion, which is the only | 169 | // If we've got a macro call, we already tried matching it pre-expansion, which is the only |
161 | // way to match the whole macro, now try expanding it and matching the expansion. | 170 | // way to match the whole macro, now try expanding it and matching the expansion. |
162 | if let Some(macro_call) = ast::MacroCall::cast(code.clone()) { | 171 | if let Some(macro_call) = ast::MacroCall::cast(code.clone()) { |
@@ -178,6 +187,38 @@ impl<'db> MatchFinder<'db> { | |||
178 | self.slow_scan_node(&child, rule, restrict_range, matches_out); | 187 | self.slow_scan_node(&child, rule, restrict_range, matches_out); |
179 | } | 188 | } |
180 | } | 189 | } |
190 | |||
191 | fn try_add_match( | ||
192 | &self, | ||
193 | rule: &ResolvedRule, | ||
194 | code: &SyntaxNode, | ||
195 | restrict_range: &Option<FileRange>, | ||
196 | matches_out: &mut Vec<Match>, | ||
197 | ) { | ||
198 | if !self.within_range_restrictions(code) { | ||
199 | mark::hit!(replace_nonpath_within_selection); | ||
200 | return; | ||
201 | } | ||
202 | if let Ok(m) = matching::get_match(false, rule, code, restrict_range, &self.sema) { | ||
203 | matches_out.push(m); | ||
204 | } | ||
205 | } | ||
206 | |||
207 | /// Returns whether `code` is within one of our range restrictions if we have any. No range | ||
208 | /// restrictions is considered unrestricted and always returns true. | ||
209 | fn within_range_restrictions(&self, code: &SyntaxNode) -> bool { | ||
210 | if self.restrict_ranges.is_empty() { | ||
211 | // There is no range restriction. | ||
212 | return true; | ||
213 | } | ||
214 | let node_range = self.sema.original_range(code); | ||
215 | for range in &self.restrict_ranges { | ||
216 | if range.file_id == node_range.file_id && range.range.contains_range(node_range.range) { | ||
217 | return true; | ||
218 | } | ||
219 | } | ||
220 | false | ||
221 | } | ||
181 | } | 222 | } |
182 | 223 | ||
183 | /// Returns whether we support matching within `node` and all of its ancestors. | 224 | /// Returns whether we support matching within `node` and all of its ancestors. |