diff options
| author | Ifeanyi Orizu <iorizu1811@gmail.com> | 2025-08-10 10:02:03 -0500 |
|---|---|---|
| committer | Ifeanyi Orizu <iorizu1811@gmail.com> | 2025-08-10 17:48:17 -0500 |
| commit | 802fa9255b59c8787d679f5da1f5534f9c0580b0 (patch) | |
| tree | fe0ebe8d654a0e29cc4c72966a3477f51d929b97 | |
| parent | bc5df9b057a1ab9f8d9c387cde9a7dbab0e1462b (diff) | |
| download | rust-802fa9255b59c8787d679f5da1f5534f9c0580b0.tar.gz rust-802fa9255b59c8787d679f5da1f5534f9c0580b0.zip | |
Add config option to exclude locals from doc search
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> | // //  -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": { |
