about summary refs log tree commit diff
diff options
context:
space:
mode:
authorIfeanyi Orizu <iorizu1811@gmail.com>2025-08-10 10:02:03 -0500
committerIfeanyi Orizu <iorizu1811@gmail.com>2025-08-10 17:48:17 -0500
commit802fa9255b59c8787d679f5da1f5534f9c0580b0 (patch)
treefe0ebe8d654a0e29cc4c72966a3477f51d929b97
parentbc5df9b057a1ab9f8d9c387cde9a7dbab0e1462b (diff)
downloadrust-802fa9255b59c8787d679f5da1f5534f9c0580b0.tar.gz
rust-802fa9255b59c8787d679f5da1f5534f9c0580b0.zip
Add config option to exclude locals from doc search
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/file_structure.rs307
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/lib.rs12
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/symbols.rs9
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs16
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs17
-rw-r--r--src/tools/rust-analyzer/docs/book/src/configuration_generated.md7
-rw-r--r--src/tools/rust-analyzer/editors/code/package.json10
7 files changed, 350 insertions, 28 deletions
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 6820f99facf..e0cd32baa97 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 | <kbd>Ctrl+Shift+O</kbd> |
 //
 // ![File Structure](https://user-images.githubusercontent.com/48062697/113020654-b42fc800-917a-11eb-8388-e7dc4d92b02e.gif)
-pub(crate) fn file_structure(file: &SourceFile) -> Vec<StructureNode> {
+pub(crate) fn file_structure(
+    file: &SourceFile,
+    config: &FileStructureConfig,
+) -> Vec<StructureNode> {
     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<StructureNode> {
     res
 }
 
-fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
+fn structure_node(node: &SyntaxNode, config: &FileStructureConfig) -> Option<StructureNode> {
     fn decl<N: HasName + HasAttrs>(node: N, kind: StructureNodeKind) -> Option<StructureNode> {
         decl_with_detail(&node, None, kind)
     }
@@ -187,6 +195,10 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
                 Some(node)
             },
             ast::LetStmt(it) => {
+                if config.exclude_locals {
+                    return None;
+                }
+
                 let pat = it.pat()?;
 
                 let mut label = String::new();
@@ -254,9 +266,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 +723,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) -> T {}
+    fn bar3<A,
+        B>(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(
+                            0,
+                        ),
+                        label: "x",
+                        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) -> T",
+                        ),
+                        deprecated: false,
+                    },
+                    StructureNode {
+                        parent: Some(
+                            2,
+                        ),
+                        label: "bar3",
+                        navigation_range: 89..93,
+                        node_range: 86..156,
+                        kind: SymbolKind(
+                            Function,
+                        ),
+                        detail: Some(
+                            "fn<A, B>(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(
-                            27,
+                            15,
                         ),
                         label: "x",
-                        navigation_range: 684..685,
-                        node_range: 680..691,
+                        navigation_range: 328..329,
+                        node_range: 324..335,
                         kind: SymbolKind(
                             Local,
                         ),
@@ -716,11 +989,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 +1002,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 +1015,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 98877482ed8..5349ebb7c82 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<Vec<StructureNode>> {
+    pub fn file_structure(
+        &self,
+        config: &FileStructureConfig,
+        file_id: FileId,
+    ) -> Cancellable<Vec<StructureNode>> {
         // 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 9fad6723afc..d7af56d3e15 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 1a00295b9ac..77e5a8e079f 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<SourceRootId>) -> DocumentSymbolConfig {
+        DocumentSymbolConfig {
+            search_exclude_locals: *self.document_symbol_search_excludeLocals(source_root),
+        }
+    }
+
     pub fn workspace_symbol(&self, source_root: Option<SourceRootId>) -> WorkspaceSymbolConfig {
         WorkspaceSymbolConfig {
             search_exclude_imports: *self.workspace_symbol_search_excludeImports(source_root),
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 25c0aac405e..74ed5e344af 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;
@@ -568,7 +569,14 @@ pub(crate) fn handle_document_symbol(
 
     let mut parents: Vec<(lsp_types::DocumentSymbol, Option<usize>)> = 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 symbol in structure_nodes {
         let mut tags = Vec::new();
         if symbol.deprecated {
             tags.push(SymbolTag::DEPRECATED)
@@ -588,8 +596,7 @@ pub(crate) fn handle_document_symbol(
         parents.push((doc_symbol, symbol.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 indices make sense)
     let document_symbols = {
         let mut acc = Vec::new();
         while let Some((mut node, parent_idx)) = parents.pop() {
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 99a30d8f621..6ee956fe0db 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 470db244f14..328eb509b4e 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -1586,6 +1586,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": {
                     "rust-analyzer.files.exclude": {