Skip to content

Commit 819d2f7

Browse files
authored
project_symbols: Support rust-analyzer path symbol search (#46511)
cc rust-lang/rust-analyzer#21415 Release Notes: - N/A *or* Added/Fixed/Improved ...
1 parent a75d3fc commit 819d2f7

File tree

3 files changed

+97
-44
lines changed

3 files changed

+97
-44
lines changed

crates/languages/src/rust.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -568,13 +568,14 @@ impl LspAdapter for RustLspAdapter {
568568
language: &Arc<Language>,
569569
) -> Option<CodeLabel> {
570570
let (prefix, suffix) = match kind {
571-
lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => ("fn ", " () {}"),
572-
lsp::SymbolKind::STRUCT => ("struct ", " {}"),
573-
lsp::SymbolKind::ENUM => ("enum ", " {}"),
574-
lsp::SymbolKind::INTERFACE => ("trait ", " {}"),
575-
lsp::SymbolKind::CONSTANT => ("const ", ": () = ();"),
576-
lsp::SymbolKind::MODULE => ("mod ", " {}"),
577-
lsp::SymbolKind::TYPE_PARAMETER => ("type ", " {}"),
571+
lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => ("fn ", "();"),
572+
lsp::SymbolKind::STRUCT => ("struct ", ";"),
573+
lsp::SymbolKind::ENUM => ("enum ", "{}"),
574+
lsp::SymbolKind::INTERFACE => ("trait ", "{}"),
575+
lsp::SymbolKind::CONSTANT => ("const ", ":()=();"),
576+
lsp::SymbolKind::MODULE => ("mod ", ";"),
577+
lsp::SymbolKind::PACKAGE => ("extern crate ", ";"),
578+
lsp::SymbolKind::TYPE_PARAMETER => ("type ", "=();"),
578579
_ => return None,
579580
};
580581

@@ -1824,6 +1825,17 @@ mod tests {
18241825
vec![(0..4, highlight_keyword), (5..10, highlight_type)],
18251826
))
18261827
);
1828+
1829+
assert_eq!(
1830+
adapter
1831+
.label_for_symbol("zed", lsp::SymbolKind::PACKAGE, &language)
1832+
.await,
1833+
Some(CodeLabel::new(
1834+
"extern crate zed".to_string(),
1835+
13..16,
1836+
vec![(0..6, highlight_keyword), (7..12, highlight_keyword),],
1837+
))
1838+
);
18271839
}
18281840

18291841
#[gpui::test]

crates/project/src/lsp_store.rs

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7792,46 +7792,62 @@ impl LspStore {
77927792
let worktree_handle = worktree_handle.clone();
77937793
let server_id = server.server_id();
77947794
requests.push(
7795-
server
7796-
.request::<lsp::request::WorkspaceSymbolRequest>(
7797-
lsp::WorkspaceSymbolParams {
7798-
query: query.to_string(),
7799-
..Default::default()
7800-
},
7801-
)
7802-
.map(move |response| {
7803-
let lsp_symbols = response.into_response()
7804-
.context("workspace symbols request")
7805-
.log_err()
7806-
.flatten()
7807-
.map(|symbol_response| match symbol_response {
7808-
lsp::WorkspaceSymbolResponse::Flat(flat_responses) => {
7809-
flat_responses.into_iter().map(|lsp_symbol| {
7810-
(lsp_symbol.name, lsp_symbol.kind, lsp_symbol.location)
7811-
}).collect::<Vec<_>>()
7812-
}
7813-
lsp::WorkspaceSymbolResponse::Nested(nested_responses) => {
7814-
nested_responses.into_iter().filter_map(|lsp_symbol| {
7795+
server
7796+
.request::<lsp::request::WorkspaceSymbolRequest>(
7797+
lsp::WorkspaceSymbolParams {
7798+
query: query.to_string(),
7799+
..Default::default()
7800+
},
7801+
)
7802+
.map(move |response| {
7803+
let lsp_symbols = response
7804+
.into_response()
7805+
.context("workspace symbols request")
7806+
.log_err()
7807+
.flatten()
7808+
.map(|symbol_response| match symbol_response {
7809+
lsp::WorkspaceSymbolResponse::Flat(flat_responses) => {
7810+
flat_responses
7811+
.into_iter()
7812+
.map(|lsp_symbol| {
7813+
(
7814+
lsp_symbol.name,
7815+
lsp_symbol.kind,
7816+
lsp_symbol.location,
7817+
)
7818+
})
7819+
.collect::<Vec<_>>()
7820+
}
7821+
lsp::WorkspaceSymbolResponse::Nested(nested_responses) => {
7822+
nested_responses
7823+
.into_iter()
7824+
.filter_map(|lsp_symbol| {
78157825
let location = match lsp_symbol.location {
78167826
OneOf::Left(location) => location,
78177827
OneOf::Right(_) => {
7818-
log::error!("Unexpected: client capabilities forbid symbol resolutions in workspace.symbol.resolveSupport");
7819-
return None
7828+
log::error!(
7829+
"Unexpected: client capabilities \
7830+
forbid symbol resolutions in \
7831+
workspace.symbol.resolveSupport"
7832+
);
7833+
return None;
78207834
}
78217835
};
78227836
Some((lsp_symbol.name, lsp_symbol.kind, location))
7823-
}).collect::<Vec<_>>()
7824-
}
7825-
}).unwrap_or_default();
7837+
})
7838+
.collect::<Vec<_>>()
7839+
}
7840+
})
7841+
.unwrap_or_default();
78267842

7827-
WorkspaceSymbolsResult {
7828-
server_id,
7829-
lsp_adapter,
7830-
worktree: worktree_handle.downgrade(),
7831-
lsp_symbols,
7832-
}
7833-
}),
7834-
);
7843+
WorkspaceSymbolsResult {
7844+
server_id,
7845+
lsp_adapter,
7846+
worktree: worktree_handle.downgrade(),
7847+
lsp_symbols,
7848+
}
7849+
}),
7850+
);
78357851
}
78367852

