1use std::{
2 borrow::Cow,
3 num::NonZeroUsize,
4 ops::Deref,
5 path::{Path, PathBuf},
6 sync::{Arc, LazyLock, OnceLock},
7};
8
9use ecow::EcoVec;
10use tinymist_std::{error::prelude::*, ImmutPath};
11use tinymist_vfs::{
12 FileId, FsProvider, PathResolution, RevisingVfs, SourceCache, Vfs, WorkspaceResolver,
13};
14use typst::{
15 diag::{eco_format, At, EcoString, FileError, FileResult, SourceResult},
16 foundations::{Bytes, Datetime, Dict},
17 syntax::{Source, Span, VirtualPath},
18 text::{Font, FontBook},
19 utils::LazyHash,
20 Features, Library, World, WorldExt,
21};
22
23use crate::{
24 package::{PackageRegistry, PackageSpec},
25 source::SourceDb,
26 CompileSnapshot, MEMORY_MAIN_ENTRY,
27};
28use crate::{
29 parser::{
30 get_semantic_tokens_full, get_semantic_tokens_legend, OffsetEncoding, SemanticToken,
31 SemanticTokensLegend,
32 },
33 WorldComputeGraph,
34};
35use crate::entry::{EntryManager, EntryReader, EntryState, DETACHED_ENTRY};
37use crate::{font::FontResolver, CompilerFeat, ShadowApi, WorldDeps};
38
39type CodespanResult<T> = Result<T, CodespanError>;
40type CodespanError = codespan_reporting::files::Error;
41
42#[derive(Debug)]
49pub struct CompilerUniverse<F: CompilerFeat> {
50 entry: EntryState,
53 inputs: Arc<LazyHash<Dict>>,
55 pub features: Features,
57
58 pub font_resolver: Arc<F::FontResolver>,
60 pub registry: Arc<F::Registry>,
62 vfs: Vfs<F::AccessModel>,
64
65 pub revision: NonZeroUsize,
67 pub creation_timestamp: Option<i64>,
69}
70
71impl<F: CompilerFeat> CompilerUniverse<F> {
73 pub fn new_raw(
80 entry: EntryState,
81 features: Features,
82 inputs: Option<Arc<LazyHash<Dict>>>,
83 vfs: Vfs<F::AccessModel>,
84 package_registry: Arc<F::Registry>,
85 font_resolver: Arc<F::FontResolver>,
86 creation_timestamp: Option<i64>,
87 ) -> Self {
88 Self {
89 entry,
90 inputs: inputs.unwrap_or_default(),
91 features,
92
93 revision: NonZeroUsize::new(1).expect("initial revision is 1"),
94
95 font_resolver,
96 registry: package_registry,
97 vfs,
98 creation_timestamp,
99 }
100 }
101
102 pub fn with_entry_file(mut self, entry_file: PathBuf) -> Self {
104 let _ = self.increment_revision(|this| this.set_entry_file_(entry_file.as_path().into()));
105 self
106 }
107
108 pub fn entry_file(&self) -> Option<PathResolution> {
109 self.path_for_id(self.main_id()?).ok()
110 }
111
112 pub fn inputs(&self) -> Arc<LazyHash<Dict>> {
113 self.inputs.clone()
114 }
115
116 pub fn snapshot(&self) -> CompilerWorld<F> {
117 self.snapshot_with(None)
118 }
119
120 pub fn computation(&self) -> Arc<WorldComputeGraph<F>> {
122 let world = self.snapshot();
123 let snap = CompileSnapshot::from_world(world);
124 WorldComputeGraph::new(snap)
125 }
126
127 pub fn computation_with(&self, mutant: TaskInputs) -> Arc<WorldComputeGraph<F>> {
128 let world = self.snapshot_with(Some(mutant));
129 let snap = CompileSnapshot::from_world(world);
130 WorldComputeGraph::new(snap)
131 }
132
133 pub fn snapshot_with_entry_content(
134 &self,
135 content: Bytes,
136 inputs: Option<TaskInputs>,
137 ) -> Arc<WorldComputeGraph<F>> {
138 let mut world = if self.main_id().is_some() {
141 self.snapshot_with(inputs)
142 } else {
143 let world = self.snapshot_with(Some(TaskInputs {
144 entry: Some(
145 self.entry_state()
146 .select_in_workspace(MEMORY_MAIN_ENTRY.vpath().as_rooted_path()),
147 ),
148 inputs: inputs.and_then(|i| i.inputs),
149 }));
150
151 world
152 };
153
154 world.map_shadow_by_id(world.main(), content).unwrap();
155
156 let snap = CompileSnapshot::from_world(world);
157 WorldComputeGraph::new(snap)
158 }
159
160 pub fn snapshot_with(&self, mutant: Option<TaskInputs>) -> CompilerWorld<F> {
161 let w = CompilerWorld {
162 entry: self.entry.clone(),
163 features: self.features.clone(),
164 inputs: self.inputs.clone(),
165 library: create_library(self.inputs.clone(), self.features.clone()),
166 font_resolver: self.font_resolver.clone(),
167 registry: self.registry.clone(),
168 vfs: self.vfs.snapshot(),
169 revision: self.revision,
170 source_db: SourceDb {
171 is_compiling: true,
172 slots: Default::default(),
173 },
174 now: OnceLock::new(),
175 creation_timestamp: self.creation_timestamp,
176 };
177
178 mutant.map(|m| w.task(m)).unwrap_or(w)
179 }
180
181 pub fn increment_revision<T>(&mut self, f: impl FnOnce(&mut RevisingUniverse<F>) -> T) -> T {
183 f(&mut RevisingUniverse {
184 vfs_revision: self.vfs.revision(),
185 creation_timestamp_changed: false,
186 font_changed: false,
187 font_revision: self.font_resolver.revision(),
188 registry_changed: false,
189 registry_revision: self.registry.revision(),
190 view_changed: false,
191 inner: self,
192 })
193 }
194
195 fn mutate_entry_(&mut self, mut state: EntryState) -> SourceResult<EntryState> {
197 std::mem::swap(&mut self.entry, &mut state);
198 Ok(state)
199 }
200
201 fn set_entry_file_(&mut self, entry_file: Arc<Path>) -> SourceResult<()> {
203 let state = self.entry_state();
204 let state = state
205 .try_select_path_in_workspace(&entry_file)
206 .map_err(|e| eco_format!("cannot select entry file out of workspace: {e}"))
207 .at(Span::detached())?
208 .ok_or_else(|| eco_format!("failed to determine root"))
209 .at(Span::detached())?;
210
211 self.mutate_entry_(state).map(|_| ())?;
212 Ok(())
213 }
214
215 pub fn vfs(&self) -> &Vfs<F::AccessModel> {
216 &self.vfs
217 }
218}
219
220impl<F: CompilerFeat> CompilerUniverse<F> {
221 pub fn reset(&mut self) {
223 self.vfs.reset_all();
224 }
226
227 pub fn evict(&mut self, vfs_threshold: usize) {
229 self.vfs.reset_access_model();
230 self.vfs.evict(vfs_threshold);
231 }
232
233 pub fn path_for_id(&self, id: FileId) -> Result<PathResolution, FileError> {
235 self.vfs.file_path(id)
236 }
237
238 pub fn id_for_path(&self, path: &Path) -> Option<FileId> {
240 let root = self.entry.workspace_root()?;
241 Some(WorkspaceResolver::workspace_file(
242 Some(&root),
243 VirtualPath::new(path.strip_prefix(&root).ok()?),
244 ))
245 }
246
247 pub fn get_semantic_token_legend(&self) -> Arc<SemanticTokensLegend> {
248 Arc::new(get_semantic_tokens_legend())
249 }
250
251 pub fn get_semantic_tokens(
252 &self,
253 file_path: Option<String>,
254 encoding: OffsetEncoding,
255 ) -> Result<Arc<Vec<SemanticToken>>> {
256 let world = match file_path {
257 Some(e) => {
258 let path = Path::new(&e);
259 let s = self
260 .entry_state()
261 .try_select_path_in_workspace(path)?
262 .ok_or_else(|| error_once!("cannot select file", path: e))?;
263
264 self.snapshot_with(Some(TaskInputs {
265 entry: Some(s),
266 inputs: None,
267 }))
268 }
269 None => self.snapshot(),
270 };
271
272 let src = world
273 .source(world.main())
274 .map_err(|e| error_once!("cannot access source file", err: e))?;
275 Ok(Arc::new(get_semantic_tokens_full(&src, encoding)))
276 }
277}
278
279impl<F: CompilerFeat> ShadowApi for CompilerUniverse<F> {
280 #[inline]
281 fn reset_shadow(&mut self) {
282 self.increment_revision(|this| this.vfs.revise().reset_shadow())
283 }
284
285 fn shadow_paths(&self) -> Vec<Arc<Path>> {
286 self.vfs.shadow_paths()
287 }
288
289 fn shadow_ids(&self) -> Vec<FileId> {
290 self.vfs.shadow_ids()
291 }
292
293 #[inline]
294 fn map_shadow(&mut self, path: &Path, content: Bytes) -> FileResult<()> {
295 self.increment_revision(|this| this.vfs().map_shadow(path, Ok(content).into()))
296 }
297
298 #[inline]
299 fn unmap_shadow(&mut self, path: &Path) -> FileResult<()> {
300 self.increment_revision(|this| this.vfs().unmap_shadow(path))
301 }
302
303 #[inline]
304 fn map_shadow_by_id(&mut self, file_id: FileId, content: Bytes) -> FileResult<()> {
305 self.increment_revision(|this| this.vfs().map_shadow_by_id(file_id, Ok(content).into()))
306 }
307
308 #[inline]
309 fn unmap_shadow_by_id(&mut self, file_id: FileId) -> FileResult<()> {
310 self.increment_revision(|this| {
311 this.vfs().remove_shadow_by_id(file_id);
312 Ok(())
313 })
314 }
315}
316
317impl<F: CompilerFeat> EntryReader for CompilerUniverse<F> {
318 fn entry_state(&self) -> EntryState {
319 self.entry.clone()
320 }
321}
322
323impl<F: CompilerFeat> EntryManager for CompilerUniverse<F> {
324 fn mutate_entry(&mut self, state: EntryState) -> SourceResult<EntryState> {
325 self.increment_revision(|this| this.mutate_entry_(state))
326 }
327}
328
329pub struct RevisingUniverse<'a, F: CompilerFeat> {
330 view_changed: bool,
331 vfs_revision: NonZeroUsize,
332 font_changed: bool,
333 creation_timestamp_changed: bool,
334 font_revision: Option<NonZeroUsize>,
335 registry_changed: bool,
336 registry_revision: Option<NonZeroUsize>,
337 pub inner: &'a mut CompilerUniverse<F>,
338}
339
340impl<F: CompilerFeat> std::ops::Deref for RevisingUniverse<'_, F> {
341 type Target = CompilerUniverse<F>;
342
343 fn deref(&self) -> &Self::Target {
344 self.inner
345 }
346}
347
348impl<F: CompilerFeat> std::ops::DerefMut for RevisingUniverse<'_, F> {
349 fn deref_mut(&mut self) -> &mut Self::Target {
350 self.inner
351 }
352}
353
354impl<F: CompilerFeat> Drop for RevisingUniverse<'_, F> {
355 fn drop(&mut self) {
356 let mut view_changed = self.view_changed;
357 if self.font_changed() {
360 view_changed = true;
361 }
362 if self.registry_changed() {
365 view_changed = true;
366
367 log::info!("resetting shadow registry_changed");
369 self.vfs.reset_read();
370 }
371 let view_changed = view_changed || self.vfs_changed();
372
373 if view_changed {
374 self.vfs.reset_access_model();
375 let revision = &mut self.revision;
376 *revision = revision.checked_add(1).unwrap();
377 }
378 }
379}
380
381impl<F: CompilerFeat> RevisingUniverse<'_, F> {
382 pub fn vfs(&mut self) -> RevisingVfs<'_, F::AccessModel> {
383 self.vfs.revise()
384 }
385
386 pub fn set_fonts(&mut self, fonts: Arc<F::FontResolver>) {
387 self.font_changed = true;
388 self.inner.font_resolver = fonts;
389 }
390
391 pub fn set_package(&mut self, packages: Arc<F::Registry>) {
392 self.registry_changed = true;
393 self.inner.registry = packages;
394 }
395
396 pub fn set_inputs(&mut self, inputs: Arc<LazyHash<Dict>>) {
398 self.view_changed = true;
399 self.inner.inputs = inputs;
400 }
401
402 pub fn set_creation_timestamp(&mut self, creation_timestamp: Option<i64>) {
404 self.creation_timestamp_changed = creation_timestamp != self.inner.creation_timestamp;
405 self.inner.creation_timestamp = creation_timestamp;
406 }
407
408 pub fn set_entry_file(&mut self, entry_file: Arc<Path>) -> SourceResult<()> {
409 self.view_changed = true;
410 self.inner.set_entry_file_(entry_file)
411 }
412
413 pub fn mutate_entry(&mut self, state: EntryState) -> SourceResult<EntryState> {
414 self.view_changed = true;
415
416 let root_changed = self.inner.entry.workspace_root() != state.workspace_root();
418 if root_changed {
419 log::info!("resetting shadow root_changed");
420 self.vfs.reset_read();
421 }
422
423 self.inner.mutate_entry_(state)
424 }
425
426 pub fn flush(&mut self) {
427 self.view_changed = true;
428 }
429
430 pub fn font_changed(&self) -> bool {
431 self.font_changed && is_revision_changed(self.font_revision, self.font_resolver.revision())
432 }
433
434 pub fn creation_timestamp_changed(&self) -> bool {
435 self.creation_timestamp_changed
436 }
437
438 pub fn registry_changed(&self) -> bool {
439 self.registry_changed
440 && is_revision_changed(self.registry_revision, self.registry.revision())
441 }
442
443 pub fn vfs_changed(&self) -> bool {
444 self.vfs_revision != self.vfs.revision()
445 }
446}
447
448fn is_revision_changed(a: Option<NonZeroUsize>, b: Option<NonZeroUsize>) -> bool {
449 a.is_none() || b.is_none() || a != b
450}
451
452#[cfg(any(feature = "web", feature = "system"))]
453type NowStorage = chrono::DateTime<chrono::Local>;
454#[cfg(not(any(feature = "web", feature = "system")))]
455type NowStorage = tinymist_std::time::UtcDateTime;
456
457pub struct CompilerWorld<F: CompilerFeat> {
458 entry: EntryState,
461 inputs: Arc<LazyHash<Dict>>,
463 features: Features,
465
466 pub library: Arc<LazyHash<Library>>,
468 pub font_resolver: Arc<F::FontResolver>,
470 pub registry: Arc<F::Registry>,
472 vfs: Vfs<F::AccessModel>,
474
475 revision: NonZeroUsize,
476 source_db: SourceDb,
478 now: OnceLock<NowStorage>,
481 creation_timestamp: Option<i64>,
483}
484
485impl<F: CompilerFeat> Clone for CompilerWorld<F> {
486 fn clone(&self) -> Self {
487 self.task(TaskInputs::default())
488 }
489}
490
491#[derive(Debug, Default)]
492pub struct TaskInputs {
493 pub entry: Option<EntryState>,
494 pub inputs: Option<Arc<LazyHash<Dict>>>,
495}
496
497impl<F: CompilerFeat> CompilerWorld<F> {
498 pub fn task(&self, mutant: TaskInputs) -> CompilerWorld<F> {
499 let _ = self.today(None);
501
502 let library = mutant
503 .inputs
504 .clone()
505 .map(|inputs| create_library(inputs, self.features.clone()));
506
507 let root_changed = if let Some(e) = mutant.entry.as_ref() {
508 self.entry.workspace_root() != e.workspace_root()
509 } else {
510 false
511 };
512
513 let mut world = CompilerWorld {
514 features: self.features.clone(),
515 inputs: mutant.inputs.unwrap_or_else(|| self.inputs.clone()),
516 library: library.unwrap_or_else(|| self.library.clone()),
517 entry: mutant.entry.unwrap_or_else(|| self.entry.clone()),
518 font_resolver: self.font_resolver.clone(),
519 registry: self.registry.clone(),
520 vfs: self.vfs.snapshot(),
521 revision: self.revision,
522 source_db: self.source_db.clone(),
523 now: self.now.clone(),
524 creation_timestamp: self.creation_timestamp,
525 };
526
527 if root_changed {
528 world.vfs.reset_read();
529 }
530
531 world
532 }
533
534 pub fn reset_read(&mut self) {
536 self.vfs.reset_read();
537 }
538
539 pub fn take_source_cache(&mut self) -> SourceCache {
541 self.vfs.take_source_cache()
542 }
543
544 pub fn clone_source_cache(&mut self) -> SourceCache {
546 self.vfs.clone_source_cache()
547 }
548
549 pub fn take_db(&mut self) -> SourceDb {
551 self.source_db.take()
552 }
553
554 pub fn vfs(&self) -> &Vfs<F::AccessModel> {
555 &self.vfs
556 }
557
558 pub fn set_is_compiling(&mut self, is_compiling: bool) {
562 self.source_db.is_compiling = is_compiling;
563 }
564
565 pub fn inputs(&self) -> Arc<LazyHash<Dict>> {
566 self.inputs.clone()
567 }
568
569 pub fn revision(&self) -> NonZeroUsize {
570 self.revision
571 }
572
573 pub fn evict_vfs(&mut self, threshold: usize) {
574 self.vfs.evict(threshold);
575 }
576
577 pub fn evict_source_cache(&mut self, threshold: usize) {
578 self.vfs
579 .clone_source_cache()
580 .evict(self.vfs.revision(), threshold);
581 }
582
583 pub fn path_for_id(&self, id: FileId) -> Result<PathResolution, FileError> {
585 self.vfs.file_path(id)
586 }
587
588 pub fn id_for_path(&self, path: &Path) -> Option<FileId> {
590 let root = self.entry.workspace_root()?;
591 Some(WorkspaceResolver::workspace_file(
592 Some(&root),
593 VirtualPath::new(path.strip_prefix(&root).ok()?),
594 ))
595 }
596
597 pub fn file_id_by_path(&self, path: &Path) -> FileResult<FileId> {
598 match self.id_for_path(path) {
600 Some(id) => Ok(id),
601 None => WorkspaceResolver::file_with_parent_root(path).ok_or_else(|| {
602 let reason = eco_format!("invalid path: {path:?}");
603 FileError::Other(Some(reason))
604 }),
605 }
606 }
607
608 pub fn source_by_path(&self, path: &Path) -> FileResult<Source> {
609 self.source(self.file_id_by_path(path)?)
610 }
611
612 pub fn depended_files(&self) -> EcoVec<FileId> {
613 let mut deps = EcoVec::new();
614 self.iter_dependencies(&mut |file_id| {
615 deps.push(file_id);
616 });
617 deps
618 }
619
620 pub fn depended_fs_paths(&self) -> EcoVec<ImmutPath> {
621 let mut deps = EcoVec::new();
622 self.iter_dependencies(&mut |file_id| {
623 if let Ok(path) = self.path_for_id(file_id) {
624 deps.push(path.as_path().into());
625 }
626 });
627 deps
628 }
629
630 pub fn packages(&self) -> &[(PackageSpec, Option<EcoString>)] {
637 self.registry.packages()
638 }
639
640 pub fn paged_task(&self) -> Cow<'_, CompilerWorld<F>> {
641 let force_html = self.features.is_enabled(typst::Feature::Html);
642 let enabled_paged = !self.library.features.is_enabled(typst::Feature::Html) || force_html;
643
644 if enabled_paged {
645 return Cow::Borrowed(self);
646 }
647
648 let mut world = self.clone();
649 world.library = create_library(world.inputs.clone(), self.features.clone());
650
651 Cow::Owned(world)
652 }
653
654 pub fn html_task(&self) -> Cow<'_, CompilerWorld<F>> {
655 let enabled_html = self.library.features.is_enabled(typst::Feature::Html);
656
657 if enabled_html {
658 return Cow::Borrowed(self);
659 }
660
661 let features = typst::Features::from_iter([typst::Feature::Html]);
664
665 let mut world = self.clone();
666 world.library = create_library(world.inputs.clone(), features);
667
668 Cow::Owned(world)
669 }
670}
671
672impl<F: CompilerFeat> ShadowApi for CompilerWorld<F> {
673 #[inline]
674 fn shadow_ids(&self) -> Vec<FileId> {
675 self.vfs.shadow_ids()
676 }
677
678 #[inline]
679 fn shadow_paths(&self) -> Vec<Arc<Path>> {
680 self.vfs.shadow_paths()
681 }
682
683 #[inline]
684 fn reset_shadow(&mut self) {
685 self.vfs.revise().reset_shadow()
686 }
687
688 #[inline]
689 fn map_shadow(&mut self, path: &Path, content: Bytes) -> FileResult<()> {
690 self.vfs.revise().map_shadow(path, Ok(content).into())
691 }
692
693 #[inline]
694 fn unmap_shadow(&mut self, path: &Path) -> FileResult<()> {
695 self.vfs.revise().unmap_shadow(path)
696 }
697
698 #[inline]
699 fn map_shadow_by_id(&mut self, file_id: FileId, content: Bytes) -> FileResult<()> {
700 self.vfs
701 .revise()
702 .map_shadow_by_id(file_id, Ok(content).into())
703 }
704
705 #[inline]
706 fn unmap_shadow_by_id(&mut self, file_id: FileId) -> FileResult<()> {
707 self.vfs.revise().remove_shadow_by_id(file_id);
708 Ok(())
709 }
710}
711
712impl<F: CompilerFeat> FsProvider for CompilerWorld<F> {
713 fn file_path(&self, file_id: FileId) -> FileResult<PathResolution> {
714 self.vfs.file_path(file_id)
715 }
716
717 fn read(&self, file_id: FileId) -> FileResult<Bytes> {
718 self.vfs.read(file_id)
719 }
720
721 fn read_source(&self, file_id: FileId) -> FileResult<Source> {
722 self.vfs.source(file_id)
723 }
724}
725
726impl<F: CompilerFeat> World for CompilerWorld<F> {
727 fn library(&self) -> &LazyHash<Library> {
729 self.library.as_ref()
730 }
731
732 fn main(&self) -> FileId {
734 self.entry.main().unwrap_or_else(|| *DETACHED_ENTRY)
735 }
736
737 fn font(&self, id: usize) -> Option<Font> {
739 self.font_resolver.font(id)
740 }
741
742 fn book(&self) -> &LazyHash<FontBook> {
744 self.font_resolver.font_book()
745 }
746
747 fn source(&self, id: FileId) -> FileResult<Source> {
754 static DETACH_SOURCE: LazyLock<Source> =
755 LazyLock::new(|| Source::new(*DETACHED_ENTRY, String::new()));
756
757 if id == *DETACHED_ENTRY {
758 return Ok(DETACH_SOURCE.clone());
759 }
760
761 self.source_db.source(id, self)
762 }
763
764 fn file(&self, id: FileId) -> FileResult<Bytes> {
766 self.source_db.file(id, self)
767 }
768
769 #[cfg(any(feature = "web", feature = "system"))]
777 fn today(&self, offset: Option<i64>) -> Option<Datetime> {
778 use chrono::{Datelike, Duration};
779
780 let now = self.now.get_or_init(|| {
781 if let Some(timestamp) = self.creation_timestamp {
782 chrono::DateTime::from_timestamp(timestamp, 0)
783 .unwrap_or_else(|| tinymist_std::time::now().into())
784 .into()
785 } else {
786 tinymist_std::time::now().into()
787 }
788 });
789
790 let naive = match offset {
791 None => now.naive_local(),
792 Some(o) => now.naive_utc() + Duration::try_hours(o)?,
793 };
794
795 Datetime::from_ymd(
796 naive.year(),
797 naive.month().try_into().ok()?,
798 naive.day().try_into().ok()?,
799 )
800 }
801
802 #[cfg(not(any(feature = "web", feature = "system")))]
810 fn today(&self, offset: Option<i64>) -> Option<Datetime> {
811 use tinymist_std::time::{now, to_typst_time, Duration};
812
813 let now = self.now.get_or_init(|| {
814 if let Some(timestamp) = self.creation_timestamp {
815 tinymist_std::time::UtcDateTime::from_unix_timestamp(timestamp)
816 .unwrap_or_else(|_| now().into())
817 .into()
818 } else {
819 now().into()
820 }
821 });
822
823 let now = offset
824 .and_then(|offset| {
825 let dur = Duration::from_secs(offset.checked_mul(3600)? as u64)
826 .try_into()
827 .ok()?;
828 now.checked_add(dur)
829 })
830 .unwrap_or(*now);
831
832 Some(to_typst_time(now))
833 }
834}
835
836impl<F: CompilerFeat> EntryReader for CompilerWorld<F> {
837 fn entry_state(&self) -> EntryState {
838 self.entry.clone()
839 }
840}
841
842impl<F: CompilerFeat> WorldDeps for CompilerWorld<F> {
843 #[inline]
844 fn iter_dependencies(&self, f: &mut dyn FnMut(FileId)) {
845 self.source_db.iter_dependencies_dyn(f)
846 }
847}
848
849pub fn with_main(world: &dyn World, id: FileId) -> WorldWithMain<'_> {
851 WorldWithMain { world, main: id }
852}
853
854pub struct WorldWithMain<'a> {
855 world: &'a dyn World,
856 main: FileId,
857}
858
859impl typst::World for WorldWithMain<'_> {
860 fn main(&self) -> FileId {
861 self.main
862 }
863
864 fn source(&self, id: FileId) -> FileResult<Source> {
865 self.world.source(id)
866 }
867
868 fn library(&self) -> &LazyHash<Library> {
869 self.world.library()
870 }
871
872 fn book(&self) -> &LazyHash<FontBook> {
873 self.world.book()
874 }
875
876 fn file(&self, id: FileId) -> FileResult<Bytes> {
877 self.world.file(id)
878 }
879
880 fn font(&self, index: usize) -> Option<Font> {
881 self.world.font(index)
882 }
883
884 fn today(&self, offset: Option<i64>) -> Option<Datetime> {
885 self.world.today(offset)
886 }
887}
888
889pub trait SourceWorld: World {
890 fn as_world(&self) -> &dyn World;
891
892 fn path_for_id(&self, id: FileId) -> Result<PathResolution, FileError>;
893 fn lookup(&self, id: FileId) -> Source {
894 self.source(id)
895 .expect("file id does not point to any source file")
896 }
897
898 fn source_range(&self, span: Span) -> Option<std::ops::Range<usize>> {
899 self.range(span)
900 }
901}
902
903impl<F: CompilerFeat> SourceWorld for CompilerWorld<F> {
904 fn as_world(&self) -> &dyn World {
905 self
906 }
907
908 fn path_for_id(&self, id: FileId) -> Result<PathResolution, FileError> {
910 self.path_for_id(id)
911 }
912}
913
914pub struct CodeSpanReportWorld<'a> {
915 pub world: &'a dyn SourceWorld,
916}
917
918impl<'a> CodeSpanReportWorld<'a> {
919 pub fn new(world: &'a dyn SourceWorld) -> Self {
920 Self { world }
921 }
922}
923
924impl<'a> codespan_reporting::files::Files<'a> for CodeSpanReportWorld<'a> {
925 type FileId = FileId;
928
929 type Name = String;
931
932 type Source = Source;
934
935 fn name(&'a self, id: FileId) -> CodespanResult<Self::Name> {
937 Ok(match self.world.path_for_id(id) {
938 Ok(path) => path.as_path().display().to_string(),
939 Err(_) => format!("{id:?}"),
940 })
941 }
942
943 fn source(&'a self, id: FileId) -> CodespanResult<Self::Source> {
945 Ok(self.world.lookup(id))
946 }
947
948 fn line_index(&'a self, id: FileId, given: usize) -> CodespanResult<usize> {
950 let source = self.world.lookup(id);
951 source
952 .byte_to_line(given)
953 .ok_or_else(|| CodespanError::IndexTooLarge {
954 given,
955 max: source.len_bytes(),
956 })
957 }
958
959 fn column_number(&'a self, id: FileId, _: usize, given: usize) -> CodespanResult<usize> {
961 let source = self.world.lookup(id);
962 source.byte_to_column(given).ok_or_else(|| {
963 let max = source.len_bytes();
964 if given <= max {
965 CodespanError::InvalidCharBoundary { given }
966 } else {
967 CodespanError::IndexTooLarge { given, max }
968 }
969 })
970 }
971
972 fn line_range(&'a self, id: FileId, given: usize) -> CodespanResult<std::ops::Range<usize>> {
974 match self.world.source(id).ok() {
975 Some(source) => {
976 source
977 .line_to_range(given)
978 .ok_or_else(|| CodespanError::LineTooLarge {
979 given,
980 max: source.len_lines(),
981 })
982 }
983 None => Ok(0..0),
984 }
985 }
986}
987
988impl<'a, F: CompilerFeat> codespan_reporting::files::Files<'a> for CompilerWorld<F> {
990 type FileId = FileId;
993
994 type Name = String;
996
997 type Source = Source;
999
1000 fn name(&'a self, id: FileId) -> CodespanResult<Self::Name> {
1002 CodeSpanReportWorld::new(self).name(id)
1003 }
1004
1005 fn source(&'a self, id: FileId) -> CodespanResult<Self::Source> {
1007 CodeSpanReportWorld::new(self).source(id)
1008 }
1009
1010 fn line_index(&'a self, id: FileId, given: usize) -> CodespanResult<usize> {
1012 CodeSpanReportWorld::new(self).line_index(id, given)
1013 }
1014
1015 fn column_number(&'a self, id: FileId, _: usize, given: usize) -> CodespanResult<usize> {
1017 CodeSpanReportWorld::new(self).column_number(id, 0, given)
1018 }
1019
1020 fn line_range(&'a self, id: FileId, given: usize) -> CodespanResult<std::ops::Range<usize>> {
1022 CodeSpanReportWorld::new(self).line_range(id, given)
1023 }
1024}
1025
1026#[comemo::memoize]
1027fn create_library(inputs: Arc<LazyHash<Dict>>, features: Features) -> Arc<LazyHash<Library>> {
1028 let lib = typst::Library::builder()
1029 .with_inputs(inputs.deref().deref().clone())
1030 .with_features(features)
1031 .build();
1032
1033 Arc::new(LazyHash::new(lib))
1034}