use std::collections::VecDeque;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::sync::mpsc::Receiver;
use std::thread;
use crate::config::LanguageRegistry;
use crate::syntax::{Highlighter, Loader};
pub struct PreviewEntry {
pub path: PathBuf,
pub lines: Vec<String>,
pub highlighter: Highlighter,
}
pub struct PreviewLru {
cap: usize,
entries: VecDeque<PreviewEntry>,
}
impl PreviewLru {
pub fn new(cap: usize) -> Self {
Self {
cap,
entries: VecDeque::with_capacity(cap),
}
}
pub fn get(&mut self, path: &Path) -> Option<&PreviewEntry> {
let pos = self.entries.iter().position(|e| e.path == path)?;
if pos != 0 {
let entry = self.entries.remove(pos).unwrap();
self.entries.push_front(entry);
}
self.entries.front()
}
pub fn take(&mut self, path: &Path) -> Option<PreviewEntry> {
let pos = self.entries.iter().position(|e| e.path == path)?;
self.entries.remove(pos)
}
pub fn insert(&mut self, entry: PreviewEntry) {
self.entries.retain(|e| e.path != entry.path);
self.entries.push_front(entry);
while self.entries.len() > self.cap {
self.entries.pop_back();
}
}
}
pub fn spawn_preview_worker(
loader: Arc<Mutex<Loader>>,
languages: LanguageRegistry,
rx: Receiver<PathBuf>,
emit: Box<dyn Fn(PreviewEntry) + Send + 'static>,
) {
thread::spawn(move || {
while let Ok(mut path) = rx.recv() {
while let Ok(latest) = rx.try_recv() {
path = latest;
}
let Some(ext) = path.extension().and_then(|s| s.to_str()) else {
continue;
};
let Some(spec) = languages.by_extension(ext).cloned() else {
continue;
};
let mut highlighter = match loader.lock().unwrap().highlighter_for(&spec) {
Ok(h) => h,
Err(_) => continue,
};
let Ok(source) = std::fs::read_to_string(&path) else {
continue;
};
let lines: Vec<String> = source.lines().map(|s| s.to_string()).collect();
highlighter.refresh(&source, 1);
emit(PreviewEntry {
path,
lines,
highlighter,
});
}
});
}