diff options
-rw-r--r-- | crates/ra_ide_api/src/symbol_index.rs | 88 |
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. |
22 | use std::{ | 22 | use 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 | ||
138 | impl SymbolIndex { | 137 | impl 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 | ||
168 | impl Query { | 199 | impl 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)] |
275 | mod tests { | 307 | mod 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#" | ||
364 | fn foo() {} | ||
365 | |||
366 | struct 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() |