tinymist_query/
document_symbol.rs

1use crate::{
2    SyntaxRequest,
3    prelude::*,
4    syntax::{LexicalHierarchy, LexicalScopeKind, get_lexical_hierarchy},
5};
6
7/// The [`textDocument/documentSymbol`] request is sent from the client to the
8/// server to retrieve all symbols found in a given text document.
9///
10/// [`textDocument/documentSymbol`]: https://microsoft.github.io/language-server-protocol/specification#textDocument_documentSymbol
11///
12/// The returned result is either:
13///
14/// * [`DocumentSymbolResponse::Flat`] which is a flat list of all symbols found
15///   in a given text document. Then neither the symbol’s location range nor the
16///   symbol’s container name should be used to infer a hierarchy.
17/// * [`DocumentSymbolResponse::Nested`] which is a hierarchy of symbols found
18///   in a given text document.
19#[derive(Debug, Clone)]
20pub struct DocumentSymbolRequest {
21    /// The path of the document to retrieve symbols from.
22    pub path: PathBuf,
23}
24
25impl SyntaxRequest for DocumentSymbolRequest {
26    type Response = DocumentSymbolResponse;
27
28    fn request(
29        self,
30        source: &Source,
31        position_encoding: PositionEncoding,
32    ) -> Option<Self::Response> {
33        let hierarchy = get_lexical_hierarchy(source, LexicalScopeKind::Symbol)?;
34        let symbols = symbols_in_hierarchy(&hierarchy, source, position_encoding);
35        Some(DocumentSymbolResponse::Nested(symbols))
36    }
37}
38
39#[allow(deprecated)]
40fn symbols_in_hierarchy(
41    hierarchy: &[LexicalHierarchy],
42    source: &Source,
43    position_encoding: PositionEncoding,
44) -> Vec<DocumentSymbol> {
45    hierarchy
46        .iter()
47        .filter(|hierarchy| hierarchy.info.kind.is_valid_lsp_symbol())
48        .map(|hierarchy| {
49            let range = to_lsp_range(hierarchy.info.range.clone(), source, position_encoding);
50
51            DocumentSymbol {
52                name: hierarchy.info.name.to_string(),
53                detail: None,
54                kind: hierarchy.info.kind.clone().into(),
55                tags: None,
56                deprecated: None,
57                range,
58                selection_range: range,
59                children: hierarchy
60                    .children
61                    .as_ref()
62                    .map(|ch| symbols_in_hierarchy(ch, source, position_encoding)),
63            }
64        })
65        .collect()
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71    use crate::tests::*;
72
73    #[test]
74    fn test() {
75        snapshot_testing("document_symbols", &|ctx, path| {
76            let request = DocumentSymbolRequest { path: path.clone() };
77
78            let source = ctx.source_by_path(&path).unwrap();
79
80            let result = request.request(&source, PositionEncoding::Utf16);
81            assert_snapshot!(JsonRepr::new_redacted(result.unwrap(), &REDACT_LOC));
82        });
83    }
84}