|
1 | 1 | use crate::vhdl_server::{srcpos_to_location, to_symbol_kind, uri_to_file_name, VHDLServer}; |
| 2 | +use fuzzy_matcher::FuzzyMatcher; |
2 | 3 | use lsp_types::{ |
3 | 4 | DidChangeWatchedFilesParams, OneOf, WorkspaceSymbol, WorkspaceSymbolParams, |
4 | 5 | WorkspaceSymbolResponse, |
5 | 6 | }; |
| 7 | +use std::cmp::Ordering; |
| 8 | +use std::collections::BinaryHeap; |
6 | 9 | use vhdl_lang::ast::Designator; |
7 | | -use vhdl_lang::Message; |
| 10 | +use vhdl_lang::{EntRef, Message}; |
8 | 11 |
|
9 | 12 | impl VHDLServer { |
10 | 13 | pub fn workspace_did_change_watched_files(&mut self, params: &DidChangeWatchedFilesParams) { |
@@ -32,39 +35,77 @@ impl VHDLServer { |
32 | 35 | params: &WorkspaceSymbolParams, |
33 | 36 | ) -> Option<WorkspaceSymbolResponse> { |
34 | 37 | let trunc_limit = 200; |
35 | | - let query = params.query.to_ascii_lowercase(); |
36 | | - let mut symbols: Vec<_> = self |
| 38 | + let query = params.query.clone(); |
| 39 | + let symbols = self |
37 | 40 | .project |
38 | 41 | .public_symbols() |
39 | 42 | .filter_map(|ent| match ent.designator() { |
40 | 43 | Designator::Identifier(_) | Designator::Character(_) => { |
41 | | - Some((ent, ent.designator().to_string().to_ascii_lowercase())) |
| 44 | + Some((ent, ent.designator().to_string())) |
42 | 45 | } |
43 | | - Designator::OperatorSymbol(op) => Some((ent, op.to_string().to_ascii_lowercase())), |
| 46 | + Designator::OperatorSymbol(op) => Some((ent, op.to_string())), |
44 | 47 | Designator::Anonymous(_) => None, |
45 | | - }) |
46 | | - .collect(); |
47 | | - symbols.sort_by(|(_, n1), (_, n2)| n1.cmp(n2)); |
| 48 | + }); |
| 49 | + |
48 | 50 | Some(WorkspaceSymbolResponse::Nested( |
49 | | - symbols |
50 | | - .into_iter() |
51 | | - .filter_map(|(ent, name)| { |
52 | | - let decl_pos = ent.decl_pos()?; |
53 | | - if name.starts_with(&query) { |
54 | | - Some(WorkspaceSymbol { |
| 51 | + self.filter_workspace_symbols(symbols.into_iter(), &query, trunc_limit), |
| 52 | + )) |
| 53 | + } |
| 54 | + |
| 55 | + /// Filters found workspace symbols according to a given query. |
| 56 | + /// This uses a fuzzy matcher internally to improve the results. |
| 57 | + /// Queries 'close' to the target string will score high and be included in the |
| 58 | + /// returned vec, while queries 'not close' to the target string will be omitted. |
| 59 | + /// The returned vec is sorted according to the score of the fuzzy matcher. |
| 60 | + fn filter_workspace_symbols<'a>( |
| 61 | + &self, |
| 62 | + symbols: impl Iterator<Item = (EntRef<'a>, String)>, |
| 63 | + query: &str, |
| 64 | + trunc_limit: usize, |
| 65 | + ) -> Vec<WorkspaceSymbol> { |
| 66 | + #[derive(Eq, PartialEq)] |
| 67 | + struct WorkspaceSymbolWithScore { |
| 68 | + symbol: WorkspaceSymbol, |
| 69 | + score: i64, |
| 70 | + } |
| 71 | + |
| 72 | + impl PartialOrd<Self> for WorkspaceSymbolWithScore { |
| 73 | + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
| 74 | + Some(self.cmp(other)) |
| 75 | + } |
| 76 | + } |
| 77 | + |
| 78 | + impl Ord for WorkspaceSymbolWithScore { |
| 79 | + fn cmp(&self, other: &Self) -> Ordering { |
| 80 | + self.score.cmp(&other.score) |
| 81 | + } |
| 82 | + } |
| 83 | + |
| 84 | + let symbols_with_scores: BinaryHeap<_> = symbols |
| 85 | + .into_iter() |
| 86 | + .filter_map(|(ent, name)| { |
| 87 | + let decl_pos = ent.decl_pos()?; |
| 88 | + self.string_matcher.fuzzy_match(&name, query).map(|score| { |
| 89 | + WorkspaceSymbolWithScore { |
| 90 | + symbol: WorkspaceSymbol { |
55 | 91 | name: ent.describe(), |
56 | 92 | kind: to_symbol_kind(ent.kind()), |
57 | 93 | tags: None, |
58 | 94 | container_name: ent.parent.map(|ent| ent.path_name()), |
59 | 95 | location: OneOf::Left(srcpos_to_location(decl_pos)), |
60 | 96 | data: None, |
61 | | - }) |
62 | | - } else { |
63 | | - None |
| 97 | + }, |
| 98 | + score, |
64 | 99 | } |
65 | 100 | }) |
66 | | - .take(trunc_limit) |
67 | | - .collect(), |
68 | | - )) |
| 101 | + }) |
| 102 | + .take(trunc_limit) |
| 103 | + .collect(); |
| 104 | + symbols_with_scores |
| 105 | + .into_sorted_vec() |
| 106 | + .into_iter() |
| 107 | + .rev() |
| 108 | + .map(|wsws| wsws.symbol) |
| 109 | + .collect() |
69 | 110 | } |
70 | 111 | } |
0 commit comments