diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 642c345740a2..0b6c1e7bd9cb 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -69,6 +69,7 @@ pub enum RustfmtConfig { pub struct ClientCapsConfig { pub location_link: bool, pub line_folding_only: bool, + pub hierarchical_symbols: bool, } impl Default for Config { @@ -214,5 +215,10 @@ impl Config { if let Some(value) = caps.folding_range.as_ref().and_then(|it| it.line_folding_only) { self.client_caps.line_folding_only = value } + if let Some(value) = + caps.document_symbol.as_ref().and_then(|it| it.hierarchical_document_symbol_support) + { + self.client_caps.hierarchical_symbols = value + } } } diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs index 7e30956cc303..441826010e3c 100644 --- a/crates/rust-analyzer/src/conv.rs +++ b/crates/rust-analyzer/src/conv.rs @@ -2,17 +2,18 @@ //! and LSP types. use lsp_types::{ - self, CreateFile, DiagnosticSeverity, DocumentChangeOperation, DocumentChanges, Documentation, - Location, LocationLink, MarkupContent, MarkupKind, ParameterInformation, ParameterLabel, - Position, Range, RenameFile, ResourceOp, SemanticTokenModifier, SemanticTokenType, - SignatureInformation, SymbolKind, TextDocumentEdit, TextDocumentIdentifier, TextDocumentItem, - TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, WorkspaceEdit, + self, CreateFile, DiagnosticSeverity, DocumentChangeOperation, DocumentChanges, DocumentSymbol, + Documentation, Location, LocationLink, MarkupContent, MarkupKind, ParameterInformation, + ParameterLabel, Position, Range, RenameFile, ResourceOp, SemanticTokenModifier, + SemanticTokenType, SignatureInformation, SymbolInformation, SymbolKind, TextDocumentEdit, + TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, Url, + VersionedTextDocumentIdentifier, WorkspaceEdit, }; use ra_ide::{ translate_offset_with_edit, CompletionItem, CompletionItemKind, CompletionScore, FileId, FilePosition, FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HighlightModifier, HighlightTag, InlayHint, InlayKind, InsertTextFormat, LineCol, LineIndex, NavigationTarget, - RangeInfo, ReferenceAccess, Severity, SourceChange, SourceFileEdit, + RangeInfo, ReferenceAccess, Severity, SourceChange, SourceFileEdit, StructureNode, }; use ra_syntax::{SyntaxKind, TextRange, TextUnit}; use ra_text_edit::{AtomTextEdit, TextEdit}; @@ -334,6 +335,75 @@ impl ConvWith<&FoldConvCtx<'_>> for Fold { } } +impl ConvWith<(&FileId, &WorldSnapshot, &LineIndex)> for Vec { + type Output = lsp_types::DocumentSymbolResponse; + + fn conv_with(self, ctx: (&FileId, &WorldSnapshot, &LineIndex)) -> Self::Output { + let file_id = ctx.0; + let world = ctx.1; + let line_index = ctx.2; + let supports_hierarchy = world.config.client_caps.hierarchical_symbols; + + if supports_hierarchy { + let mut parents: Vec<(DocumentSymbol, Option)> = Vec::new(); + + for symbol in self.into_iter() { + let doc_symbol = DocumentSymbol { + name: symbol.label, + detail: symbol.detail, + kind: symbol.kind.conv(), + deprecated: Some(symbol.deprecated), + range: symbol.node_range.conv_with(line_index), + selection_range: symbol.navigation_range.conv_with(line_index), + children: None, + }; + parents.push((doc_symbol, symbol.parent)); + } + let mut res = Vec::new(); + while let Some((node, parent)) = parents.pop() { + match parent { + None => res.push(node), + Some(i) => { + let children = &mut parents[i].0.children; + if children.is_none() { + *children = Some(Vec::new()); + } + children.as_mut().unwrap().push(node); + } + } + } + res.into() + } else { + let mut parents: Vec<(SymbolInformation, Option)> = Vec::new(); + for symbol in self.into_iter() { + parents.push(( + SymbolInformation { + name: symbol.label, + kind: symbol.kind.conv(), + deprecated: Some(symbol.deprecated), + location: to_location(*file_id, symbol.navigation_range, world, line_index) + .unwrap(), + container_name: None, + }, + symbol.parent, + )) + } + let mut res = Vec::new(); + while let Some((mut node, parent)) = parents.pop() { + match parent { + None => res.push(node), + Some(i) => { + node.container_name = Some(parents[i].0.name.clone()); + res.push(node) + } + } + } + + res.into() + } + } +} + impl ConvWith<&LineIndex> for InlayHint { type Output = req::InlayHint; fn conv_with(self, line_index: &LineIndex) -> Self::Output { diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index ee669f383216..52242f6e4e86 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -12,11 +12,10 @@ use lsp_types::{ CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic, - DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, - Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse, - Range, RenameParams, SemanticTokensParams, SemanticTokensRangeParams, - SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, - TextEdit, WorkspaceEdit, + DocumentFormattingParams, DocumentHighlight, FoldingRange, FoldingRangeParams, Hover, + HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, + RenameParams, SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, + SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, TextEdit, WorkspaceEdit, }; use ra_ide::{ Assist, AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, @@ -220,35 +219,7 @@ pub fn handle_document_symbol( let file_id = params.text_document.try_conv_with(&world)?; let line_index = world.analysis().file_line_index(file_id)?; - let mut parents: Vec<(DocumentSymbol, Option)> = Vec::new(); - - for symbol in world.analysis().file_structure(file_id)? { - let doc_symbol = DocumentSymbol { - name: symbol.label, - detail: symbol.detail, - kind: symbol.kind.conv(), - deprecated: Some(symbol.deprecated), - range: symbol.node_range.conv_with(&line_index), - selection_range: symbol.navigation_range.conv_with(&line_index), - children: None, - }; - parents.push((doc_symbol, symbol.parent)); - } - let mut res = Vec::new(); - while let Some((node, parent)) = parents.pop() { - match parent { - None => res.push(node), - Some(i) => { - let children = &mut parents[i].0.children; - if children.is_none() { - *children = Some(Vec::new()); - } - children.as_mut().unwrap().push(node); - } - } - } - - Ok(Some(res.into())) + Ok(Some(world.analysis().file_structure(file_id)?.conv_with((&file_id, &world, &line_index)))) } pub fn handle_workspace_symbol(