Skip to main content

oak_symbols/
lib.rs

1#![feature(new_range_api)]
2use oak_core::{
3    Arc, Range,
4    language::{ElementRole, ElementType, Language, TokenType, UniversalElementRole, UniversalTokenRole},
5    source::Source,
6    tree::{RedNode, RedTree},
7};
8use serde::{Deserialize, Serialize};
9
10/// Represents information about a symbol (e.g., function, variable, class).
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct SymbolInformation {
13    /// The name of the symbol.
14    pub name: String,
15    /// The universal role of the symbol.
16    pub role: UniversalElementRole,
17    /// The URI of the resource.
18    #[serde(with = "oak_core::serde_arc_str")]
19    pub uri: Arc<str>,
20    /// The byte range within the resource.
21    #[serde(with = "oak_core::serde_range")]
22    pub range: Range<usize>,
23    /// The name of the container this symbol is in.
24    pub container_name: Option<String>,
25}
26
27/// Trait for languages that support symbol search and navigation.
28pub trait SymbolProvider<L: Language> {
29    /// Returns symbols defined in the document.
30    fn document_symbols<S: Source + ?Sized>(&self, uri: &str, root: &RedNode<L>, source: &S) -> Vec<SymbolInformation>;
31}
32
33/// A universal symbol provider that works for any language whose ElementType implements role().
34pub struct UniversalSymbolProvider;
35
36impl UniversalSymbolProvider {
37    pub fn new() -> Self {
38        Self
39    }
40
41    fn collect_symbols<L: Language, S: Source + ?Sized>(&self, uri: &str, node: &RedNode<L>, symbols: &mut Vec<SymbolInformation>, container_name: Option<String>, source: &S) {
42        let role = node.green.kind.role();
43
44        if role.universal() == UniversalElementRole::Definition {
45            // Try to find the name of the definition
46            let mut name = None;
47            for child in node.children() {
48                match child {
49                    RedTree::Leaf(leaf) => {
50                        // In many languages, the first name identifier in a definition is its name
51                        if leaf.kind.is_universal(UniversalTokenRole::Name) || leaf.kind.is_universal(UniversalTokenRole::None) {
52                            name = Some(source.get_text_in(leaf.span).to_string());
53                            break;
54                        }
55                    }
56                    _ => {}
57                }
58            }
59
60            let name = name.unwrap_or_else(|| format!("<{:?}>", node.green.kind));
61
62            symbols.push(SymbolInformation { name: name.clone(), role: role.universal(), uri: uri.to_string().into(), range: node.span(), container_name: container_name.clone() });
63
64            // Recurse with this definition as the container
65            for child in node.children() {
66                if let RedTree::Node(child_node) = child {
67                    self.collect_symbols(uri, &child_node, symbols, Some(name.clone()), source);
68                }
69            }
70        }
71        else {
72            // Just recurse
73            for child in node.children() {
74                if let RedTree::Node(child_node) = child {
75                    self.collect_symbols(uri, &child_node, symbols, container_name.clone(), source);
76                }
77            }
78        }
79    }
80}
81
82impl<L: Language> SymbolProvider<L> for UniversalSymbolProvider {
83    fn document_symbols<S: Source + ?Sized>(&self, uri: &str, root: &RedNode<L>, source: &S) -> Vec<SymbolInformation> {
84        let mut symbols = Vec::new();
85        self.collect_symbols(uri, root, &mut symbols, None, source);
86        symbols
87    }
88}