Skip to content

Commit 7021352

Browse files
bors[bot]kjeremybnjjjVeetaha
authored
4113: Support returning non-hierarchical symbols r=matklad a=kjeremy 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 4136: add support for cfg feature attributes on expression #4063 r=matklad a=bnjjj close issue #4063 4141: Fix typo r=matklad a=Veetaha 4142: Remove unnecessary async from vscode language client creation r=matklad a=Veetaha Co-authored-by: kjeremy <[email protected]> Co-authored-by: Benjamin Coenen <[email protected]> Co-authored-by: veetaha <[email protected]>
5 parents 5671bac + 0619c67 + b87b335 + f52e2f6 + 5f88df8 commit 7021352

File tree

7 files changed

+78
-7
lines changed

7 files changed

+78
-7
lines changed

crates/ra_hir_def/src/body.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use crate::{
2727
AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId,
2828
};
2929

30-
/// A subser of Exander that only deals with cfg attributes. We only need it to
30+
/// A subset of Exander that only deals with cfg attributes. We only need it to
3131
/// avoid cyclic queries in crate def map during enum processing.
3232
pub(crate) struct CfgExpander {
3333
cfg_options: CfgOptions,

crates/ra_hir_def/src/body/lower.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ impl ExprCollector<'_> {
141141

142142
fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
143143
let syntax_ptr = AstPtr::new(&expr);
144+
let attrs = self.expander.parse_attrs(&expr);
145+
if !self.expander.is_cfg_enabled(&attrs) {
146+
return self.missing_expr();
147+
}
144148
match expr {
145149
ast::Expr::IfExpr(e) => {
146150
let then_branch = self.collect_block_opt(e.then_branch());

crates/ra_hir_ty/src/tests.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,38 @@ fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() {
390390
assert_snapshot!(diagnostics, @r###""###);
391391
}
392392

393+
#[test]
394+
fn no_such_field_with_feature_flag_diagnostics_on_block_expr() {
395+
let diagnostics = TestDB::with_files(
396+
r#"
397+
//- /lib.rs crate:foo cfg:feature=foo
398+
struct S {
399+
#[cfg(feature = "foo")]
400+
foo: u32,
401+
#[cfg(not(feature = "foo"))]
402+
bar: u32,
403+
}
404+
405+
impl S {
406+
fn new(bar: u32) -> Self {
407+
#[cfg(feature = "foo")]
408+
{
409+
Self { foo: bar }
410+
}
411+
#[cfg(not(feature = "foo"))]
412+
{
413+
Self { bar }
414+
}
415+
}
416+
}
417+
"#,
418+
)
419+
.diagnostics()
420+
.0;
421+
422+
assert_snapshot!(diagnostics, @r###""###);
423+
}
424+
393425
#[test]
394426
fn no_such_field_with_feature_flag_diagnostics_on_struct_fields() {
395427
let diagnostics = TestDB::with_files(

crates/rust-analyzer/src/config.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ pub enum RustfmtConfig {
6969
pub struct ClientCapsConfig {
7070
pub location_link: bool,
7171
pub line_folding_only: bool,
72+
pub hierarchical_symbols: bool,
7273
}
7374

7475
impl Default for Config {
@@ -215,6 +216,11 @@ impl Config {
215216
if let Some(value) = caps.folding_range.as_ref().and_then(|it| it.line_folding_only) {
216217
self.client_caps.line_folding_only = value
217218
}
219+
if let Some(value) =
220+
caps.document_symbol.as_ref().and_then(|it| it.hierarchical_document_symbol_support)
221+
{
222+
self.client_caps.hierarchical_symbols = value
223+
}
218224
self.completion.allow_snippets(false);
219225
if let Some(completion) = &caps.completion {
220226
if let Some(completion_item) = &completion.completion_item {

crates/rust-analyzer/src/main_loop/handlers.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use lsp_types::{
1616
Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse,
1717
Range, RenameParams, SemanticTokensParams, SemanticTokensRangeParams,
1818
SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, TextDocumentIdentifier,
19-
TextEdit, WorkspaceEdit,
19+
TextEdit, Url, WorkspaceEdit,
2020
};
2121
use ra_ide::{
2222
Assist, AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind,
@@ -219,6 +219,7 @@ pub fn handle_document_symbol(
219219
let _p = profile("handle_document_symbol");
220220
let file_id = params.text_document.try_conv_with(&world)?;
221221
let line_index = world.analysis().file_line_index(file_id)?;
222+
let url = file_id.try_conv_with(&world)?;
222223

223224
let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new();
224225

@@ -234,10 +235,10 @@ pub fn handle_document_symbol(
234235
};
235236
parents.push((doc_symbol, symbol.parent));
236237
}
237-
let mut res = Vec::new();
238+
let mut document_symbols = Vec::new();
238239
while let Some((node, parent)) = parents.pop() {
239240
match parent {
240-
None => res.push(node),
241+
None => document_symbols.push(node),
241242
Some(i) => {
242243
let children = &mut parents[i].0.children;
243244
if children.is_none() {
@@ -248,7 +249,35 @@ pub fn handle_document_symbol(
248249
}
249250
}
250251

251-
Ok(Some(res.into()))
252+
if world.config.client_caps.hierarchical_symbols {
253+
Ok(Some(document_symbols.into()))
254+
} else {
255+
let mut symbol_information = Vec::<SymbolInformation>::new();
256+
for symbol in document_symbols {
257+
flatten_document_symbol(&symbol, None, &url, &mut symbol_information);
258+
}
259+
260+
Ok(Some(symbol_information.into()))
261+
}
262+
}
263+
264+
fn flatten_document_symbol(
265+
symbol: &DocumentSymbol,
266+
container_name: Option<String>,
267+
url: &Url,
268+
res: &mut Vec<SymbolInformation>,
269+
) {
270+
res.push(SymbolInformation {
271+
name: symbol.name.clone(),
272+
kind: symbol.kind,
273+
deprecated: symbol.deprecated,
274+
location: Location::new(url.clone(), symbol.range),
275+
container_name: container_name,
276+
});
277+
278+
for child in symbol.children.iter().flatten() {
279+
flatten_document_symbol(child, Some(symbol.name.clone()), url, res);
280+
}
252281
}
253282

254283
pub fn handle_workspace_symbol(

editors/code/src/client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as vscode from 'vscode';
44
import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed';
55
import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed';
66

7-
export async function createClient(serverPath: string, cwd: string): Promise<lc.LanguageClient> {
7+
export function createClient(serverPath: string, cwd: string): lc.LanguageClient {
88
// '.' Is the fallback if no folder is open
99
// TODO?: Workspace folders support Uri's (eg: file://test.txt).
1010
// It might be a good idea to test if the uri points to a file.

editors/code/src/ctx.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export class Ctx {
2121
serverPath: string,
2222
cwd: string,
2323
): Promise<Ctx> {
24-
const client = await createClient(serverPath, cwd);
24+
const client = createClient(serverPath, cwd);
2525
const res = new Ctx(config, extCtx, client, serverPath);
2626
res.pushCleanup(client.start());
2727
await client.onReady();

0 commit comments

Comments
 (0)