Skip to main content

atomcode_core/semantic/
cache.rs

1use std::collections::HashMap;
2use std::path::{Path, PathBuf};
3use std::time::SystemTime;
4
5use tree_sitter::{Parser, Tree};
6
7use super::language::{Lang, LanguageRegistry};
8
9/// Entry in the AST cache: parsed tree + file modification time.
10struct CacheEntry {
11    tree: Tree,
12    modified: SystemTime,
13    lang: Lang,
14}
15
16/// Cache of parsed ASTs keyed by file path.
17/// Avoids re-parsing files that haven't changed.
18pub struct ASTCache {
19    entries: HashMap<PathBuf, CacheEntry>,
20    parser: Parser,
21}
22
23impl ASTCache {
24    pub fn new() -> Self {
25        Self {
26            entries: HashMap::new(),
27            parser: Parser::new(),
28        }
29    }
30
31    /// Parse a file and return its AST tree + detected language.
32    /// Uses cache if the file hasn't been modified since last parse.
33    /// Parse a file and return its AST tree + detected language.
34    /// Uses cache if the file hasn't been modified since last parse.
35    pub fn get_tree(&mut self, path: &Path) -> Option<(Tree, Lang)> {
36        let modified = std::fs::metadata(path).ok()?.modified().ok()?;
37        let abs = std::fs::canonicalize(path).unwrap_or_else(|_| path.to_path_buf());
38
39        // Check cache validity
40        if let Some(entry) = self.entries.get(&abs) {
41            if entry.modified == modified {
42                return Some((entry.tree.clone(), entry.lang));
43            }
44        }
45
46        // Parse fresh
47        let lang = LanguageRegistry::detect(path)?;
48        self.parser.set_language(&lang.grammar()).ok()?;
49        let source = std::fs::read_to_string(path).ok()?;
50        let tree = self.parser.parse(&source, None)?;
51
52        self.entries.insert(
53            abs,
54            CacheEntry {
55                tree: tree.clone(),
56                modified,
57                lang,
58            },
59        );
60
61        Some((tree, lang))
62    }
63
64    /// Parse source code directly (not from file). No caching.
65    pub fn parse_source(&mut self, source: &str, lang: Lang) -> Option<Tree> {
66        self.parser.set_language(&lang.grammar()).ok()?;
67        self.parser.parse(source, None)
68    }
69
70    /// Invalidate cache for a specific file (e.g. after edit_file).
71    pub fn invalidate(&mut self, path: &Path) {
72        let abs = std::fs::canonicalize(path).unwrap_or_else(|_| path.to_path_buf());
73        self.entries.remove(&abs);
74    }
75
76    /// Clear entire cache.
77    pub fn clear(&mut self) {
78        self.entries.clear();
79    }
80}