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