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