Skip to content

Commit 6009af9

Browse files
bors[bot]jrvidal
andcommitted
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]>
2 parents a71d0ec + dd8c384 commit 6009af9

File tree

1 file changed

+70
-18
lines changed

1 file changed

+70
-18
lines changed

crates/ra_ide_api/src/symbol_index.rs

Lines changed: 70 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
//! file in the current workspace, and run a query against the union of all
2121
//! those FSTs.
2222
use std::{
23-
cmp::Ordering,
2423
hash::{Hash, Hasher},
2524
sync::Arc,
2625
mem,
@@ -137,13 +136,32 @@ impl Hash for SymbolIndex {
137136

138137
impl SymbolIndex {
139138
fn new(mut symbols: Vec<FileSymbol>) -> SymbolIndex {
140-
fn cmp(s1: &FileSymbol, s2: &FileSymbol) -> Ordering {
141-
unicase::Ascii::new(s1.name.as_str()).cmp(&unicase::Ascii::new(s2.name.as_str()))
139+
fn cmp_key<'a>(s1: &'a FileSymbol) -> impl Ord + 'a {
140+
unicase::Ascii::new(s1.name.as_str())
142141
}
143-
symbols.par_sort_by(cmp);
144-
symbols.dedup_by(|s1, s2| cmp(s1, s2) == Ordering::Equal);
145-
let names = symbols.iter().map(|it| it.name.as_str().to_lowercase());
146-
let map = fst::Map::from_iter(names.zip(0u64..)).unwrap();
142+
143+
symbols.par_sort_by(|s1, s2| cmp_key(s1).cmp(&cmp_key(s2)));
144+
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();
147165
SymbolIndex { symbols, map }
148166
}
149167

@@ -163,6 +181,19 @@ impl SymbolIndex {
163181
.collect::<Vec<_>>();
164182
SymbolIndex::new(symbols)
165183
}
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+
}
166197
}
167198

168199
impl Query {
@@ -179,17 +210,18 @@ impl Query {
179210
break;
180211
}
181212
for indexed_value in indexed_values {
182-
let file_symbols = &indices[indexed_value.index];
183-
let idx = indexed_value.value as usize;
184-
185-
let symbol = &file_symbols.symbols[idx];
186-
if self.only_types && !is_type(symbol.ptr.kind()) {
187-
continue;
188-
}
189-
if self.exact && symbol.name != self.query {
190-
continue;
213+
let symbol_index = &indices[indexed_value.index];
214+
let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value);
215+
216+
for symbol in &symbol_index.symbols[start..end] {
217+
if self.only_types && !is_type(symbol.ptr.kind()) {
218+
continue;
219+
}
220+
if self.exact && symbol.name != self.query {
221+
continue;
222+
}
223+
res.push(symbol.clone());
191224
}
192-
res.push(symbol.clone());
193225
}
194226
}
195227
res
@@ -273,7 +305,10 @@ fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> {
273305

274306
#[cfg(test)]
275307
mod tests {
276-
use ra_syntax::SmolStr;
308+
use ra_syntax::{
309+
SmolStr,
310+
SyntaxKind::{FN_DEF, STRUCT_DEF}
311+
};
277312
use crate::{
278313
display::NavigationTarget,
279314
mock_analysis::single_file,
@@ -323,6 +358,23 @@ mod foo {
323358
assert_eq!(s.container_name(), Some(&SmolStr::new("foo")));
324359
}
325360

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+
326378
fn get_symbols_matching(text: &str, query: &str) -> Vec<NavigationTarget> {
327379
let (analysis, _) = single_file(text);
328380
analysis.symbol_search(Query::new(query.into())).unwrap()

0 commit comments

Comments
 (0)