1use std::{
16 borrow::Cow,
17 num::NonZeroUsize,
18 ops::Deref,
19 path::{Path, PathBuf},
20 sync::{Arc, LazyLock, OnceLock},
21};
22
23use ecow::EcoVec;
24use tinymist_std::{ImmutPath, error::prelude::*};
25use tinymist_vfs::{
26 FileId, FsProvider, PathResolution, RevisingVfs, SourceCache, Vfs, WorkspaceResolver,
27};
28use typst::{
29 Features, Library, World, WorldExt,
30 diag::{At, EcoString, FileError, FileResult, SourceResult, eco_format},
31 foundations::{Bytes, Datetime, Dict},
32 syntax::{Source, Span, VirtualPath},
33 text::{Font, FontBook},
34 utils::LazyHash,
35};
36
37use crate::{
38 CompileSnapshot, MEMORY_MAIN_ENTRY,
39 package::{PackageRegistry, PackageSpec},
40 source::SourceDb,
41};
42use crate::{
43 WorldComputeGraph,
44 parser::{
45 OffsetEncoding, SemanticToken, SemanticTokensLegend, get_semantic_tokens_full,
46 get_semantic_tokens_legend,
47 },
48};
49use crate::entry::{DETACHED_ENTRY, EntryManager, EntryReader, EntryState};
51use crate::{CompilerFeat, ShadowApi, WorldDeps, font::FontResolver};
52
53type CodespanResult<T> = Result<T, CodespanError>;
54type CodespanError = codespan_reporting::files::Error;
55
56#[derive(Debug)]
63pub struct CompilerUniverse<F: CompilerFeat> {
64 entry: EntryState,
67 inputs: Arc<LazyHash<Dict>>,
69 pub features: Features,
71
72 pub font_resolver: Arc<F::FontResolver>,
74 pub registry: Arc<F::Registry>,
76 vfs: Vfs<F::AccessModel>,
78
79 pub revision: NonZeroUsize,
83
84 pub creation_timestamp: Option<i64>,
86}
87
88impl<F: CompilerFeat> CompilerUniverse<F> {
90 pub fn new_raw(
97 entry: EntryState,
98 features: Features,
99 inputs: Option<Arc<LazyHash<Dict>>>,
100 vfs: Vfs<F::AccessModel>,
101 package_registry: Arc<F::Registry>,
102 font_resolver: Arc<F::FontResolver>,
103 creation_timestamp: Option<i64>,
104 ) -> Self {
105 Self {
106 entry,
107 inputs: inputs.unwrap_or_default(),
108 features,
109
110 revision: NonZeroUsize::new(1).expect("initial revision is 1"),
111
112 font_resolver,
113 registry: package_registry,
114 vfs,
115 creation_timestamp,
116 }
117 }
118
119 pub fn with_entry_file(mut self, entry_file: PathBuf) -> Self {
121 let _ = self.increment_revision(|this| this.set_entry_file_(entry_file.as_path().into()));
122 self
123 }
124
125 pub fn entry_file(&self) -> Option<PathResolution> {
127 self.path_for_id(self.main_id()?).ok()
128 }
129
130 pub fn inputs(&self) -> Arc<LazyHash<Dict>> {
132 self.inputs.clone()
133 }
134
135 pub fn snapshot(&self) -> CompilerWorld<F> {
137 self.snapshot_with(None)
138 }
139
140 pub fn computation(&self) -> Arc<WorldComputeGraph<F>> {
146 let world = self.snapshot();
147 let snap = CompileSnapshot::from_world(world);
148 WorldComputeGraph::new(snap)
149 }
150
151 pub fn computation_with(&self, mutant: TaskInputs) -> Arc<WorldComputeGraph<F>> {
153 let world = self.snapshot_with(Some(mutant));
154 let snap = CompileSnapshot::from_world(world);
155 WorldComputeGraph::new(snap)
156 }
157
158 pub fn snapshot_with_entry_content(
161 &self,
162 content: Bytes,
163 inputs: Option<TaskInputs>,
164 ) -> Arc<WorldComputeGraph<F>> {
165 let mut world = if self.main_id().is_some() {
167 self.snapshot_with(inputs)
168 } else {
169 self.snapshot_with(Some(TaskInputs {
170 entry: Some(
171 self.entry_state()
172 .select_in_workspace(MEMORY_MAIN_ENTRY.vpath().as_rooted_path()),
173 ),
174 inputs: inputs.and_then(|i| i.inputs),
175 }))
176 };
177
178 world.map_shadow_by_id(world.main(), content).unwrap();
179
180 let snap = CompileSnapshot::from_world(world);
181 WorldComputeGraph::new(snap)
182 }
183
184 pub fn snapshot_with(&self, mutant: Option<TaskInputs>) -> CompilerWorld<F> {
186 let w = CompilerWorld {
187 entry: self.entry.clone(),
188 features: self.features.clone(),
189 inputs: self.inputs.clone(),
190 library: create_library(self.inputs.clone(), self.features.clone()),
191 font_resolver: self.font_resolver.clone(),
192 registry: self.registry.clone(),
193 vfs: self.vfs.snapshot(),
194 revision: self.revision,
195 source_db: SourceDb {
196 is_compiling: true,
197 slots: Default::default(),
198 },
199 now: OnceLock::new(),
200 creation_timestamp: self.creation_timestamp,
201 };
202
203 mutant.map(|m| w.task(m)).unwrap_or(w)
204 }
205
206 pub fn increment_revision<T>(&mut self, f: impl FnOnce(&mut RevisingUniverse<F>) -> T) -> T {
208 f(&mut RevisingUniverse {
209 vfs_revision: self.vfs.revision(),
210 creation_timestamp_changed: false,
211 font_changed: false,
212 font_revision: self.font_resolver.revision(),
213 registry_changed: false,
214 registry_revision: self.registry.revision(),
215 view_changed: false,
216 inner: self,
217 })
218 }
219
220 fn mutate_entry_(&mut self, mut state: EntryState) -> SourceResult<EntryState> {
222 std::mem::swap(&mut self.entry, &mut state);
223 Ok(state)
224 }
225
226 fn set_entry_file_(&mut self, entry_file: Arc<Path>) -> SourceResult<()> {
228 let state = self.entry_state();
229 let state = state
230 .try_select_path_in_workspace(&entry_file)
231 .map_err(|e| eco_format!("cannot select entry file out of workspace: {e}"))
232 .at(Span::detached())?
233 .ok_or_else(|| eco_format!("failed to determine root"))
234 .at(Span::detached())?;
235
236 self.mutate_entry_(state).map(|_| ())?;
237 Ok(())
238 }
239
240 pub fn vfs(&self) -> &Vfs<F::AccessModel> {
244 &self.vfs
245 }
246}
247
248impl<F: CompilerFeat> CompilerUniverse<F> {
249 pub fn reset(&mut self) {
251 self.vfs.reset_all();
252 }
254
255 pub fn evict(&mut self, vfs_threshold: usize) {
257 self.vfs.reset_access_model();
258 self.vfs.evict(vfs_threshold);
259 }
260
261 pub fn path_for_id(&self, id: FileId) -> Result<PathResolution, FileError> {
263 self.vfs.file_path(id)
264 }
265
266 pub fn id_for_path(&self, path: &Path) -> Option<FileId> {
268 let root = self.entry.workspace_root()?;
269 Some(WorkspaceResolver::workspace_file(
270 Some(&root),
271 VirtualPath::new(path.strip_prefix(&root).ok()?),
272 ))
273 }
274
275 pub fn get_semantic_token_legend(&self) -> Arc<SemanticTokensLegend> {
277 Arc::new(get_semantic_tokens_legend())
278 }
279
280 pub fn get_semantic_tokens(
282 &self,
283 file_path: Option<String>,
284 encoding: OffsetEncoding,
285 ) -> Result<Arc<Vec<SemanticToken>>> {
286 let world = match file_path {
287 Some(e) => {
288 let path = Path::new(&e);
289 let s = self
290 .entry_state()
291 .try_select_path_in_workspace(path)?
292 .ok_or_else(|| error_once!("cannot select file", path: e))?;
293
294 self.snapshot_with(Some(TaskInputs {
295 entry: Some(s),
296 inputs: None,
297 }))
298 }
299 None => self.snapshot(),
300 };
301
302 let src = world
303 .source(world.main())
304 .map_err(|e| error_once!("cannot access source file", err: e))?;
305 Ok(Arc::new(get_semantic_tokens_full(&src, encoding)))
306 }
307}
308
309impl<F: CompilerFeat> ShadowApi for CompilerUniverse<F> {
310 #[inline]
311 fn reset_shadow(&mut self) {
312 self.increment_revision(|this| this.vfs.revise().reset_shadow())
313 }
314
315 fn shadow_paths(&self) -> Vec<Arc<Path>> {
316 self.vfs.shadow_paths()
317 }
318
319 fn shadow_ids(&self) -> Vec<FileId> {
320 self.vfs.shadow_ids()
321 }
322
323 #[inline]
324 fn map_shadow(&mut self, path: &Path, content: Bytes) -> FileResult<()> {
325 self.increment_revision(|this| this.vfs().map_shadow(path, Ok(content).into()))
326 }
327
328 #[inline]
329 fn unmap_shadow(&mut self, path: &Path) -> FileResult<()> {
330 self.increment_revision(|this| this.vfs().unmap_shadow(path))
331 }
332
333 #[inline]
334 fn map_shadow_by_id(&mut self, file_id: FileId, content: Bytes) -> FileResult<()> {
335 self.increment_revision(|this| this.vfs().map_shadow_by_id(file_id, Ok(content).into()))
336 }
337
338 #[inline]
339 fn unmap_shadow_by_id(&mut self, file_id: FileId) -> FileResult<()> {
340 self.increment_revision(|this| {
341 this.vfs().remove_shadow_by_id(file_id);
342 Ok(())
343 })
344 }
345}
346
347impl<F: CompilerFeat> EntryReader for CompilerUniverse<F> {
348 fn entry_state(&self) -> EntryState {
349 self.entry.clone()
350 }
351}
352
353impl<F: CompilerFeat> EntryManager for CompilerUniverse<F> {
354 fn mutate_entry(&mut self, state: EntryState) -> SourceResult<EntryState> {
355 self.increment_revision(|this| this.mutate_entry_(state))
356 }
357}
358
359pub struct RevisingUniverse<'a, F: CompilerFeat> {
361 view_changed: bool,
363 vfs_revision: NonZeroUsize,
365 font_changed: bool,
367 creation_timestamp_changed: bool,
369 font_revision: Option<NonZeroUsize>,
371 registry_changed: bool,
373 registry_revision: Option<NonZeroUsize>,
375 pub inner: &'a mut CompilerUniverse<F>,
377}
378
379impl<F: CompilerFeat> std::ops::Deref for RevisingUniverse<'_, F> {
380 type Target = CompilerUniverse<F>;
381
382 fn deref(&self) -> &Self::Target {
383 self.inner
384 }
385}
386
387impl<F: CompilerFeat> std::ops::DerefMut for RevisingUniverse<'_, F> {
388 fn deref_mut(&mut self) -> &mut Self::Target {
389 self.inner
390 }
391}
392
393impl<F: CompilerFeat> Drop for RevisingUniverse<'_, F> {
394 fn drop(&mut self) {
395 let mut view_changed = self.view_changed;
396 if self.font_changed() {
399 view_changed = true;
400 }
401 if self.registry_changed() {
404 view_changed = true;
405
406 log::info!("resetting shadow registry_changed");
408 self.vfs.reset_read();
409 }
410 let view_changed = view_changed || self.vfs_changed();
411
412 if view_changed {
413 self.vfs.reset_access_model();
414 let revision = &mut self.revision;
415 *revision = revision.checked_add(1).unwrap();
416 }
417 }
418}
419
420impl<F: CompilerFeat> RevisingUniverse<'_, F> {
421 pub fn vfs(&mut self) -> RevisingVfs<'_, F::AccessModel> {
423 self.vfs.revise()
424 }
425
426 pub fn set_fonts(&mut self, fonts: Arc<F::FontResolver>) {
428 self.font_changed = true;
429 self.inner.font_resolver = fonts;
430 }
431
432 pub fn set_package(&mut self, packages: Arc<F::Registry>) {
434 self.registry_changed = true;
435 self.inner.registry = packages;
436 }
437
438 pub fn set_inputs(&mut self, inputs: Arc<LazyHash<Dict>>) {
440 self.view_changed = true;
441 self.inner.inputs = inputs;
442 }
443
444 pub fn set_creation_timestamp(&mut self, creation_timestamp: Option<i64>) {
446 self.creation_timestamp_changed = creation_timestamp != self.inner.creation_timestamp;
447 self.inner.creation_timestamp = creation_timestamp;
448 }
449
450 pub fn set_entry_file(&mut self, entry_file: Arc<Path>) -> SourceResult<()> {
452 self.view_changed = true;
453 self.inner.set_entry_file_(entry_file)
454 }
455
456 pub fn mutate_entry(&mut self, state: EntryState) -> SourceResult<EntryState> {
458 self.view_changed = true;
459
460 let root_changed = self.inner.entry.workspace_root() != state.workspace_root();
462 if root_changed {
463 log::info!("resetting shadow root_changed");
464 self.vfs.reset_read();
465 }
466
467 self.inner.mutate_entry_(state)
468 }
469
470 pub fn flush(&mut self) {
472 self.view_changed = true;
473 }
474
475 pub fn font_changed(&self) -> bool {
477 self.font_changed && is_revision_changed(self.font_revision, self.font_resolver.revision())
478 }
479
480 pub fn creation_timestamp_changed(&self) -> bool {
482 self.creation_timestamp_changed
483 }
484
485 pub fn registry_changed(&self) -> bool {
487 self.registry_changed
488 && is_revision_changed(self.registry_revision, self.registry.revision())
489 }
490
491 pub fn vfs_changed(&self) -> bool {
493 self.vfs_revision != self.vfs.revision()
494 }
495}
496
497fn is_revision_changed(a: Option<NonZeroUsize>, b: Option<NonZeroUsize>) -> bool {
499 a.is_none() || b.is_none() || a != b
500}
501
502#[cfg(any(feature = "web", feature = "system"))]
503type NowStorage = chrono::DateTime<chrono::Local>;
504#[cfg(not(any(feature = "web", feature = "system")))]
505type NowStorage = tinymist_std::time::UtcDateTime;
506
507pub struct CompilerWorld<F: CompilerFeat> {
509 entry: EntryState,
512 inputs: Arc<LazyHash<Dict>>,
514 features: Features,
516
517 pub library: Arc<LazyHash<Library>>,
519 pub font_resolver: Arc<F::FontResolver>,
521 pub registry: Arc<F::Registry>,
523 vfs: Vfs<F::AccessModel>,
525
526 revision: NonZeroUsize,
527 source_db: SourceDb,
529 now: OnceLock<NowStorage>,
532 creation_timestamp: Option<i64>,
534}
535
536impl<F: CompilerFeat> Clone for CompilerWorld<F> {
537 fn clone(&self) -> Self {
538 self.task(TaskInputs::default())
539 }
540}
541
542#[derive(Debug, Default)]
544pub struct TaskInputs {
545 pub entry: Option<EntryState>,
547 pub inputs: Option<Arc<LazyHash<Dict>>>,
549}
550
551impl<F: CompilerFeat> CompilerWorld<F> {
552 pub fn task(&self, mutant: TaskInputs) -> CompilerWorld<F> {
554 let _ = self.today(None);
556
557 let library = mutant
558 .inputs
559 .clone()
560 .map(|inputs| create_library(inputs, self.features.clone()));
561
562 let root_changed = if let Some(e) = mutant.entry.as_ref() {
563 self.entry.workspace_root() != e.workspace_root()
564 } else {
565 false
566 };
567
568 let mut world = CompilerWorld {
569 features: self.features.clone(),
570 inputs: mutant.inputs.unwrap_or_else(|| self.inputs.clone()),
571 library: library.unwrap_or_else(|| self.library.clone()),
572 entry: mutant.entry.unwrap_or_else(|| self.entry.clone()),
573 font_resolver: self.font_resolver.clone(),
574 registry: self.registry.clone(),
575 vfs: self.vfs.snapshot(),
576 revision: self.revision,
577 source_db: self.source_db.clone(),
578 now: self.now.clone(),
579 creation_timestamp: self.creation_timestamp,
580 };
581
582 if root_changed {
583 world.vfs.reset_read();
584 }
585
586 world
587 }
588
589 pub fn reset_read(&mut self) {
591 self.vfs.reset_read();
592 }
593
594 pub fn take_source_cache(&mut self) -> SourceCache {
596 self.vfs.take_source_cache()
597 }
598
599 pub fn clone_source_cache(&mut self) -> SourceCache {
601 self.vfs.clone_source_cache()
602 }
603
604 pub fn take_db(&mut self) -> SourceDb {
606 self.source_db.take()
607 }
608
609 pub fn vfs(&self) -> &Vfs<F::AccessModel> {
611 &self.vfs
612 }
613
614 pub fn inputs(&self) -> Arc<LazyHash<Dict>> {
616 self.inputs.clone()
617 }
618
619 pub fn set_is_compiling(&mut self, is_compiling: bool) {
623 self.source_db.is_compiling = is_compiling;
624 }
625
626 pub fn revision(&self) -> NonZeroUsize {
628 self.revision
629 }
630
631 pub fn evict_vfs(&mut self, threshold: usize) {
633 self.vfs.evict(threshold);
634 }
635
636 pub fn evict_source_cache(&mut self, threshold: usize) {
638 self.vfs
639 .clone_source_cache()
640 .evict(self.vfs.revision(), threshold);
641 }
642
643 pub fn path_for_id(&self, id: FileId) -> Result<PathResolution, FileError> {
645 self.vfs.file_path(id)
646 }
647
648 pub fn id_for_path(&self, path: &Path) -> Option<FileId> {
650 let root = self.entry.workspace_root()?;
651 Some(WorkspaceResolver::workspace_file(
652 Some(&root),
653 VirtualPath::new(path.strip_prefix(&root).ok()?),
654 ))
655 }
656
657 pub fn file_id_by_path(&self, path: &Path) -> FileResult<FileId> {
659 match self.id_for_path(path) {
661 Some(id) => Ok(id),
662 None => WorkspaceResolver::file_with_parent_root(path).ok_or_else(|| {
663 let reason = eco_format!("invalid path: {path:?}");
664 FileError::Other(Some(reason))
665 }),
666 }
667 }
668
669 pub fn source_by_path(&self, path: &Path) -> FileResult<Source> {
671 self.source(self.file_id_by_path(path)?)
672 }
673
674 pub fn depended_files(&self) -> EcoVec<FileId> {
676 let mut deps = EcoVec::new();
677 self.iter_dependencies(&mut |file_id| {
678 deps.push(file_id);
679 });
680 deps
681 }
682
683 pub fn depended_fs_paths(&self) -> EcoVec<ImmutPath> {
685 let mut deps = EcoVec::new();
686 self.iter_dependencies(&mut |file_id| {
687 if let Ok(path) = self.path_for_id(file_id) {
688 deps.push(path.as_path().into());
689 }
690 });
691 deps
692 }
693
694 pub fn packages(&self) -> &[(PackageSpec, Option<EcoString>)] {
701 self.registry.packages()
702 }
703
704 pub fn paged_task(&self) -> Cow<'_, CompilerWorld<F>> {
706 let force_html = self.features.is_enabled(typst::Feature::Html);
707 let enabled_paged = !self.library.features.is_enabled(typst::Feature::Html) || force_html;
708
709 if enabled_paged {
710 return Cow::Borrowed(self);
711 }
712
713 let mut world = self.clone();
714 world.library = create_library(world.inputs.clone(), self.features.clone());
715
716 Cow::Owned(world)
717 }
718
719 pub fn html_task(&self) -> Cow<'_, CompilerWorld<F>> {
721 let enabled_html = self.library.features.is_enabled(typst::Feature::Html);
722
723 if enabled_html {
724 return Cow::Borrowed(self);
725 }
726
727 let features = typst::Features::from_iter([typst::Feature::Html]);
730
731 let mut world = self.clone();
732 world.library = create_library(world.inputs.clone(), features);
733
734 Cow::Owned(world)
735 }
736}
737
738impl<F: CompilerFeat> ShadowApi for CompilerWorld<F> {
739 #[inline]
740 fn shadow_ids(&self) -> Vec<FileId> {
741 self.vfs.shadow_ids()
742 }
743
744 #[inline]
745 fn shadow_paths(&self) -> Vec<Arc<Path>> {
746 self.vfs.shadow_paths()
747 }
748
749 #[inline]
750 fn reset_shadow(&mut self) {
751 self.vfs.revise().reset_shadow()
752 }
753
754 #[inline]
755 fn map_shadow(&mut self, path: &Path, content: Bytes) -> FileResult<()> {
756 self.vfs.revise().map_shadow(path, Ok(content).into())
757 }
758
759 #[inline]
760 fn unmap_shadow(&mut self, path: &Path) -> FileResult<()> {
761 self.vfs.revise().unmap_shadow(path)
762 }
763
764 #[inline]
765 fn map_shadow_by_id(&mut self, file_id: FileId, content: Bytes) -> FileResult<()> {
766 self.vfs
767 .revise()
768 .map_shadow_by_id(file_id, Ok(content).into())
769 }
770
771 #[inline]
772 fn unmap_shadow_by_id(&mut self, file_id: FileId) -> FileResult<()> {
773 self.vfs.revise().remove_shadow_by_id(file_id);
774 Ok(())
775 }
776}
777
778impl<F: CompilerFeat> FsProvider for CompilerWorld<F> {
779 fn file_path(&self, file_id: FileId) -> FileResult<PathResolution> {
780 self.vfs.file_path(file_id)
781 }
782
783 fn read(&self, file_id: FileId) -> FileResult<Bytes> {
784 self.vfs.read(file_id)
785 }
786
787 fn read_source(&self, file_id: FileId) -> FileResult<Source> {
788 self.vfs.source(file_id)
789 }
790}
791
792impl<F: CompilerFeat> World for CompilerWorld<F> {
793 fn library(&self) -> &LazyHash<Library> {
795 self.library.as_ref()
796 }
797
798 fn main(&self) -> FileId {
800 self.entry.main().unwrap_or_else(|| *DETACHED_ENTRY)
801 }
802
803 fn font(&self, id: usize) -> Option<Font> {
805 self.font_resolver.font(id)
806 }
807
808 fn book(&self) -> &LazyHash<FontBook> {
810 self.font_resolver.font_book()
811 }
812
813 fn source(&self, id: FileId) -> FileResult<Source> {
820 static DETACH_SOURCE: LazyLock<Source> =
821 LazyLock::new(|| Source::new(*DETACHED_ENTRY, String::new()));
822
823 if id == *DETACHED_ENTRY {
824 return Ok(DETACH_SOURCE.clone());
825 }
826
827 self.source_db.source(id, self)
828 }
829
830 fn file(&self, id: FileId) -> FileResult<Bytes> {
832 self.source_db.file(id, self)
833 }
834
835 #[cfg(any(feature = "web", feature = "system"))]
843 fn today(&self, offset: Option<i64>) -> Option<Datetime> {
844 use chrono::{Datelike, Duration};
845
846 let now = self.now.get_or_init(|| {
847 if let Some(timestamp) = self.creation_timestamp {
848 chrono::DateTime::from_timestamp(timestamp, 0)
849 .unwrap_or_else(|| tinymist_std::time::now().into())
850 .into()
851 } else {
852 tinymist_std::time::now().into()
853 }
854 });
855
856 let naive = match offset {
857 None => now.naive_local(),
858 Some(o) => now.naive_utc() + Duration::try_hours(o)?,
859 };
860
861 Datetime::from_ymd(
862 naive.year(),
863 naive.month().try_into().ok()?,
864 naive.day().try_into().ok()?,
865 )
866 }
867
868 #[cfg(not(any(feature = "web", feature = "system")))]
876 fn today(&self, offset: Option<i64>) -> Option<Datetime> {
877 use tinymist_std::time::{Duration, now, to_typst_time};
878
879 let now = self.now.get_or_init(|| {
880 if let Some(timestamp) = self.creation_timestamp {
881 tinymist_std::time::UtcDateTime::from_unix_timestamp(timestamp)
882 .unwrap_or_else(|_| now().into())
883 } else {
884 now().into()
885 }
886 });
887
888 let now = offset
889 .and_then(|offset| {
890 let dur = Duration::from_secs(offset.checked_mul(3600)? as u64)
891 .try_into()
892 .ok()?;
893 now.checked_add(dur)
894 })
895 .unwrap_or(*now);
896
897 Some(to_typst_time(now))
898 }
899}
900
901impl<F: CompilerFeat> EntryReader for CompilerWorld<F> {
902 fn entry_state(&self) -> EntryState {
903 self.entry.clone()
904 }
905}
906
907impl<F: CompilerFeat> WorldDeps for CompilerWorld<F> {
908 #[inline]
909 fn iter_dependencies(&self, f: &mut dyn FnMut(FileId)) {
910 self.source_db.iter_dependencies_dyn(f)
911 }
912}
913
914pub fn with_main(world: &dyn World, id: FileId) -> WorldWithMain<'_> {
916 WorldWithMain { world, main: id }
917}
918
919pub struct WorldWithMain<'a> {
921 world: &'a dyn World,
922 main: FileId,
923}
924
925impl typst::World for WorldWithMain<'_> {
926 fn main(&self) -> FileId {
927 self.main
928 }
929
930 fn source(&self, id: FileId) -> FileResult<Source> {
931 self.world.source(id)
932 }
933
934 fn library(&self) -> &LazyHash<Library> {
935 self.world.library()
936 }
937
938 fn book(&self) -> &LazyHash<FontBook> {
939 self.world.book()
940 }
941
942 fn file(&self, id: FileId) -> FileResult<Bytes> {
943 self.world.file(id)
944 }
945
946 fn font(&self, index: usize) -> Option<Font> {
947 self.world.font(index)
948 }
949
950 fn today(&self, offset: Option<i64>) -> Option<Datetime> {
951 self.world.today(offset)
952 }
953}
954
955pub trait SourceWorld: World {
957 fn as_world(&self) -> &dyn World;
959
960 fn path_for_id(&self, id: FileId) -> Result<PathResolution, FileError>;
962
963 fn lookup(&self, id: FileId) -> Source {
965 self.source(id)
966 .expect("file id does not point to any source file")
967 }
968
969 fn source_range(&self, span: Span) -> Option<std::ops::Range<usize>> {
971 self.range(span)
972 }
973}
974
975impl<F: CompilerFeat> SourceWorld for CompilerWorld<F> {
976 fn as_world(&self) -> &dyn World {
977 self
978 }
979
980 fn path_for_id(&self, id: FileId) -> Result<PathResolution, FileError> {
982 self.path_for_id(id)
983 }
984}
985
986pub struct CodeSpanReportWorld<'a> {
988 pub world: &'a dyn SourceWorld,
990}
991
992impl<'a> CodeSpanReportWorld<'a> {
993 pub fn new(world: &'a dyn SourceWorld) -> Self {
995 Self { world }
996 }
997}
998
999impl<'a> codespan_reporting::files::Files<'a> for CodeSpanReportWorld<'a> {
1000 type FileId = FileId;
1003
1004 type Name = String;
1006
1007 type Source = Source;
1009
1010 fn name(&'a self, id: FileId) -> CodespanResult<Self::Name> {
1012 Ok(match self.world.path_for_id(id) {
1013 Ok(path) => path.as_path().display().to_string(),
1014 Err(_) => format!("{id:?}"),
1015 })
1016 }
1017
1018 fn source(&'a self, id: FileId) -> CodespanResult<Self::Source> {
1020 Ok(self.world.lookup(id))
1021 }
1022
1023 fn line_index(&'a self, id: FileId, given: usize) -> CodespanResult<usize> {
1025 let source = self.world.lookup(id);
1026 source
1027 .byte_to_line(given)
1028 .ok_or_else(|| CodespanError::IndexTooLarge {
1029 given,
1030 max: source.len_bytes(),
1031 })
1032 }
1033
1034 fn column_number(&'a self, id: FileId, _: usize, given: usize) -> CodespanResult<usize> {
1036 let source = self.world.lookup(id);
1037 source.byte_to_column(given).ok_or_else(|| {
1038 let max = source.len_bytes();
1039 if given <= max {
1040 CodespanError::InvalidCharBoundary { given }
1041 } else {
1042 CodespanError::IndexTooLarge { given, max }
1043 }
1044 })
1045 }
1046
1047 fn line_range(&'a self, id: FileId, given: usize) -> CodespanResult<std::ops::Range<usize>> {
1049 match self.world.source(id).ok() {
1050 Some(source) => {
1051 source
1052 .line_to_range(given)
1053 .ok_or_else(|| CodespanError::LineTooLarge {
1054 given,
1055 max: source.len_lines(),
1056 })
1057 }
1058 None => Ok(0..0),
1059 }
1060 }
1061}
1062
1063impl<'a, F: CompilerFeat> codespan_reporting::files::Files<'a> for CompilerWorld<F> {
1065 type FileId = FileId;
1068
1069 type Name = String;
1071
1072 type Source = Source;
1074
1075 fn name(&'a self, id: FileId) -> CodespanResult<Self::Name> {
1077 CodeSpanReportWorld::new(self).name(id)
1078 }
1079
1080 fn source(&'a self, id: FileId) -> CodespanResult<Self::Source> {
1082 CodeSpanReportWorld::new(self).source(id)
1083 }
1084
1085 fn line_index(&'a self, id: FileId, given: usize) -> CodespanResult<usize> {
1087 CodeSpanReportWorld::new(self).line_index(id, given)
1088 }
1089
1090 fn column_number(&'a self, id: FileId, _: usize, given: usize) -> CodespanResult<usize> {
1092 CodeSpanReportWorld::new(self).column_number(id, 0, given)
1093 }
1094
1095 fn line_range(&'a self, id: FileId, given: usize) -> CodespanResult<std::ops::Range<usize>> {
1097 CodeSpanReportWorld::new(self).line_range(id, given)
1098 }
1099}
1100
1101#[comemo::memoize]
1102fn create_library(inputs: Arc<LazyHash<Dict>>, features: Features) -> Arc<LazyHash<Library>> {
1103 let lib = typst::Library::builder()
1104 .with_inputs(inputs.deref().deref().clone())
1105 .with_features(features)
1106 .build();
1107
1108 Arc::new(LazyHash::new(lib))
1109}