1use core::fmt;
4use std::sync::Arc;
5
6use parking_lot::Mutex;
7use tinymist_std::{QueryRef, hash::FxHashMap};
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 {
16 touched_by_compile: bool,
18 fid: FileId,
20 source: FileQuery<Source>,
22 buffer: FileQuery<Bytes>,
24}
25
26impl fmt::Debug for SourceCache {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 f.debug_struct("SourceCache").finish()
29 }
30}
31
32#[derive(Clone)]
34pub struct SourceDb {
35 pub is_compiling: bool,
37 pub slots: Arc<Mutex<FxHashMap<FileId, SourceCache>>>,
39}
40
41impl fmt::Debug for SourceDb {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 f.debug_struct("SourceDb").finish()
44 }
45}
46
47impl SourceDb {
48 pub fn set_is_compiling(&mut self, is_compiling: bool) {
50 self.is_compiling = is_compiling;
51 }
52
53 pub fn memory_usage(&self) -> usize {
55 let mut w = self.slots.lock().len() * core::mem::size_of::<SourceCache>();
56 w += self
57 .slots
58 .lock()
59 .values()
60 .map(|slot| {
61 slot.source
62 .get_uninitialized()
63 .and_then(|e| e.as_ref().ok())
64 .map_or(16, |e| e.text().len() * 8)
65 + slot
66 .buffer
67 .get_uninitialized()
68 .and_then(|e| e.as_ref().ok())
69 .map_or(16, |e| e.len())
70 })
71 .sum::<usize>();
72
73 w
74 }
75
76 pub fn iter_dependencies_dyn(&self, f: &mut dyn FnMut(FileId)) {
84 for slot in self.slots.lock().values() {
85 if !slot.touched_by_compile {
86 continue;
87 }
88 f(slot.fid);
89 }
90 }
91
92 pub fn file(&self, fid: FileId, p: &impl FsProvider) -> FileResult<Bytes> {
94 self.slot(fid, |slot| slot.buffer.compute(|| p.read(fid)).cloned())
95 }
96
97 pub fn source(&self, fid: FileId, p: &impl FsProvider) -> FileResult<Source> {
102 self.slot(fid, |slot| {
103 slot.source.compute(|| p.read_source(fid)).cloned()
104 })
105 }
106
107 fn slot<T>(&self, fid: FileId, f: impl FnOnce(&SourceCache) -> T) -> T {
109 let mut slots = self.slots.lock();
110 f({
111 let entry = slots.entry(fid).or_insert_with(|| SourceCache {
112 touched_by_compile: self.is_compiling,
113 fid,
114 source: FileQuery::default(),
115 buffer: FileQuery::default(),
116 });
117 if self.is_compiling && !entry.touched_by_compile {
118 entry.touched_by_compile = true;
121 }
122 entry
123 })
124 }
125
126 pub(crate) fn take(&mut self) -> Self {
128 Self {
129 is_compiling: self.is_compiling,
130 slots: std::mem::take(&mut self.slots),
131 }
132 }
133}