aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-04-23 22:25:37 +0100
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-04-23 22:25:37 +0100
commit6009af9b7c1270e612b3a21118283b9bc6925aac (patch)
treed904400b3930ec17a81f4e146feae40282504d62 /crates/ra_ide_api
parenta71d0ecd774008bbfe8cb2215ffa6dedd5024877 (diff)
parentdd8c3840cbed2c204a71dd5baec3dd4a3194806e (diff)
Merge #1200
1200: Allows searching for case-equivalent symbols (fixes #1151) r=matklad a=jrvidal I couldn't find a nice, functional way of calculating the ranges in one pass so I resorted to a plain old `for` loop. Co-authored-by: Roberto Vidal <[email protected]>
Diffstat (limited to 'crates/ra_ide_api')
-rw-r--r--crates/ra_ide_api/src/symbol_index.rs88
1 files changed, 70 insertions, 18 deletions
diff --git a/crates/ra_ide_api/src/symbol_index.rs b/crates/ra_ide_api/src/symbol_index.rs
index 914d3fc71..1dcff8beb 100644
--- a/crates/ra_ide_api/src/symbol_index.rs
+++ b/crates/ra_ide_api/src/symbol_index.rs
@@ -20,7 +20,6 @@
20//! file in the current workspace, and run a query against the union of all 20//! file in the current workspace, and run a query against the union of all
21//! those FSTs. 21//! those FSTs.
22use std::{ 22use std::{
23 cmp::Ordering,
24 hash::{Hash, Hasher}, 23 hash::{Hash, Hasher},
25 sync::Arc, 24 sync::Arc,
26 mem, 25 mem,
@@ -137,13 +136,32 @@ impl Hash for SymbolIndex {
137 136
138impl SymbolIndex { 137impl SymbolIndex {
139 fn new(mut symbols: Vec<FileSymbol>) -> SymbolIndex { 138 fn new(mut symbols: Vec<FileSymbol>) -> SymbolIndex {
140 fn cmp(s1: &FileSymbol, s2: &FileSymbol) -> Ordering { 139 fn cmp_key<'a>(s1: &'a FileSymbol) -> impl Ord + 'a {
141 unicase::Ascii::new(s1.name.as_str()).cmp(&unicase::Ascii::new(s2.name.as_str())) 140 unicase::Ascii::new(s1.name.as_str())
142 } 141 }
143 symbols.par_sort_by(cmp); 142
144 symbols.dedup_by(|s1, s2| cmp(s1, s2) == Ordering::Equal); 143 symbols.par_sort_by(|s1, s2| cmp_key(s1).cmp(&cmp_key(s2)));
145 let names = symbols.iter().map(|it| it.name.as_str().to_lowercase()); 144
146 let map = fst::Map::from_iter(names.zip(0u64..)).unwrap(); 145 let mut builder = fst::MapBuilder::memory();
146
147 let mut last_batch_start = 0;
148
149 for idx in 0..symbols.len() {
150 if symbols.get(last_batch_start).map(cmp_key) == symbols.get(idx + 1).map(cmp_key) {
151 continue;
152 }
153
154 let start = last_batch_start;
155 let end = idx + 1;
156 last_batch_start = end;
157
158 let key = symbols[start].name.as_str().to_lowercase();
159 let value = SymbolIndex::range_to_map_value(start, end);
160
161 builder.insert(key, value).unwrap();
162 }
163
164 let map = fst::Map::from_bytes(builder.into_inner().unwrap()).unwrap();
147 SymbolIndex { symbols, map } 165 SymbolIndex { symbols, map }
148 } 166 }
149 167
@@ -163,6 +181,19 @@ impl SymbolIndex {
163 .collect::<Vec<_>>(); 181 .collect::<Vec<_>>();
164 SymbolIndex::new(symbols) 182 SymbolIndex::new(symbols)
165 } 183 }
184
185 fn range_to_map_value(start: usize, end: usize) -> u64 {
186 debug_assert![start <= (std::u32::MAX as usize)];
187 debug_assert![end <= (std::u32::MAX as usize)];
188
189 ((start as u64) << 32) | end as u64
190 }
191
192 fn map_value_to_range(value: u64) -> (usize, usize) {
193 let end = value as u32 as usize;
194 let start = (value >> 32) as usize;
195 (start, end)
196 }
166} 197}
167 198
168impl Query { 199impl Query {
@@ -179,17 +210,18 @@ impl Query {
179 break; 210 break;
180 } 211 }
181 for indexed_value in indexed_values { 212 for indexed_value in indexed_values {
182 let file_symbols = &indices[indexed_value.index]; 213 let symbol_index = &indices[indexed_value.index];
183 let idx = indexed_value.value as usize; 214 let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value);
184 215
185 let symbol = &file_symbols.symbols[idx]; 216 for symbol in &symbol_index.symbols[start..end] {
186 if self.only_types && !is_type(symbol.ptr.kind()) { 217 if self.only_types && !is_type(symbol.ptr.kind()) {
187 continue; 218 continue;
188 } 219 }
189 if self.exact && symbol.name != self.query { 220 if self.exact && symbol.name != self.query {
190 continue; 221 continue;
222 }
223 res.push(symbol.clone());
191 } 224 }
192 res.push(symbol.clone());
193 } 225 }
194 } 226 }
195 res 227 res
@@ -273,7 +305,10 @@ fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> {
273 305
274#[cfg(test)] 306#[cfg(test)]
275mod tests { 307mod tests {
276 use ra_syntax::SmolStr; 308 use ra_syntax::{
309 SmolStr,
310 SyntaxKind::{FN_DEF, STRUCT_DEF}
311};
277 use crate::{ 312 use crate::{
278 display::NavigationTarget, 313 display::NavigationTarget,
279 mock_analysis::single_file, 314 mock_analysis::single_file,
@@ -323,6 +358,23 @@ mod foo {
323 assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); 358 assert_eq!(s.container_name(), Some(&SmolStr::new("foo")));
324 } 359 }
325 360
361 #[test]
362 fn test_world_symbols_are_case_sensitive() {
363 let code = r#"
364fn foo() {}
365
366struct Foo;
367 "#;
368
369 let symbols = get_symbols_matching(code, "Foo");
370
371 let fn_match = symbols.iter().find(|s| s.name() == "foo").map(|s| s.kind());
372 let struct_match = symbols.iter().find(|s| s.name() == "Foo").map(|s| s.kind());
373
374 assert_eq!(fn_match, Some(FN_DEF));
375 assert_eq!(struct_match, Some(STRUCT_DEF));
376 }
377
326 fn get_symbols_matching(text: &str, query: &str) -> Vec<NavigationTarget> { 378 fn get_symbols_matching(text: &str, query: &str) -> Vec<NavigationTarget> {
327 let (analysis, _) = single_file(text); 379 let (analysis, _) = single_file(text);
328 analysis.symbol_search(Query::new(query.into())).unwrap() 380 analysis.symbol_search(Query::new(query.into())).unwrap()