tinymist_world/
snapshot.rs1use core::fmt;
4
5use crate::{CompilerFeat, CompilerWorld, EntryReader, TaskInputs, args::TaskWhen};
6use ecow::EcoString;
7use serde::{Deserialize, Serialize};
8use tinymist_std::typst::TypstDocument;
9
10#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub struct ProjectInsId(pub EcoString);
14
15impl fmt::Display for ProjectInsId {
16 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17 self.0.fmt(f)
18 }
19}
20
21impl ProjectInsId {
22 pub const PRIMARY: ProjectInsId = ProjectInsId(EcoString::inline("primary"));
24}
25
26#[deprecated(note = "Use `CompileSignal` directly.")]
28pub type ExportSignal = CompileSignal;
29
30#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
35#[serde(rename_all = "camelCase")]
36pub struct CompileSignal {
37 pub by_mem_events: bool,
39 pub by_fs_events: bool,
41 pub by_entry_update: bool,
43}
44
45impl CompileSignal {
46 pub fn merge(&mut self, other: CompileSignal) {
48 self.by_mem_events |= other.by_mem_events;
49 self.by_fs_events |= other.by_fs_events;
50 self.by_entry_update |= other.by_entry_update;
51 }
52
53 pub fn any(&self) -> bool {
57 self.by_mem_events || self.by_fs_events || self.by_entry_update
58 }
59
60 pub fn exclude(&self, excluded: Self) -> Self {
62 Self {
63 by_mem_events: self.by_mem_events && !excluded.by_mem_events,
64 by_fs_events: self.by_fs_events && !excluded.by_fs_events,
65 by_entry_update: self.by_entry_update && !excluded.by_entry_update,
66 }
67 }
68
69 pub fn should_run_task_dyn(
71 &self,
72 when: &TaskWhen,
73 docs: Option<&TypstDocument>,
74 ) -> Option<bool> {
75 match docs {
76 Some(TypstDocument::Paged(doc)) => self.should_run_task(when, Some(doc.as_ref())),
77 Some(TypstDocument::Html(doc)) => self.should_run_task(when, Some(doc.as_ref())),
78 None => self.should_run_task::<typst::layout::PagedDocument>(when, None),
79 }
80 }
81
82 pub fn should_run_task<D: typst::Document>(
84 &self,
85 when: &TaskWhen,
86 docs: Option<&D>,
87 ) -> Option<bool> {
88 match when {
89 TaskWhen::Never => Some(false),
90 TaskWhen::Script => Some(self.by_entry_update),
92 TaskWhen::OnType => Some(self.by_mem_events),
93 TaskWhen::OnSave => Some(self.by_fs_events),
94 TaskWhen::OnDocumentHasTitle if self.by_fs_events => {
95 docs.map(|doc| doc.info().title.is_some())
96 }
97 TaskWhen::OnDocumentHasTitle => Some(false),
98 }
99 }
100}
101
102pub struct CompileSnapshot<F: CompilerFeat> {
106 pub id: ProjectInsId,
108 pub signal: CompileSignal,
110 pub world: CompilerWorld<F>,
112 pub success_doc: Option<TypstDocument>,
114}
115
116impl<F: CompilerFeat + 'static> CompileSnapshot<F> {
117 pub fn from_world(world: CompilerWorld<F>) -> Self {
119 Self {
120 id: ProjectInsId("primary".into()),
121 signal: CompileSignal::default(),
122 world,
123 success_doc: None,
124 }
125 }
126
127 pub fn task(mut self, inputs: TaskInputs) -> Self {
133 'check_changed: {
134 if let Some(entry) = &inputs.entry
135 && *entry != self.world.entry_state()
136 {
137 break 'check_changed;
138 }
139 if let Some(inputs) = &inputs.inputs
140 && inputs.clone() != self.world.inputs()
141 {
142 break 'check_changed;
143 }
144
145 return self;
146 };
147
148 self.world = self.world.task(inputs);
149
150 self
151 }
152}
153
154impl<F: CompilerFeat> Clone for CompileSnapshot<F> {
155 fn clone(&self) -> Self {
156 Self {
157 id: self.id.clone(),
158 signal: self.signal,
159 world: self.world.clone(),
160 success_doc: self.success_doc.clone(),
161 }
162 }
163}