diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
index 6820f99facf2..ac15af0334fc 100644
--- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
@@ -23,6 +23,11 @@ pub enum StructureNodeKind {
Region,
}
+#[derive(Debug, Clone)]
+pub struct FileStructureConfig {
+ pub exclude_locals: bool,
+}
+
// Feature: File Structure
//
// Provides a tree of the symbols defined in the file. Can be used to
@@ -36,21 +41,24 @@ pub enum StructureNodeKind {
// | VS Code | Ctrl+Shift+O |
//
// 
-pub(crate) fn file_structure(file: &SourceFile) -> Vec {
+pub(crate) fn file_structure(
+ file: &SourceFile,
+ config: &FileStructureConfig,
+) -> Vec {
let mut res = Vec::new();
let mut stack = Vec::new();
for event in file.syntax().preorder_with_tokens() {
match event {
WalkEvent::Enter(NodeOrToken::Node(node)) => {
- if let Some(mut symbol) = structure_node(&node) {
+ if let Some(mut symbol) = structure_node(&node, config) {
symbol.parent = stack.last().copied();
stack.push(res.len());
res.push(symbol);
}
}
WalkEvent::Leave(NodeOrToken::Node(node)) => {
- if structure_node(&node).is_some() {
+ if structure_node(&node, config).is_some() {
stack.pop().unwrap();
}
}
@@ -71,7 +79,7 @@ pub(crate) fn file_structure(file: &SourceFile) -> Vec {
res
}
-fn structure_node(node: &SyntaxNode) -> Option {
+fn structure_node(node: &SyntaxNode, config: &FileStructureConfig) -> Option {
fn decl(node: N, kind: StructureNodeKind) -> Option {
decl_with_detail(&node, None, kind)
}
@@ -187,6 +195,10 @@ fn structure_node(node: &SyntaxNode) -> Option {
Some(node)
},
ast::LetStmt(it) => {
+ if config.exclude_locals {
+ return None;
+ }
+
let pat = it.pat()?;
let mut label = String::new();
@@ -201,7 +213,6 @@ fn structure_node(node: &SyntaxNode) -> Option {
detail: it.ty().map(|ty| ty.to_string()),
deprecated: false,
};
-
Some(node)
},
ast::ExternBlock(it) => {
@@ -254,9 +265,19 @@ mod tests {
use super::*;
+ const DEFAULT_CONFIG: FileStructureConfig = FileStructureConfig { exclude_locals: true };
+
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
+ check_with_config(ra_fixture, &DEFAULT_CONFIG, expect);
+ }
+
+ fn check_with_config(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ config: &FileStructureConfig,
+ expect: Expect,
+ ) {
let file = SourceFile::parse(ra_fixture, span::Edition::CURRENT).ok().unwrap();
- let structure = file_structure(&file);
+ let structure = file_structure(&file, config);
expect.assert_debug_eq(&structure)
}
@@ -701,13 +722,264 @@ fn let_statements() {
),
deprecated: false,
},
+ ]
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_file_structure_include_locals() {
+ check_with_config(
+ r#"
+struct Foo {
+ x: i32
+}
+
+mod m {
+ fn bar1() {}
+ fn bar2(t: T) -> T {}
+ fn bar3(a: A,
+ b: B) -> Vec<
+ u32
+ > {}
+}
+
+enum E { X, Y(i32) }
+type T = ();
+static S: i32 = 42;
+const C: i32 = 42;
+trait Tr {}
+trait Alias = Tr;
+
+macro_rules! mc {
+ () => {}
+}
+
+fn let_statements() {
+ let x = 42;
+ let mut y = x;
+ let Foo {
+ ..
+ } = Foo { x };
+ _ = ();
+ let _ = g();
+}
+"#,
+ &FileStructureConfig { exclude_locals: false },
+ expect![[r#"
+ [
+ StructureNode {
+ parent: None,
+ label: "Foo",
+ navigation_range: 8..11,
+ node_range: 1..26,
+ kind: SymbolKind(
+ Struct,
+ ),
+ detail: None,
+ deprecated: false,
+ },
StructureNode {
parent: Some(
- 27,
+ 0,
),
label: "x",
- navigation_range: 684..685,
- node_range: 680..691,
+ navigation_range: 18..19,
+ node_range: 18..24,
+ kind: SymbolKind(
+ Field,
+ ),
+ detail: Some(
+ "i32",
+ ),
+ deprecated: false,
+ },
+ StructureNode {
+ parent: None,
+ label: "m",
+ navigation_range: 32..33,
+ node_range: 28..158,
+ kind: SymbolKind(
+ Module,
+ ),
+ detail: None,
+ deprecated: false,
+ },
+ StructureNode {
+ parent: Some(
+ 2,
+ ),
+ label: "bar1",
+ navigation_range: 43..47,
+ node_range: 40..52,
+ kind: SymbolKind(
+ Function,
+ ),
+ detail: Some(
+ "fn()",
+ ),
+ deprecated: false,
+ },
+ StructureNode {
+ parent: Some(
+ 2,
+ ),
+ label: "bar2",
+ navigation_range: 60..64,
+ node_range: 57..81,
+ kind: SymbolKind(
+ Function,
+ ),
+ detail: Some(
+ "fn(t: T) -> T",
+ ),
+ deprecated: false,
+ },
+ StructureNode {
+ parent: Some(
+ 2,
+ ),
+ label: "bar3",
+ navigation_range: 89..93,
+ node_range: 86..156,
+ kind: SymbolKind(
+ Function,
+ ),
+ detail: Some(
+ "fn(a: A, b: B) -> Vec< u32 >",
+ ),
+ deprecated: false,
+ },
+ StructureNode {
+ parent: None,
+ label: "E",
+ navigation_range: 165..166,
+ node_range: 160..180,
+ kind: SymbolKind(
+ Enum,
+ ),
+ detail: None,
+ deprecated: false,
+ },
+ StructureNode {
+ parent: Some(
+ 6,
+ ),
+ label: "X",
+ navigation_range: 169..170,
+ node_range: 169..170,
+ kind: SymbolKind(
+ Variant,
+ ),
+ detail: None,
+ deprecated: false,
+ },
+ StructureNode {
+ parent: Some(
+ 6,
+ ),
+ label: "Y",
+ navigation_range: 172..173,
+ node_range: 172..178,
+ kind: SymbolKind(
+ Variant,
+ ),
+ detail: None,
+ deprecated: false,
+ },
+ StructureNode {
+ parent: None,
+ label: "T",
+ navigation_range: 186..187,
+ node_range: 181..193,
+ kind: SymbolKind(
+ TypeAlias,
+ ),
+ detail: Some(
+ "()",
+ ),
+ deprecated: false,
+ },
+ StructureNode {
+ parent: None,
+ label: "S",
+ navigation_range: 201..202,
+ node_range: 194..213,
+ kind: SymbolKind(
+ Static,
+ ),
+ detail: Some(
+ "i32",
+ ),
+ deprecated: false,
+ },
+ StructureNode {
+ parent: None,
+ label: "C",
+ navigation_range: 220..221,
+ node_range: 214..232,
+ kind: SymbolKind(
+ Const,
+ ),
+ detail: Some(
+ "i32",
+ ),
+ deprecated: false,
+ },
+ StructureNode {
+ parent: None,
+ label: "Tr",
+ navigation_range: 239..241,
+ node_range: 233..244,
+ kind: SymbolKind(
+ Trait,
+ ),
+ detail: None,
+ deprecated: false,
+ },
+ StructureNode {
+ parent: None,
+ label: "Alias",
+ navigation_range: 251..256,
+ node_range: 245..262,
+ kind: SymbolKind(
+ TraitAlias,
+ ),
+ detail: None,
+ deprecated: false,
+ },
+ StructureNode {
+ parent: None,
+ label: "mc",
+ navigation_range: 277..279,
+ node_range: 264..296,
+ kind: SymbolKind(
+ Macro,
+ ),
+ detail: None,
+ deprecated: false,
+ },
+ StructureNode {
+ parent: None,
+ label: "let_statements",
+ navigation_range: 301..315,
+ node_range: 298..429,
+ kind: SymbolKind(
+ Function,
+ ),
+ detail: Some(
+ "fn()",
+ ),
+ deprecated: false,
+ },
+ StructureNode {
+ parent: Some(
+ 15,
+ ),
+ label: "x",
+ navigation_range: 328..329,
+ node_range: 324..335,
kind: SymbolKind(
Local,
),
@@ -716,11 +988,11 @@ fn let_statements() {
},
StructureNode {
parent: Some(
- 27,
+ 15,
),
label: "mut y",
- navigation_range: 700..705,
- node_range: 696..710,
+ navigation_range: 344..349,
+ node_range: 340..354,
kind: SymbolKind(
Local,
),
@@ -729,11 +1001,11 @@ fn let_statements() {
},
StructureNode {
parent: Some(
- 27,
+ 15,
),
label: "Foo { .. }",
- navigation_range: 719..741,
- node_range: 715..754,
+ navigation_range: 363..385,
+ node_range: 359..398,
kind: SymbolKind(
Local,
),
@@ -742,11 +1014,11 @@ fn let_statements() {
},
StructureNode {
parent: Some(
- 27,
+ 15,
),
label: "_",
- navigation_range: 804..805,
- node_range: 800..812,
+ navigation_range: 419..420,
+ node_range: 415..427,
kind: SymbolKind(
Local,
),
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index 98877482ed86..5349ebb7c82a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -81,7 +81,7 @@ pub use crate::{
annotations::{Annotation, AnnotationConfig, AnnotationKind, AnnotationLocation},
call_hierarchy::{CallHierarchyConfig, CallItem},
expand_macro::ExpandedMacro,
- file_structure::{StructureNode, StructureNodeKind},
+ file_structure::{FileStructureConfig, StructureNode, StructureNodeKind},
folding_ranges::{Fold, FoldKind},
highlight_related::{HighlightRelatedConfig, HighlightedRange},
hover::{
@@ -430,12 +430,16 @@ impl Analysis {
/// Returns a tree representation of symbols in the file. Useful to draw a
/// file outline.
- pub fn file_structure(&self, file_id: FileId) -> Cancellable> {
+ pub fn file_structure(
+ &self,
+ config: &FileStructureConfig,
+ file_id: FileId,
+ ) -> Cancellable> {
// FIXME: Edition
self.with_db(|db| {
let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id);
-
- file_structure::file_structure(&db.parse(editioned_file_id_wrapper).tree())
+ let source_file = db.parse(editioned_file_id_wrapper).tree();
+ file_structure::file_structure(&source_file, config)
})
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/symbols.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/symbols.rs
index 9fad6723afcd..d7af56d3e15b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/symbols.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/symbols.rs
@@ -1,5 +1,5 @@
//! Read Rust code on stdin, print syntax tree on stdout.
-use ide::Analysis;
+use ide::{Analysis, FileStructureConfig};
use crate::cli::{flags, read_stdin};
@@ -7,7 +7,12 @@ impl flags::Symbols {
pub fn run(self) -> anyhow::Result<()> {
let text = read_stdin()?;
let (analysis, file_id) = Analysis::from_single_file(text);
- let structure = analysis.file_structure(file_id).unwrap();
+ let structure = analysis
+ // The default setting in config.rs (document_symbol_search_excludeLocals) is to exclude
+ // locals because it is unlikely that users want document search to return the names of
+ // local variables, but here we include them deliberately.
+ .file_structure(&FileStructureConfig { exclude_locals: false }, file_id)
+ .unwrap();
for s in structure {
println!("{s:?}");
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 1a00295b9ac1..d4cd56dc55bf 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -858,6 +858,9 @@ config_data! {
/// check will be performed.
check_workspace: bool = true,
+ /// Exclude all locals from document symbol search.
+ document_symbol_search_excludeLocals: bool = true,
+
/// These proc-macros will be ignored when trying to expand them.
///
/// This config takes a map of crate names with the exported proc-macro names to ignore as values.
@@ -1481,6 +1484,13 @@ pub enum FilesWatcher {
Server,
}
+/// Configuration for document symbol search requests.
+#[derive(Debug, Clone)]
+pub struct DocumentSymbolConfig {
+ /// Should locals be excluded.
+ pub search_exclude_locals: bool,
+}
+
#[derive(Debug, Clone)]
pub struct NotificationsConfig {
pub cargo_toml_not_found: bool,
@@ -2438,6 +2448,12 @@ impl Config {
}
}
+ pub fn document_symbol(&self, source_root: Option) -> DocumentSymbolConfig {
+ DocumentSymbolConfig {
+ search_exclude_locals: *self.document_symbol_search_excludeLocals(source_root),
+ }
+ }
+
pub fn workspace_symbol(&self, source_root: Option) -> WorkspaceSymbolConfig {
WorkspaceSymbolConfig {
search_exclude_imports: *self.workspace_symbol_search_excludeImports(source_root),
@@ -3067,7 +3083,7 @@ macro_rules! _config_data {
}) => {
/// Default config values for this grouping.
#[allow(non_snake_case)]
- #[derive(Debug, Clone )]
+ #[derive(Debug, Clone)]
struct $name { $($field: $ty,)* }
impl_for_config_data!{
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
index 25c0aac405e7..6cb28aecf748 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
@@ -8,8 +8,9 @@ use anyhow::Context;
use base64::{Engine, prelude::BASE64_STANDARD};
use ide::{
AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, CompletionFieldsToResolve,
- FilePosition, FileRange, HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query,
- RangeInfo, ReferenceCategory, Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit,
+ FilePosition, FileRange, FileStructureConfig, HoverAction, HoverGotoTypeData,
+ InlayFieldsToResolve, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind,
+ SingleResolve, SourceChange, TextEdit,
};
use ide_db::{FxHashMap, SymbolKind};
use itertools::Itertools;
@@ -566,41 +567,47 @@ pub(crate) fn handle_document_symbol(
let file_id = try_default!(from_proto::file_id(&snap, ¶ms.text_document.uri)?);
let line_index = snap.file_line_index(file_id)?;
- let mut parents: Vec<(lsp_types::DocumentSymbol, Option)> = Vec::new();
+ let mut symbols: Vec<(lsp_types::DocumentSymbol, Option)> = Vec::new();
- for symbol in snap.analysis.file_structure(file_id)? {
+ let config = snap.config.document_symbol(None);
+
+ let structure_nodes = snap.analysis.file_structure(
+ &FileStructureConfig { exclude_locals: config.search_exclude_locals },
+ file_id,
+ )?;
+
+ for node in structure_nodes {
let mut tags = Vec::new();
- if symbol.deprecated {
+ if node.deprecated {
tags.push(SymbolTag::DEPRECATED)
};
#[allow(deprecated)]
- let doc_symbol = lsp_types::DocumentSymbol {
- name: symbol.label,
- detail: symbol.detail,
- kind: to_proto::structure_node_kind(symbol.kind),
+ let symbol = lsp_types::DocumentSymbol {
+ name: node.label,
+ detail: node.detail,
+ kind: to_proto::structure_node_kind(node.kind),
tags: Some(tags),
- deprecated: Some(symbol.deprecated),
- range: to_proto::range(&line_index, symbol.node_range),
- selection_range: to_proto::range(&line_index, symbol.navigation_range),
+ deprecated: Some(node.deprecated),
+ range: to_proto::range(&line_index, node.node_range),
+ selection_range: to_proto::range(&line_index, node.navigation_range),
children: None,
};
- parents.push((doc_symbol, symbol.parent));
+ symbols.push((symbol, node.parent));
}
- // Builds hierarchy from a flat list, in reverse order (so that indices
- // makes sense)
+ // Builds hierarchy from a flat list, in reverse order (so that the indices make sense)
let document_symbols = {
let mut acc = Vec::new();
- while let Some((mut node, parent_idx)) = parents.pop() {
- if let Some(children) = &mut node.children {
+ while let Some((mut symbol, parent_idx)) = symbols.pop() {
+ if let Some(children) = &mut symbol.children {
children.reverse();
}
let parent = match parent_idx {
None => &mut acc,
- Some(i) => parents[i].0.children.get_or_insert_with(Vec::new),
+ Some(i) => symbols[i].0.children.get_or_insert_with(Vec::new),
};
- parent.push(node);
+ parent.push(symbol);
}
acc.reverse();
acc
@@ -610,7 +617,7 @@ pub(crate) fn handle_document_symbol(
document_symbols.into()
} else {
let url = to_proto::url(&snap, file_id);
- let mut symbol_information = Vec::::new();
+ let mut symbol_information = Vec::new();
for symbol in document_symbols {
flatten_document_symbol(&symbol, None, &url, &mut symbol_information);
}
@@ -647,7 +654,7 @@ pub(crate) fn handle_workspace_symbol(
let _p = tracing::info_span!("handle_workspace_symbol").entered();
let config = snap.config.workspace_symbol(None);
- let (all_symbols, libs) = decide_search_scope_and_kind(¶ms, &config);
+ let (all_symbols, libs) = decide_search_kind_and_scope(¶ms, &config);
let query = {
let query: String = params.query.chars().filter(|&c| c != '#' && c != '*').collect();
@@ -670,7 +677,7 @@ pub(crate) fn handle_workspace_symbol(
return Ok(Some(lsp_types::WorkspaceSymbolResponse::Nested(res)));
- fn decide_search_scope_and_kind(
+ fn decide_search_kind_and_scope(
params: &WorkspaceSymbolParams,
config: &WorkspaceSymbolConfig,
) -> (bool, bool) {
diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
index 99a30d8f6213..6ee956fe0dbf 100644
--- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
+++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
@@ -610,6 +610,13 @@ The warnings will be indicated by a blue squiggly underline in code and a blue i
the `Problems Panel`.
+## rust-analyzer.document.symbol.search.excludeLocals {#document.symbol.search.excludeLocals}
+
+Default: `true`
+
+Exclude all locals from document symbol search.
+
+
## rust-analyzer.files.exclude {#files.exclude}
Default: `[]`
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index 470db244f14b..328eb509b4ee 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -1585,6 +1585,16 @@
}
}
},
+ {
+ "title": "Document",
+ "properties": {
+ "rust-analyzer.document.symbol.search.excludeLocals": {
+ "markdownDescription": "Exclude all locals from document symbol search.",
+ "default": true,
+ "type": "boolean"
+ }
+ }
+ },
{
"title": "Files",
"properties": {