78377853
cx.spawn(async move |this, cx| {

crates/project_symbols/src/project_symbols.rs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,15 @@ impl PickerDelegate for ProjectSymbolsDelegate {
178178
window: &mut Window,
179179
cx: &mut Context<Picker<Self>>,
180180
) -> Task<()> {
181-
self.filter(&query, window, cx);
181+
// Try to support rust-analyzer's path based symbols feature which
182+
// allows to search by rust path syntax, in that case we only want to
183+
// filter names by the last segment
184+
// Ideally this was a first class LSP feature (rich queries)
185+
let query_filter = query
186+
.rsplit_once("::")
187+
.map_or(&*query, |(_, suffix)| suffix)
188+
.to_owned();
189+
self.filter(&query_filter, window, cx);
182190
self.show_worktree_root_name = self.project.read(cx).visible_worktrees(cx).count() > 1;
183191
let symbols = self
184192
.project
@@ -208,7 +216,7 @@ impl PickerDelegate for ProjectSymbolsDelegate {
208216
delegate.visible_match_candidates = visible_match_candidates;
209217
delegate.external_match_candidates = external_match_candidates;
210218
delegate.symbols = symbols;
211-
delegate.filter(&query, window, cx);
219+
delegate.filter(&query_filter, window, cx);
212220
})
213221
.log_err();
214222
}
@@ -354,17 +362,24 @@ mod tests {
354362
let executor = cx.background_executor().clone();
355363
let fake_symbols = fake_symbols.clone();
356364
async move {
365+
let (query, prefixed) = match params.query.strip_prefix("dir::") {
366+
Some(query) => (query, true),
367+
None => (&*params.query, false),
368+
};
357369
let candidates = fake_symbols
358370
.iter()
359371
.enumerate()
372+
.filter(|(_, symbol)| {
373+
!prefixed || symbol.location.uri.path().contains("dir")
374+
})
360375
.map(|(id, symbol)| StringMatchCandidate::new(id, &symbol.name))
361376
.collect::<Vec<_>>();
362-
let matches = if params.query.is_empty() {
377+
let matches = if query.is_empty() {
363378
Vec::new()
364379
} else {
365380
fuzzy::match_strings(
366381
&candidates,
367-
&params.query,
382+
&query,
368383
true,
369384
true,
370385
100,
@@ -434,6 +449,16 @@ mod tests {
434449
symbols.read_with(cx, |symbols, _| {
435450
assert_eq!(symbols.delegate.matches.len(), 0);
436451
});
452+
453+
// Check that rust-analyzer path style symbols work
454+
symbols.update_in(cx, |p, window, cx| {
455+
p.update_matches("dir::to".to_string(), window, cx);
456+
});
457+
458+
cx.run_until_parked();
459+
symbols.read_with(cx, |symbols, _| {
460+
assert_eq!(symbols.delegate.matches.len(), 1);
461+
});
437462
}
438463

439464
fn init_test(cx: &mut TestAppContext) {

0 commit comments

Comments
 (0)