use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::SystemTime;
use dashmap::mapref::entry::Entry;
use dashmap::DashMap;
struct CacheEntry {
outline: Arc<str>,
}
const PARSE_CACHE_LIMIT: usize = 2000;
pub struct OutlineCache {
entries: DashMap<(PathBuf, SystemTime), CacheEntry>,
trees: DashMap<(PathBuf, SystemTime), tree_sitter::Tree>,
tree_count: AtomicUsize,
}
impl Default for OutlineCache {
fn default() -> Self {
Self {
entries: DashMap::new(),
trees: DashMap::new(),
tree_count: AtomicUsize::new(0),
}
}
}
impl OutlineCache {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn get_or_compute(
&self,
path: &Path,
mtime: SystemTime,
compute: impl FnOnce() -> String,
) -> Arc<str> {
match self.entries.entry((path.to_path_buf(), mtime)) {
Entry::Occupied(e) => Arc::clone(&e.get().outline),
Entry::Vacant(e) => {
let outline: Arc<str> = compute().into();
e.insert(CacheEntry {
outline: Arc::clone(&outline),
});
outline
}
}
}
pub fn get_or_parse(
&self,
path: &Path,
mtime: SystemTime,
content: &str,
lang: &tree_sitter::Language,
) -> Option<tree_sitter::Tree> {
let key = (path.to_path_buf(), mtime);
if let Some(tree) = self.trees.get(&key) {
return Some(tree.clone());
}
let mut parser = tree_sitter::Parser::new();
parser.set_language(lang).ok()?;
let tree = parser.parse(content, None)?;
if self.tree_count.load(Ordering::Relaxed) >= PARSE_CACHE_LIMIT {
self.trees.clear();
self.tree_count.store(0, Ordering::Relaxed);
}
match self.trees.entry(key) {
Entry::Occupied(e) => Some(e.get().clone()),
Entry::Vacant(e) => {
self.tree_count.fetch_add(1, Ordering::Relaxed);
Some(e.insert(tree).value().clone())
}
}
}
}