1use crate::{
2 SemanticRequest,
3 prelude::*,
4 syntax::{LexicalHierarchy, LexicalScopeKind, get_lexical_hierarchy},
5};
6
7#[derive(Debug, Clone)]
25pub struct SymbolRequest {
26 pub pattern: Option<String>,
29}
30
31impl SemanticRequest for SymbolRequest {
32 type Response = Vec<SymbolInformation>;
33
34 fn request(self, ctx: &mut LocalContext) -> Option<Self::Response> {
35 let mut symbols = vec![];
36
37 for id in ctx.depended_files() {
38 let Ok(source) = ctx.source_by_id(id) else {
39 continue;
40 };
41 let uri = ctx.uri_for_id(id).unwrap();
42 let res = get_lexical_hierarchy(&source, LexicalScopeKind::Symbol).map(|symbols| {
43 filter_document_symbols(
44 &symbols,
45 self.pattern.as_deref(),
46 &source,
47 &uri,
48 ctx.position_encoding(),
49 )
50 });
51
52 if let Some(mut res) = res {
53 symbols.append(&mut res)
54 }
55 }
56
57 Some(symbols)
58 }
59}
60
61#[allow(deprecated)]
62fn filter_document_symbols(
63 hierarchy: &[LexicalHierarchy],
64 query_string: Option<&str>,
65 source: &Source,
66 uri: &Url,
67 position_encoding: PositionEncoding,
68) -> Vec<SymbolInformation> {
69 hierarchy
70 .iter()
71 .flat_map(|hierarchy| {
72 [hierarchy]
73 .into_iter()
74 .chain(hierarchy.children.as_deref().into_iter().flatten())
75 })
76 .filter(|hierarchy| hierarchy.info.kind.is_valid_lsp_symbol())
77 .flat_map(|hierarchy| {
78 if query_string.is_some_and(|s| !hierarchy.info.name.contains(s)) {
79 return None;
80 }
81
82 let rng = to_lsp_range(hierarchy.info.range.clone(), source, position_encoding);
83
84 Some(SymbolInformation {
85 name: hierarchy.info.name.to_string(),
86 kind: hierarchy.info.kind.clone().into(),
87 tags: None,
88 deprecated: None,
89 location: LspLocation {
90 uri: uri.clone(),
91 range: rng,
92 },
93 container_name: None,
94 })
95 })
96 .collect()
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102 use crate::syntax::find_module_level_docs;
103 use crate::tests::*;
104
105 #[test]
106 fn test() {
107 snapshot_testing("symbols", &|ctx, path| {
108 let source = ctx.source_by_path(&path).unwrap();
109
110 let docs = find_module_level_docs(&source).unwrap_or_default();
111 let mut properties = get_test_properties(&docs);
112 properties.insert("compile", "true");
114 let _doc = compile_doc_for_test(ctx, &properties);
115
116 let request = SymbolRequest {
117 pattern: properties.get("pattern").copied().map(str::to_owned),
118 };
119
120 let mut result = request.request(ctx);
121 if let Some(result) = &mut result {
122 result.sort_by(|x, y| {
124 x.name
125 .cmp(&y.name)
126 .then_with(|| x.location.uri.cmp(&y.location.uri))
127 });
128 }
129 assert_snapshot!(JsonRepr::new_redacted(result, &REDACT_LOC));
130 });
131 }
132}