diff --git a/Cargo.lock b/Cargo.lock index 57df633a4d..46071311fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4241,11 +4241,13 @@ name = "ty_ide" version = "0.0.0" dependencies = [ "bitflags 2.9.2", + "get-size2", "insta", "itertools 0.14.0", "rayon", "regex", "ruff_db", + "ruff_index", "ruff_memory_usage", "ruff_python_ast", "ruff_python_parser", diff --git a/crates/ty_ide/Cargo.toml b/crates/ty_ide/Cargo.toml index bf6d0db791..e00b8db200 100644 --- a/crates/ty_ide/Cargo.toml +++ b/crates/ty_ide/Cargo.toml @@ -13,6 +13,7 @@ license = { workspace = true } [dependencies] bitflags = { workspace = true } ruff_db = { workspace = true } +ruff_index = { workspace = true } ruff_memory_usage = { workspace = true } ruff_python_ast = { workspace = true } ruff_python_parser = { workspace = true } @@ -22,6 +23,7 @@ ruff_text_size = { workspace = true } ty_python_semantic = { workspace = true } ty_project = { workspace = true, features = ["testing"] } +get-size2 = { workspace = true } itertools = { workspace = true } rayon = { workspace = true } regex = { workspace = true } diff --git a/crates/ty_ide/src/document_symbols.rs b/crates/ty_ide/src/document_symbols.rs index 2edb70241b..8e426168dc 100644 --- a/crates/ty_ide/src/document_symbols.rs +++ b/crates/ty_ide/src/document_symbols.rs @@ -1,29 +1,16 @@ -use crate::symbols::{SymbolInfo, SymbolsOptions, symbols_for_file}; +use crate::symbols::{FlatSymbols, symbols_for_file}; use ruff_db::files::File; use ty_project::Db; /// Get all document symbols for a file with the given options. -pub fn document_symbols_with_options( - db: &dyn Db, - file: File, - options: &SymbolsOptions, -) -> Vec { - symbols_for_file(db, file, options).cloned().collect() -} - -/// Get all document symbols for a file (hierarchical by default). -pub fn document_symbols(db: &dyn Db, file: File) -> Vec { - let options = SymbolsOptions { - hierarchical: true, - global_only: false, - query_string: None, - }; - document_symbols_with_options(db, file, &options) +pub fn document_symbols(db: &dyn Db, file: File) -> &FlatSymbols { + symbols_for_file(db, file) } #[cfg(test)] mod tests { use super::*; + use crate::symbols::{HierarchicalSymbols, SymbolId, SymbolInfo}; use crate::tests::{CursorTest, IntoDiagnostic, cursor_test}; use insta::assert_snapshot; use ruff_db::diagnostic::{ @@ -324,42 +311,45 @@ class OuterClass: impl CursorTest { fn document_symbols(&self) -> String { - let symbols = document_symbols(&self.db, self.cursor.file); + let symbols = document_symbols(&self.db, self.cursor.file).to_hierarchical(); if symbols.is_empty() { return "No symbols found".to_string(); } - self.render_diagnostics( - symbols - .into_iter() - .flat_map(|symbol| symbol_to_diagnostics(symbol, self.cursor.file)), - ) + self.render_diagnostics(symbols.iter().flat_map(|(id, symbol)| { + symbol_to_diagnostics(&symbols, id, symbol, self.cursor.file) + })) } } - fn symbol_to_diagnostics(symbol: SymbolInfo, file: File) -> Vec { + fn symbol_to_diagnostics<'db>( + symbols: &'db HierarchicalSymbols, + id: SymbolId, + symbol: SymbolInfo<'db>, + file: File, + ) -> Vec> { // Output the symbol and recursively output all child symbols - let mut diagnostics = vec![DocumentSymbolDiagnostic::new(symbol.clone(), file)]; + let mut diagnostics = vec![DocumentSymbolDiagnostic::new(symbol, file)]; - for child in symbol.children { - diagnostics.extend(symbol_to_diagnostics(child, file)); + for (child_id, child) in symbols.children(id) { + diagnostics.extend(symbol_to_diagnostics(symbols, child_id, child, file)); } diagnostics } - struct DocumentSymbolDiagnostic { - symbol: SymbolInfo, + struct DocumentSymbolDiagnostic<'db> { + symbol: SymbolInfo<'db>, file: File, } - impl DocumentSymbolDiagnostic { - fn new(symbol: SymbolInfo, file: File) -> Self { + impl<'db> DocumentSymbolDiagnostic<'db> { + fn new(symbol: SymbolInfo<'db>, file: File) -> Self { Self { symbol, file } } } - impl IntoDiagnostic for DocumentSymbolDiagnostic { + impl IntoDiagnostic for DocumentSymbolDiagnostic<'_> { fn into_diagnostic(self) -> Diagnostic { let symbol_kind_str = self.symbol.kind.to_string(); diff --git a/crates/ty_ide/src/lib.rs b/crates/ty_ide/src/lib.rs index 922c551ebd..c2567ece0d 100644 --- a/crates/ty_ide/src/lib.rs +++ b/crates/ty_ide/src/lib.rs @@ -26,7 +26,7 @@ mod workspace_symbols; pub use completion::completion; pub use doc_highlights::document_highlights; -pub use document_symbols::{document_symbols, document_symbols_with_options}; +pub use document_symbols::document_symbols; pub use goto::{goto_declaration, goto_definition, goto_type_definition}; pub use goto_references::goto_references; pub use hover::hover; @@ -39,7 +39,7 @@ pub use semantic_tokens::{ SemanticToken, SemanticTokenModifier, SemanticTokenType, SemanticTokens, semantic_tokens, }; pub use signature_help::{ParameterDetails, SignatureDetails, SignatureHelpInfo, signature_help}; -pub use symbols::{SymbolInfo, SymbolKind, SymbolsOptions}; +pub use symbols::{FlatSymbols, HierarchicalSymbols, SymbolId, SymbolInfo, SymbolKind}; pub use workspace_symbols::{WorkspaceSymbolInfo, workspace_symbols}; use ruff_db::files::{File, FileRange}; diff --git a/crates/ty_ide/src/symbols.rs b/crates/ty_ide/src/symbols.rs index 4d4e72ea1f..e787a0c0c0 100644 --- a/crates/ty_ide/src/symbols.rs +++ b/crates/ty_ide/src/symbols.rs @@ -1,26 +1,22 @@ //! Implements logic used by the document symbol provider, workspace symbol //! provider, and auto-import feature of the completion provider. +use std::borrow::Cow; +use std::ops::Range; + use regex::Regex; use ruff_db::files::File; use ruff_db::parsed::parsed_module; +use ruff_index::{IndexVec, newtype_index}; use ruff_python_ast::visitor::source_order::{self, SourceOrderVisitor}; use ruff_python_ast::{Expr, Stmt}; use ruff_text_size::{Ranged, TextRange}; use ty_project::Db; -/// Options that control which symbols are returned -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct SymbolsOptions { - /// Return a hierarchy of symbols or a flattened list? - pub hierarchical: bool, - /// Include only symbols in the global scope - pub global_only: bool, - /// Query string for filtering symbol names - pub query_string: Option, -} - +/// A compiled query pattern used for searching symbols. +/// +/// This can be used with the `FlatSymbols::search` API. #[derive(Clone, Debug)] pub struct QueryPattern { re: Option, @@ -28,6 +24,7 @@ pub struct QueryPattern { } impl QueryPattern { + /// Create a new query pattern from a literal search string given. pub fn new(literal_query_string: &str) -> QueryPattern { let mut pattern = "(?i)".to_string(); for ch in literal_query_string.chars() { @@ -36,14 +33,16 @@ impl QueryPattern { } // In theory regex compilation could fail if the pattern string // was long enough to exceed the default regex compilation size - // limit. But this length would be approaching ~10MB or so. + // limit. But this length would be approaching ~10MB or so. If + // is does somehow fail, we'll just fall back to simple substring + // search using `original`. QueryPattern { re: Regex::new(&pattern).ok(), original: literal_query_string.to_string(), } } - fn is_match(&self, symbol: &SymbolInfo) -> bool { + fn is_match(&self, symbol: &SymbolInfo<'_>) -> bool { self.is_match_symbol_name(&symbol.name) } @@ -73,23 +72,183 @@ impl PartialEq for QueryPattern { } } +/// A flat list of indexed symbols for a single file. +#[derive(Clone, Debug, Default, PartialEq, Eq, get_size2::GetSize)] +pub struct FlatSymbols { + symbols: IndexVec, +} + +impl FlatSymbols { + /// Get the symbol info for the symbol identified by the given ID. + /// + /// Returns `None` when the given ID does not reference a symbol in this + /// collection. + pub fn get(&self, id: SymbolId) -> Option> { + self.symbols.get(id).map(Into::into) + } + + /// Returns true if and only if this collection is empty. + pub fn is_empty(&self) -> bool { + self.symbols.is_empty() + } + + /// Returns the total number of symbols in this collection. + pub fn len(&self) -> usize { + self.symbols.len() + } + + /// Returns an iterator over every symbol along with its ID. + pub fn iter(&self) -> impl Iterator)> { + self.symbols + .iter_enumerated() + .map(|(id, symbol)| (id, symbol.into())) + } + + /// Returns a sequence of symbols that matches the given query. + pub fn search(&self, query: &QueryPattern) -> impl Iterator)> { + self.iter().filter(|(_, symbol)| query.is_match(symbol)) + } + + /// Turns this flat sequence of symbols into a hierarchy of symbols. + pub fn to_hierarchical(&self) -> HierarchicalSymbols { + let mut children_ids: IndexVec> = IndexVec::new(); + for (id, symbol) in self.symbols.iter_enumerated() { + children_ids.push(vec![]); + let Some(parent_id) = symbol.parent else { + continue; + }; + // OK because the symbol visitor guarantees that + // all parents are ordered before their children. + assert!(parent_id.index() < id.index()); + children_ids[parent_id].push(id); + } + + // Now flatten our map of symbol ID to its children + // IDs into a single vec that doesn't nest allocations. + let mut symbols = IndexVec::new(); + let mut children: Vec = vec![]; + let mut last_end: usize = 0; + for (tree, child_symbol_ids) in self.symbols.iter().zip(children_ids) { + let start = last_end; + let end = start + child_symbol_ids.len(); + symbols.push(SymbolTreeWithChildren { + tree: tree.clone(), + children: start..end, + }); + children.extend_from_slice(&child_symbol_ids); + last_end = end; + } + + HierarchicalSymbols { symbols, children } + } +} + +/// A collection of hierarchical indexed symbols for a single file. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct HierarchicalSymbols { + symbols: IndexVec, + children: Vec, +} + +impl HierarchicalSymbols { + /// Get the symbol info for the symbol identified by the given ID. + /// + /// Returns `None` when the given ID does not reference a symbol in this + /// collection. + pub fn get(&self, id: SymbolId) -> Option> { + self.symbols.get(id).map(Into::into) + } + + /// Returns true if and only if this collection is empty. + pub fn is_empty(&self) -> bool { + self.symbols.is_empty() + } + + /// Returns the total number of symbols in this collection. + pub fn len(&self) -> usize { + self.symbols.len() + } + + /// Returns an iterator over every top-level symbol along with its ID. + pub fn iter(&self) -> impl Iterator)> { + self.symbols + .iter_enumerated() + .filter(|(_, symbol)| symbol.tree.parent.is_none()) + .map(|(id, symbol)| (id, symbol.into())) + } + + /// Returns an iterator over the child symbols for the symbol + /// identified by the given ID. + /// + /// Returns `None` when there aren't any children or when the given + /// ID does not reference a symbol in this collection. + pub fn children(&self, id: SymbolId) -> impl Iterator)> { + self.symbols + .get(id) + .into_iter() + .flat_map(|symbol| self.children[symbol.children.clone()].iter()) + .copied() + .map(|id| (id, SymbolInfo::from(&self.symbols[id]))) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +struct SymbolTreeWithChildren { + tree: SymbolTree, + /// The index range into `HierarchicalSymbols::children` + /// corresponding to the children symbol IDs for this + /// symbol. + children: Range, +} + +/// Uniquely identifies a symbol. +#[newtype_index] +#[derive(get_size2::GetSize)] +pub struct SymbolId; + /// Symbol information for IDE features like document outline. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct SymbolInfo { +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SymbolInfo<'a> { /// The name of the symbol - pub name: String, + pub name: Cow<'a, str>, /// The kind of symbol (function, class, variable, etc.) pub kind: SymbolKind, /// The range of the symbol name pub name_range: TextRange, /// The full range of the symbol (including body) pub full_range: TextRange, - /// Child symbols (e.g., methods in a class) - pub children: Vec, +} + +impl SymbolInfo<'_> { + pub fn to_owned(&self) -> SymbolInfo<'static> { + SymbolInfo { + name: Cow::Owned(self.name.to_string()), + kind: self.kind, + name_range: self.name_range, + full_range: self.full_range, + } + } +} + +impl<'a> From<&'a SymbolTree> for SymbolInfo<'a> { + fn from(symbol: &'a SymbolTree) -> SymbolInfo<'a> { + SymbolInfo { + name: Cow::Borrowed(&symbol.name), + kind: symbol.kind, + name_range: symbol.name_range, + full_range: symbol.full_range, + } + } +} + +impl<'a> From<&'a SymbolTreeWithChildren> for SymbolInfo<'a> { + fn from(symbol: &'a SymbolTreeWithChildren) -> SymbolInfo<'a> { + SymbolInfo::from(&symbol.tree) + } } /// The kind of symbol -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, get_size2::GetSize)] pub enum SymbolKind { Module, Class, @@ -125,61 +284,68 @@ impl SymbolKind { } } -pub(crate) fn symbols_for_file<'db>( - db: &'db dyn Db, - file: File, - options: &SymbolsOptions, -) -> impl Iterator { - assert!( - !options.hierarchical || options.query_string.is_none(), - "Cannot use hierarchical mode with a query string" - ); - - let ingredient = SymbolsOptionsWithoutQuery { - hierarchical: options.hierarchical, - global_only: options.global_only, - }; - symbols_for_file_inner(db, file, ingredient) - .iter() - .filter(|symbol| { - let Some(ref query) = options.query_string else { - return true; - }; - query.is_match(symbol) - }) -} - -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -struct SymbolsOptionsWithoutQuery { - hierarchical: bool, - global_only: bool, -} - -#[salsa::tracked(returns(deref))] -fn symbols_for_file_inner<'db>( - db: &'db dyn Db, - file: File, - options: SymbolsOptionsWithoutQuery, -) -> Vec { +/// Returns a flat list of symbols in the file given. +/// +/// The flattened list includes parent/child information and can be +/// converted into a hierarchical collection of symbols. +#[salsa::tracked(returns(ref), heap_size=ruff_memory_usage::heap_size)] +pub(crate) fn symbols_for_file(db: &dyn Db, file: File) -> FlatSymbols { let parsed = parsed_module(db, file); let module = parsed.load(db); let mut visitor = SymbolVisitor { - symbols: vec![], + symbols: IndexVec::new(), symbol_stack: vec![], in_function: false, - options, + global_only: false, }; visitor.visit_body(&module.syntax().body); - visitor.symbols + FlatSymbols { + symbols: visitor.symbols, + } } +/// Returns a flat list of *only global* symbols in the file given. +/// +/// While callers can convert this into a hierarchical collection of +/// symbols, it won't result in anything meaningful since the flat list +/// returned doesn't include children. +#[salsa::tracked(returns(ref), heap_size=ruff_memory_usage::heap_size)] +pub(crate) fn symbols_for_file_global_only(db: &dyn Db, file: File) -> FlatSymbols { + let parsed = parsed_module(db, file); + let module = parsed.load(db); + + let mut visitor = SymbolVisitor { + symbols: IndexVec::new(), + symbol_stack: vec![], + in_function: false, + global_only: true, + }; + visitor.visit_body(&module.syntax().body); + FlatSymbols { + symbols: visitor.symbols, + } +} + +#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)] +struct SymbolTree { + parent: Option, + name: String, + kind: SymbolKind, + name_range: TextRange, + full_range: TextRange, +} + +/// A visitor over all symbols in a single file. +/// +/// This guarantees that child symbols have a symbol ID greater +/// than all of its parents. struct SymbolVisitor { - symbols: Vec, - symbol_stack: Vec, + symbols: IndexVec, + symbol_stack: Vec, /// Track if we're currently inside a function (to exclude local variables) in_function: bool, - options: SymbolsOptionsWithoutQuery, + global_only: bool, } impl SymbolVisitor { @@ -189,32 +355,33 @@ impl SymbolVisitor { } } - fn add_symbol(&mut self, symbol: SymbolInfo) { - if self.options.hierarchical { - if let Some(parent) = self.symbol_stack.last_mut() { - parent.children.push(symbol); - } else { - self.symbols.push(symbol); - } - } else { - self.symbols.push(symbol); + fn add_symbol(&mut self, mut symbol: SymbolTree) -> SymbolId { + if let Some(&parent_id) = self.symbol_stack.last() { + symbol.parent = Some(parent_id); } + // It's important that we push the symbol and allocate + // an ID before visiting its child. This preserves the + // guarantee that parent IDs are always less than their + // children IDs. + let symbol_id = self.symbols.next_index(); + self.symbols.push(symbol); + symbol_id } - fn push_symbol(&mut self, symbol: SymbolInfo) { - if self.options.hierarchical { - self.symbol_stack.push(symbol); - } else { - self.add_symbol(symbol); - } + fn push_symbol(&mut self, symbol: SymbolTree) { + let symbol_id = self.add_symbol(symbol); + self.symbol_stack.push(symbol_id); } fn pop_symbol(&mut self) { - if self.options.hierarchical { - if let Some(symbol) = self.symbol_stack.pop() { - self.add_symbol(symbol); - } - } + self.symbol_stack.pop().unwrap(); + } + + fn iter_symbol_stack(&self) -> impl Iterator { + self.symbol_stack + .iter() + .copied() + .map(|id| &self.symbols[id]) } fn is_constant_name(name: &str) -> bool { @@ -227,8 +394,7 @@ impl SourceOrderVisitor<'_> for SymbolVisitor { match stmt { Stmt::FunctionDef(func_def) => { let kind = if self - .symbol_stack - .iter() + .iter_symbol_stack() .any(|s| s.kind == SymbolKind::Class) { if func_def.name.as_str() == "__init__" { @@ -240,15 +406,15 @@ impl SourceOrderVisitor<'_> for SymbolVisitor { SymbolKind::Function }; - let symbol = SymbolInfo { + let symbol = SymbolTree { + parent: None, name: func_def.name.to_string(), kind, name_range: func_def.name.range(), full_range: stmt.range(), - children: Vec::new(), }; - if self.options.global_only { + if self.global_only { self.add_symbol(symbol); // If global_only, don't walk function bodies return; @@ -269,15 +435,15 @@ impl SourceOrderVisitor<'_> for SymbolVisitor { } Stmt::ClassDef(class_def) => { - let symbol = SymbolInfo { + let symbol = SymbolTree { + parent: None, name: class_def.name.to_string(), kind: SymbolKind::Class, name_range: class_def.name.range(), full_range: stmt.range(), - children: Vec::new(), }; - if self.options.global_only { + if self.global_only { self.add_symbol(symbol); // If global_only, don't walk class bodies return; @@ -296,8 +462,7 @@ impl SourceOrderVisitor<'_> for SymbolVisitor { let kind = if Self::is_constant_name(name.id.as_str()) { SymbolKind::Constant } else if self - .symbol_stack - .iter() + .iter_symbol_stack() .any(|s| s.kind == SymbolKind::Class) { SymbolKind::Field @@ -305,12 +470,12 @@ impl SourceOrderVisitor<'_> for SymbolVisitor { SymbolKind::Variable }; - let symbol = SymbolInfo { + let symbol = SymbolTree { + parent: None, name: name.id.to_string(), kind, name_range: name.range(), full_range: stmt.range(), - children: Vec::new(), }; self.add_symbol(symbol); @@ -326,8 +491,7 @@ impl SourceOrderVisitor<'_> for SymbolVisitor { let kind = if Self::is_constant_name(name.id.as_str()) { SymbolKind::Constant } else if self - .symbol_stack - .iter() + .iter_symbol_stack() .any(|s| s.kind == SymbolKind::Class) { SymbolKind::Field @@ -335,12 +499,12 @@ impl SourceOrderVisitor<'_> for SymbolVisitor { SymbolKind::Variable }; - let symbol = SymbolInfo { + let symbol = SymbolTree { + parent: None, name: name.id.to_string(), kind, name_range: name.range(), full_range: stmt.range(), - children: Vec::new(), }; self.add_symbol(symbol); diff --git a/crates/ty_ide/src/workspace_symbols.rs b/crates/ty_ide/src/workspace_symbols.rs index 738c208bcb..9d490bd700 100644 --- a/crates/ty_ide/src/workspace_symbols.rs +++ b/crates/ty_ide/src/workspace_symbols.rs @@ -1,4 +1,4 @@ -use crate::symbols::{SymbolInfo, SymbolsOptions, symbols_for_file}; +use crate::symbols::{QueryPattern, SymbolInfo, symbols_for_file_global_only}; use ruff_db::files::File; use ty_project::Db; @@ -12,31 +12,26 @@ pub fn workspace_symbols(db: &dyn Db, query: &str) -> Vec { let project = db.project(); - let options = SymbolsOptions { - hierarchical: false, // Workspace symbols are always flat - global_only: false, - query_string: Some(query.into()), - }; - + let query = QueryPattern::new(query); let files = project.files(db); let results = std::sync::Mutex::new(Vec::new()); { let db = db.dyn_clone(); let files = &files; - let options = &options; let results = &results; + let query = &query; rayon::scope(move |s| { // For each file, extract symbols and add them to results for file in files.iter() { let db = db.dyn_clone(); s.spawn(move |_| { - for symbol in symbols_for_file(&*db, *file, options) { + for (_, symbol) in symbols_for_file_global_only(&*db, *file).search(query) { // It seems like we could do better here than // locking `results` for every single symbol, // but this works pretty well as it is. results.lock().unwrap().push(WorkspaceSymbolInfo { - symbol: symbol.clone(), + symbol: symbol.to_owned(), file: *file, }); } @@ -52,7 +47,7 @@ pub fn workspace_symbols(db: &dyn Db, query: &str) -> Vec { #[derive(Debug, Clone, PartialEq, Eq)] pub struct WorkspaceSymbolInfo { /// The symbol information - pub symbol: SymbolInfo, + pub symbol: SymbolInfo<'static>, /// The file containing the symbol pub file: File, } diff --git a/crates/ty_server/src/server/api/requests/document_symbols.rs b/crates/ty_server/src/server/api/requests/document_symbols.rs index eb19c5826c..46c4c3eb2e 100644 --- a/crates/ty_server/src/server/api/requests/document_symbols.rs +++ b/crates/ty_server/src/server/api/requests/document_symbols.rs @@ -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 = 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 = symbols - .into_iter() - .map(|symbol| { + .iter() + .map(|(_, symbol)| { convert_to_lsp_symbol_information( symbol, ¶ms.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(), ), } diff --git a/crates/ty_server/src/server/api/symbols.rs b/crates/ty_server/src/server/api/symbols.rs index 39a701e2fa..396f236e8d 100644 --- a/crates/ty_server/src/server/api/symbols.rs +++ b/crates/ty_server/src/server/api/symbols.rs @@ -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)]