diff options
author | David Lattimore <[email protected]> | 2020-07-29 02:44:01 +0100 |
---|---|---|
committer | David Lattimore <[email protected]> | 2020-07-29 06:06:58 +0100 |
commit | cf55806257776baf7db6b02d260bdaa9e851c7d4 (patch) | |
tree | 6827c094a4a27631ee9b1b97d98a8ac9c0a56aac /crates/ra_ssr/src/search.rs | |
parent | 5a8124273dd663f7f1ed43b53defc4a2c52dbc12 (diff) |
SSR: Restrict to current selection if any
The selection is also used to avoid unnecessary work, but only to the
file level. Further restricting unnecessary work is left for later.
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. |