Skip to content

Commit

Permalink
Support returning non-hierarchical symbols
Browse files Browse the repository at this point in the history
If `hierarchicalDocumentSymbolSupport` is not true in the client capabilites
then it does not support the `DocumentSymbol[]` return type from the
`textDocument/documentSymbol` request and we must fall back to `SymbolInformation[]`.

This is one of the few requests that use the client capabilities to
differentiate between return types and could cause problems for clients.

See microsoft/language-server-protocol#538 (comment) for more context.

Found while looking at #144
  • Loading branch information
kjeremy committed Apr 23, 2020
1 parent 5eb51c1 commit 5c0cc96
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 40 deletions.
6 changes: 6 additions & 0 deletions crates/rust-analyzer/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
}
}
82 changes: 76 additions & 6 deletions crates/rust-analyzer/src/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -334,6 +335,75 @@ impl ConvWith<&FoldConvCtx<'_>> for Fold {
}
}

impl ConvWith<(&FileId, &WorldSnapshot, &LineIndex)> for Vec<StructureNode> {
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<usize>)> = 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<usize>)> = 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 {
Expand Down
39 changes: 5 additions & 34 deletions crates/rust-analyzer/src/main_loop/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<usize>)> = 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(
Expand Down

0 comments on commit 5c0cc96

Please sign in to comment.