@@ -5,12 +5,13 @@ use crate::{
5
5
resolving:: { ResolvedPath , ResolvedPattern , ResolvedRule } ,
6
6
Match , MatchFinder ,
7
7
} ;
8
- use ra_db:: FileRange ;
8
+ use ra_db:: { FileId , FileRange } ;
9
9
use ra_ide_db:: {
10
10
defs:: Definition ,
11
11
search:: { Reference , SearchScope } ,
12
12
} ;
13
13
use ra_syntax:: { ast, AstNode , SyntaxKind , SyntaxNode } ;
14
+ use rustc_hash:: FxHashSet ;
14
15
use test_utils:: mark;
15
16
16
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
55
mark:: hit!( use_declaration_with_braces) ;
55
56
continue ;
56
57
}
57
- if let Ok ( m) =
58
- matching:: get_match ( false , rule, & node_to_match, & None , & self . sema )
59
- {
60
- matches_out. push ( m) ;
61
- }
58
+ self . try_add_match ( rule, & node_to_match, & None , matches_out) ;
62
59
}
63
60
}
64
61
}
@@ -121,25 +118,39 @@ impl<'db> MatchFinder<'db> {
121
118
// FIXME: We should ideally have a test that checks that we edit local roots and not library
122
119
// roots. This probably would require some changes to fixtures, since currently everything
123
120
// seems to get put into a single source root.
124
- use ra_db:: SourceDatabaseExt ;
125
- use ra_ide_db:: symbol_index:: SymbolsDatabase ;
126
121
let mut files = Vec :: new ( ) ;
127
- for & root in self . sema . db . local_roots ( ) . iter ( ) {
128
- let sr = self . sema . db . source_root ( root) ;
129
- files. extend ( sr. iter ( ) ) ;
130
- }
122
+ self . search_files_do ( |file_id| {
123
+ files. push ( file_id) ;
124
+ } ) ;
131
125
SearchScope :: files ( & files)
132
126
}
133
127
134
128
fn slow_scan ( & self , rule : & ResolvedRule , matches_out : & mut Vec < Match > ) {
135
- use ra_db:: SourceDatabaseExt ;
136
- use ra_ide_db:: symbol_index:: SymbolsDatabase ;
137
- for & root in self . sema . db . local_roots ( ) . iter ( ) {
138
- let sr = self . sema . db . source_root ( root) ;
139
- for file_id in sr. iter ( ) {
140
- let file = self . sema . parse ( file_id) ;
141
- let code = file. syntax ( ) ;
142
- self . slow_scan_node ( code, rule, & None , matches_out) ;
129
+ self . search_files_do ( |file_id| {
130
+ let file = self . sema . parse ( file_id) ;
131
+ let code = file. syntax ( ) ;
132
+ self . slow_scan_node ( code, rule, & None , matches_out) ;
133
+ } )
134
+ }
135
+
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
165
if !is_search_permitted ( code) {
155
166
return ;
156
167
}
157
- if let Ok ( m) = matching:: get_match ( false , rule, & code, restrict_range, & self . sema ) {
158
- matches_out. push ( m) ;
159
- }
168
+ self . try_add_match ( rule, & code, restrict_range, matches_out) ;
160
169
// If we've got a macro call, we already tried matching it pre-expansion, which is the only
161
170
// way to match the whole macro, now try expanding it and matching the expansion.
162
171
if let Some ( macro_call) = ast:: MacroCall :: cast ( code. clone ( ) ) {
@@ -178,6 +187,38 @@ impl<'db> MatchFinder<'db> {
178
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
224
/// Returns whether we support matching within `node` and all of its ancestors.
0 commit comments