Skip to main content

lspkit_treesitter/
cache.rs

1//! Content-hash-keyed parse cache.
2//!
3//! Parse trees are cached by `(language_name, content_hash)`. Repeated parses
4//! of identical source bytes return the cached tree directly. Cache eviction
5//! is the caller's responsibility — pass `clear()` between sessions.
6
7use std::collections::hash_map::DefaultHasher;
8use std::hash::{Hash, Hasher};
9use std::sync::Arc;
10
11use dashmap::DashMap;
12use tree_sitter::{Language, Parser, Tree};
13
14use crate::parser::ParserError;
15
16/// FNV-1a-style content hash. Cheap to compute, sufficient for cache keys.
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub struct ContentHash(u64);
19
20impl ContentHash {
21    /// Compute a hash of `source`.
22    #[must_use]
23    pub fn of(source: &[u8]) -> Self {
24        let mut hasher = DefaultHasher::new();
25        source.hash(&mut hasher);
26        Self(hasher.finish())
27    }
28
29    /// Raw value.
30    #[must_use]
31    pub const fn get(self) -> u64 {
32        self.0
33    }
34}
35
36/// Concurrent parse cache.
37#[derive(Default, Clone)]
38pub struct ParseCache {
39    entries: Arc<DashMap<(String, ContentHash), Arc<Tree>>>,
40}
41
42impl ParseCache {
43    /// New empty cache.
44    #[must_use]
45    pub fn new() -> Self {
46        Self::default()
47    }
48
49    /// Parse `source` for `language`, returning a shared tree.
50    ///
51    /// # Errors
52    /// Returns [`ParserError`] if tree-sitter rejects the language.
53    pub fn parse(
54        &self,
55        language_name: &str,
56        language: &Language,
57        source: &[u8],
58    ) -> Result<Arc<Tree>, ParserError> {
59        let key = (language_name.to_owned(), ContentHash::of(source));
60        if let Some(existing) = self.entries.get(&key) {
61            return Ok(existing.clone());
62        }
63        let mut parser = Parser::new();
64        parser
65            .set_language(language)
66            .map_err(|_| ParserError::UnknownExtension(language_name.to_owned()))?;
67        let Some(tree) = parser.parse(source, None) else {
68            return Err(ParserError::UnknownExtension(language_name.to_owned()));
69        };
70        let arc = Arc::new(tree);
71        self.entries.insert(key, arc.clone());
72        Ok(arc)
73    }
74
75    /// Drop all cached trees.
76    pub fn clear(&self) {
77        self.entries.clear();
78    }
79
80    /// Number of cached trees.
81    #[must_use]
82    pub fn len(&self) -> usize {
83        self.entries.len()
84    }
85
86    /// `true` if the cache is empty.
87    #[must_use]
88    pub fn is_empty(&self) -> bool {
89        self.entries.is_empty()
90    }
91}
92
93impl std::fmt::Debug for ParseCache {
94    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95        f.debug_struct("ParseCache")
96            .field("entries", &self.entries.len())
97            .finish()
98    }
99}