1use core::fmt;
4use std::sync::Arc;
5
6use parking_lot::Mutex;
7use tinymist_std::{hash::FxHashMap, QueryRef};
8use tinymist_vfs::{Bytes, FileId, FsProvider};
9use typst::diag::{FileError, FileResult};
10use typst::syntax::Source;
11
12type FileQuery<T> = QueryRef<T, FileError>;
13
14pub struct SourceCache {
15 touched_by_compile: bool,
16 fid: FileId,
17 source: FileQuery<Source>,
18 buffer: FileQuery<Bytes>,
19}
20
21impl fmt::Debug for SourceCache {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 f.debug_struct("SourceCache").finish()
24 }
25}
26
27#[derive(Clone)]
28pub struct SourceDb {
29 pub is_compiling: bool,
30 pub slots: Arc<Mutex<FxHashMap<FileId, SourceCache>>>,
32}
33
34impl fmt::Debug for SourceDb {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 f.debug_struct("SourceDb").finish()
37 }
38}
39
40impl SourceDb {
41 pub fn set_is_compiling(&mut self, is_compiling: bool) {
42 self.is_compiling = is_compiling;
43 }
44
45 pub fn memory_usage(&self) -> usize {
47 let mut w = self.slots.lock().len() * core::mem::size_of::<SourceCache>();
48 w += self
49 .slots
50 .lock()
51 .iter()
52 .map(|(_, slot)| {
53 slot.source
54 .get_uninitialized()
55 .and_then(|e| e.as_ref().ok())
56 .map_or(16, |e| e.text().len() * 8)
57 + slot
58 .buffer
59 .get_uninitialized()
60 .and_then(|e| e.as_ref().ok())
61 .map_or(16, |e| e.len())
62 })
63 .sum::<usize>();
64
65 w
66 }
67
68 pub fn iter_dependencies_dyn(&self, f: &mut dyn FnMut(FileId)) {
76 for slot in self.slots.lock().values() {
77 if !slot.touched_by_compile {
78 continue;
79 }
80 f(slot.fid);
81 }
82 }
83
84 pub fn file(&self, fid: FileId, p: &impl FsProvider) -> FileResult<Bytes> {
86 self.slot(fid, |slot| slot.buffer.compute(|| p.read(fid)).cloned())
87 }
88
89 pub fn source(&self, fid: FileId, p: &impl FsProvider) -> FileResult<Source> {
94 self.slot(fid, |slot| {
95 slot.source.compute(|| p.read_source(fid)).cloned()
96 })
97 }
98
99 fn slot<T>(&self, fid: FileId, f: impl FnOnce(&SourceCache) -> T) -> T {
101 let mut slots = self.slots.lock();
102 f({
103 let entry = slots.entry(fid).or_insert_with(|| SourceCache {
104 touched_by_compile: self.is_compiling,
105 fid,
106 source: FileQuery::default(),
107 buffer: FileQuery::default(),
108 });
109 if self.is_compiling && !entry.touched_by_compile {
110 entry.touched_by_compile = true;
113 }
114 entry
115 })
116 }
117
118 pub(crate) fn take(&mut self) -> Self {
119 Self {
120 is_compiling: self.is_compiling,
121 slots: std::mem::take(&mut self.slots),
122 }
123 }
124}