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 let opts = Opts { need_compile: true };
109 snapshot_testing_with("symbols", opts, &|ctx, path| {
110 let source = ctx.source_by_path(&path).unwrap();
111
112 let docs = find_module_level_docs(&source).unwrap_or_default();
113 let properties = get_test_properties(&docs);
114
115 let request = SymbolRequest {
116 pattern: properties.get("pattern").copied().map(str::to_owned),
117 };
118
119 let mut result = request.request(ctx);
120 if let Some(result) = &mut result {
121 result.sort_by(|x, y| {
123 x.name
124 .cmp(&y.name)
125 .then_with(|| x.location.uri.cmp(&y.location.uri))
126 });
127 }
128 assert_snapshot!(JsonRepr::new_redacted(result, &REDACT_LOC));
129 });
130 }
131}