Skip to main content

oak_navigation/
lib.rs

1#![feature(new_range_api)]
2use core::range::Range;
3use oak_core::{Language, TokenType, language::UniversalTokenRole, tree::RedNode, visitor::Visitor};
4
5/// Represents a location in a source file.
6#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
7pub struct Location {
8    /// The URI of the resource.
9    #[serde(with = "oak_core::serde_arc_str")]
10    pub uri: oak_core::Arc<str>,
11    /// The byte range within the resource.
12    #[serde(with = "oak_core::serde_range")]
13    pub range: Range<usize>,
14}
15
16/// Trait for languages that support jumping to definition.
17pub trait DefinitionProvider<L: Language> {
18    /// Returns the definition(s) of the symbol at the given offset.
19    fn definition(&self, root: &RedNode<L>, offset: usize) -> Vec<Location>;
20}
21
22/// Trait for languages that support finding references.
23pub trait ReferencesProvider<L: Language> {
24    /// Returns the references to the symbol at the given offset.
25    fn references(&self, root: &RedNode<L>, offset: usize, include_declaration: bool) -> Vec<Location>;
26}
27
28/// A helper to find all references of a name in a tree.
29pub struct SimpleReferenceFinder<'a, L: Language> {
30    name: &'a str,
31    source: &'a str,
32    uri: oak_core::Arc<str>,
33    references: Vec<Location>,
34    _phantom: std::marker::PhantomData<L>,
35}
36
37impl<'a, L: Language> SimpleReferenceFinder<'a, L> {
38    pub fn find(root: &RedNode<'a, L>, name: &'a str, source: &'a str, uri: impl Into<oak_core::Arc<str>>) -> Vec<Location> {
39        let mut finder = Self { name, source, uri: uri.into(), references: Vec::new(), _phantom: std::marker::PhantomData };
40        finder.visit_node(*root);
41        finder.references
42    }
43}
44
45impl<'a, L: Language> Visitor<'a, L> for SimpleReferenceFinder<'a, L> {
46    fn visit_node(&mut self, node: RedNode<'a, L>) {
47        self.walk_node(node);
48    }
49
50    fn visit_token(&mut self, token: oak_core::tree::RedLeaf<L>) {
51        if token.kind.is_universal(UniversalTokenRole::Name) {
52            let text = &self.source[token.span.clone()];
53            if text == self.name {
54                self.references.push(Location { uri: self.uri.clone(), range: token.span });
55            }
56        }
57    }
58}