[ty] Rejigger workspace symbols for more efficient caching

In effect, we make the Salsa query aspect keyed only on whether we want
global symbols. We move everything else (hierarchical and querying) to
an aggregate step *after* the query.

This was a somewhat involved change since we want to return a flattened
list from visiting the source while also preserving enough information
to reform the symbols into a hierarchical structure that the LSP
expects. But I think overall the API has gotten simpler and we encode
more invariants into the type system. (For example, previously you got a
runtime assertion if you tried to provide a query string while enabling
hierarchical mode. But now that's prevented by construction.)
This commit is contained in:
Andrew Gallant 2025-08-23 08:11:39 -04:00 committed by Andrew Gallant
parent f407f12f4c
commit 205eae14d2
8 changed files with 317 additions and 164 deletions

View file

@ -4,7 +4,7 @@ use lsp_types::request::DocumentSymbolRequest;
use lsp_types::{DocumentSymbol, DocumentSymbolParams, SymbolInformation, Url};
use ruff_db::source::{line_index, source_text};
use ruff_source_file::LineIndex;
use ty_ide::{SymbolInfo, SymbolsOptions, document_symbols_with_options};
use ty_ide::{HierarchicalSymbols, SymbolId, SymbolInfo, document_symbols};
use ty_project::ProjectDatabase;
use crate::document::{PositionEncoding, ToRangeExt};
@ -51,24 +51,19 @@ impl BackgroundDocumentRequestHandler for DocumentSymbolRequestHandler {
.resolved_client_capabilities()
.supports_hierarchical_document_symbols();
let options = SymbolsOptions {
hierarchical: supports_hierarchical,
global_only: false,
query_string: None,
};
let symbols = document_symbols_with_options(db, file, &options);
let symbols = document_symbols(db, file);
if symbols.is_empty() {
return Ok(None);
}
if supports_hierarchical {
// Return hierarchical symbols
let symbols = symbols.to_hierarchical();
let lsp_symbols: Vec<DocumentSymbol> = symbols
.into_iter()
.map(|symbol| {
.iter()
.map(|(id, symbol)| {
convert_to_lsp_document_symbol(
&symbols,
id,
symbol,
&source,
&line_index,
@ -81,8 +76,8 @@ impl BackgroundDocumentRequestHandler for DocumentSymbolRequestHandler {
} else {
// Return flattened symbols as SymbolInformation
let lsp_symbols: Vec<SymbolInformation> = symbols
.into_iter()
.map(|symbol| {
.iter()
.map(|(_, symbol)| {
convert_to_lsp_symbol_information(
symbol,
&params.text_document.uri,
@ -101,7 +96,9 @@ impl BackgroundDocumentRequestHandler for DocumentSymbolRequestHandler {
impl RetriableRequestHandler for DocumentSymbolRequestHandler {}
fn convert_to_lsp_document_symbol(
symbol: SymbolInfo,
symbols: &HierarchicalSymbols,
id: SymbolId,
symbol: SymbolInfo<'_>,
source: &str,
line_index: &LineIndex,
encoding: PositionEncoding,
@ -109,7 +106,7 @@ fn convert_to_lsp_document_symbol(
let symbol_kind = convert_symbol_kind(symbol.kind);
DocumentSymbol {
name: symbol.name,
name: symbol.name.into_owned(),
detail: None,
kind: symbol_kind,
tags: None,
@ -118,10 +115,13 @@ fn convert_to_lsp_document_symbol(
range: symbol.full_range.to_lsp_range(source, line_index, encoding),
selection_range: symbol.name_range.to_lsp_range(source, line_index, encoding),
children: Some(
symbol
.children
.into_iter()
.map(|child| convert_to_lsp_document_symbol(child, source, line_index, encoding))
symbols
.children(id)
.map(|(child_id, child)| {
convert_to_lsp_document_symbol(
symbols, child_id, child, source, line_index, encoding,
)
})
.collect(),
),
}

View file

@ -27,7 +27,7 @@ pub(crate) fn convert_symbol_kind(kind: ty_ide::SymbolKind) -> SymbolKind {
/// Convert a `ty_ide` `SymbolInfo` to LSP `SymbolInformation`
pub(crate) fn convert_to_lsp_symbol_information(
symbol: SymbolInfo,
symbol: SymbolInfo<'_>,
uri: &Url,
source: &str,
line_index: &LineIndex,
@ -36,7 +36,7 @@ pub(crate) fn convert_to_lsp_symbol_information(
let symbol_kind = convert_symbol_kind(symbol.kind);
SymbolInformation {
name: symbol.name,
name: symbol.name.into_owned(),
kind: symbol_kind,
tags: None,
#[allow(deprecated)]