mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-08-31 15:47:31 +00:00
Add config option to exclude locals from doc search
This commit is contained in:
parent
caef0f46fd
commit
dc6e6d2b86
7 changed files with 350 additions and 28 deletions
|
@ -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(
|
||||
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) -> 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(
|
||||
15,
|
||||
),
|
||||
label: "x",
|
||||
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,
|
||||
),
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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:?}");
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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: `[]`
|
||||
|
|
|
@ -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": {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue