aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ssr/src/search.rs
diff options
context:
space:
mode:
authorDavid Lattimore <[email protected]>2020-07-29 02:44:01 +0100
committerDavid Lattimore <[email protected]>2020-07-29 06:06:58 +0100
commitcf55806257776baf7db6b02d260bdaa9e851c7d4 (patch)
tree6827c094a4a27631ee9b1b97d98a8ac9c0a56aac /crates/ra_ssr/src/search.rs
parent5a8124273dd663f7f1ed43b53defc4a2c52dbc12 (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.rs87
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};
8use ra_db::FileRange; 8use ra_db::{FileId, FileRange};
9use ra_ide_db::{ 9use ra_ide_db::{
10 defs::Definition, 10 defs::Definition,
11 search::{Reference, SearchScope}, 11 search::{Reference, SearchScope},
12}; 12};
13use ra_syntax::{ast, AstNode, SyntaxKind, SyntaxNode}; 13use ra_syntax::{ast, AstNode, SyntaxKind, SyntaxNode};
14use rustc_hash::FxHashSet;
14use test_utils::mark; 15use 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.