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