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 FsProvider, PathResolution, RevisingVfs, SourceCache, TypstFileId, Vfs, WorkspaceResolver,
12};
13use typst::{
14 diag::{eco_format, At, EcoString, FileError, FileResult, SourceResult},
15 foundations::{Bytes, Datetime, Dict},
16 syntax::{FileId, 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<TypstFileId> {
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_cache();
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_cache();
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.revise().reset_cache();
508 }
509
510 world
511 }
512
513 pub fn take_cache(&mut self) -> SourceCache {
514 self.vfs.take_source_cache()
515 }
516
517 pub fn clone_cache(&mut self) -> SourceCache {
518 self.vfs.clone_source_cache()
519 }
520
521 pub fn take_db(&mut self) -> SourceDb {
522 self.source_db.take_state()
523 }
524
525 pub fn vfs(&self) -> &Vfs<F::AccessModel> {
526 &self.vfs
527 }
528
529 pub fn set_is_compiling(&mut self, is_compiling: bool) {
533 self.source_db.is_compiling = is_compiling;
534 }
535
536 pub fn inputs(&self) -> Arc<LazyHash<Dict>> {
537 self.inputs.clone()
538 }
539
540 pub fn path_for_id(&self, id: FileId) -> Result<PathResolution, FileError> {
542 self.vfs.file_path(id)
543 }
544
545 pub fn id_for_path(&self, path: &Path) -> Option<FileId> {
547 let root = self.entry.workspace_root()?;
548 Some(WorkspaceResolver::workspace_file(
549 Some(&root),
550 VirtualPath::new(path.strip_prefix(&root).ok()?),
551 ))
552 }
553
554 pub fn revision(&self) -> NonZeroUsize {
555 self.revision
556 }
557
558 pub fn evict_vfs(&mut self, threshold: usize) {
559 self.vfs.evict(threshold);
560 }
561
562 pub fn evict_source_cache(&mut self, threshold: usize) {
563 self.vfs
564 .clone_source_cache()
565 .evict(self.vfs.revision(), threshold);
566 }
567
568 pub fn packages(&self) -> &[(PackageSpec, Option<EcoString>)] {
575 self.registry.packages()
576 }
577
578 pub fn paged_task(&self) -> Cow<'_, CompilerWorld<F>> {
579 let force_html = self.features.is_enabled(typst::Feature::Html);
580 let enabled_paged = !self.library.features.is_enabled(typst::Feature::Html) || force_html;
581
582 if enabled_paged {
583 return Cow::Borrowed(self);
584 }
585
586 let mut world = self.clone();
587 world.library = create_library(world.inputs.clone(), self.features.clone());
588
589 Cow::Owned(world)
590 }
591
592 pub fn html_task(&self) -> Cow<'_, CompilerWorld<F>> {
593 let enabled_html = self.library.features.is_enabled(typst::Feature::Html);
594
595 if enabled_html {
596 return Cow::Borrowed(self);
597 }
598
599 let features = typst::Features::from_iter([typst::Feature::Html]);
602
603 let mut world = self.clone();
604 world.library = create_library(world.inputs.clone(), features);
605
606 Cow::Owned(world)
607 }
608}
609
610impl<F: CompilerFeat> ShadowApi for CompilerWorld<F> {
611 #[inline]
612 fn shadow_ids(&self) -> Vec<TypstFileId> {
613 self.vfs.shadow_ids()
614 }
615
616 #[inline]
617 fn shadow_paths(&self) -> Vec<Arc<Path>> {
618 self.vfs.shadow_paths()
619 }
620
621 #[inline]
622 fn reset_shadow(&mut self) {
623 self.vfs.revise().reset_shadow()
624 }
625
626 #[inline]
627 fn map_shadow(&mut self, path: &Path, content: Bytes) -> FileResult<()> {
628 self.vfs.revise().map_shadow(path, Ok(content).into())
629 }
630
631 #[inline]
632 fn unmap_shadow(&mut self, path: &Path) -> FileResult<()> {
633 self.vfs.revise().unmap_shadow(path)
634 }
635
636 #[inline]
637 fn map_shadow_by_id(&mut self, file_id: TypstFileId, content: Bytes) -> FileResult<()> {
638 self.vfs
639 .revise()
640 .map_shadow_by_id(file_id, Ok(content).into())
641 }
642
643 #[inline]
644 fn unmap_shadow_by_id(&mut self, file_id: TypstFileId) -> FileResult<()> {
645 self.vfs.revise().remove_shadow_by_id(file_id);
646 Ok(())
647 }
648}
649
650impl<F: CompilerFeat> FsProvider for CompilerWorld<F> {
651 fn file_path(&self, file_id: TypstFileId) -> FileResult<PathResolution> {
652 self.vfs.file_path(file_id)
653 }
654
655 fn read(&self, file_id: TypstFileId) -> FileResult<Bytes> {
656 self.vfs.read(file_id)
657 }
658
659 fn read_source(&self, file_id: TypstFileId) -> FileResult<Source> {
660 self.vfs.source(file_id)
661 }
662}
663
664impl<F: CompilerFeat> World for CompilerWorld<F> {
665 fn library(&self) -> &LazyHash<Library> {
667 self.library.as_ref()
668 }
669
670 fn main(&self) -> FileId {
672 self.entry.main().unwrap_or_else(|| *DETACHED_ENTRY)
673 }
674
675 fn font(&self, id: usize) -> Option<Font> {
677 self.font_resolver.font(id)
678 }
679
680 fn book(&self) -> &LazyHash<FontBook> {
682 self.font_resolver.font_book()
683 }
684
685 fn source(&self, id: FileId) -> FileResult<Source> {
692 static DETACH_SOURCE: LazyLock<Source> =
693 LazyLock::new(|| Source::new(*DETACHED_ENTRY, String::new()));
694
695 if id == *DETACHED_ENTRY {
696 return Ok(DETACH_SOURCE.clone());
697 }
698
699 self.source_db.source(id, self)
700 }
701
702 fn file(&self, id: FileId) -> FileResult<Bytes> {
704 self.source_db.file(id, self)
705 }
706
707 #[cfg(any(feature = "web", feature = "system"))]
715 fn today(&self, offset: Option<i64>) -> Option<Datetime> {
716 use chrono::{Datelike, Duration};
717 let now = self.now.get_or_init(|| tinymist_std::time::now().into());
719
720 let naive = match offset {
721 None => now.naive_local(),
722 Some(o) => now.naive_utc() + Duration::try_hours(o)?,
723 };
724
725 Datetime::from_ymd(
726 naive.year(),
727 naive.month().try_into().ok()?,
728 naive.day().try_into().ok()?,
729 )
730 }
731
732 #[cfg(not(any(feature = "web", feature = "system")))]
740 fn today(&self, offset: Option<i64>) -> Option<Datetime> {
741 use tinymist_std::time::{now, to_typst_time, Duration};
742 let now = self.now.get_or_init(|| now().into());
744
745 let now = offset
746 .and_then(|offset| {
747 let dur = Duration::from_secs(offset.checked_mul(3600)? as u64)
748 .try_into()
749 .ok()?;
750 now.checked_add(dur)
751 })
752 .unwrap_or(*now);
753
754 Some(to_typst_time(now))
755 }
756}
757
758impl<F: CompilerFeat> EntryReader for CompilerWorld<F> {
759 fn entry_state(&self) -> EntryState {
760 self.entry.clone()
761 }
762}
763
764impl<F: CompilerFeat> WorldDeps for CompilerWorld<F> {
765 #[inline]
766 fn iter_dependencies(&self, f: &mut dyn FnMut(TypstFileId)) {
767 self.source_db.iter_dependencies_dyn(f)
768 }
769}
770
771pub fn with_main(world: &dyn World, id: FileId) -> WorldWithMain<'_> {
773 WorldWithMain { world, main: id }
774}
775
776pub struct WorldWithMain<'a> {
777 world: &'a dyn World,
778 main: FileId,
779}
780
781impl typst::World for WorldWithMain<'_> {
782 fn main(&self) -> FileId {
783 self.main
784 }
785
786 fn source(&self, id: FileId) -> FileResult<Source> {
787 self.world.source(id)
788 }
789
790 fn library(&self) -> &LazyHash<Library> {
791 self.world.library()
792 }
793
794 fn book(&self) -> &LazyHash<FontBook> {
795 self.world.book()
796 }
797
798 fn file(&self, id: FileId) -> FileResult<Bytes> {
799 self.world.file(id)
800 }
801
802 fn font(&self, index: usize) -> Option<Font> {
803 self.world.font(index)
804 }
805
806 fn today(&self, offset: Option<i64>) -> Option<Datetime> {
807 self.world.today(offset)
808 }
809}
810
811pub trait SourceWorld: World {
812 fn as_world(&self) -> &dyn World;
813
814 fn path_for_id(&self, id: FileId) -> Result<PathResolution, FileError>;
815 fn lookup(&self, id: FileId) -> Source {
816 self.source(id)
817 .expect("file id does not point to any source file")
818 }
819}
820
821impl<F: CompilerFeat> SourceWorld for CompilerWorld<F> {
822 fn as_world(&self) -> &dyn World {
823 self
824 }
825
826 fn path_for_id(&self, id: FileId) -> Result<PathResolution, FileError> {
828 self.path_for_id(id)
829 }
830}
831
832pub struct CodeSpanReportWorld<'a> {
833 pub world: &'a dyn SourceWorld,
834}
835
836impl<'a> CodeSpanReportWorld<'a> {
837 pub fn new(world: &'a dyn SourceWorld) -> Self {
838 Self { world }
839 }
840}
841
842impl<'a> codespan_reporting::files::Files<'a> for CodeSpanReportWorld<'a> {
843 type FileId = FileId;
846
847 type Name = String;
849
850 type Source = Source;
852
853 fn name(&'a self, id: FileId) -> CodespanResult<Self::Name> {
855 Ok(match self.world.path_for_id(id) {
856 Ok(path) => path.as_path().display().to_string(),
857 Err(_) => format!("{id:?}"),
858 })
859 }
860
861 fn source(&'a self, id: FileId) -> CodespanResult<Self::Source> {
863 Ok(self.world.lookup(id))
864 }
865
866 fn line_index(&'a self, id: FileId, given: usize) -> CodespanResult<usize> {
868 let source = self.world.lookup(id);
869 source
870 .byte_to_line(given)
871 .ok_or_else(|| CodespanError::IndexTooLarge {
872 given,
873 max: source.len_bytes(),
874 })
875 }
876
877 fn column_number(&'a self, id: FileId, _: usize, given: usize) -> CodespanResult<usize> {
879 let source = self.world.lookup(id);
880 source.byte_to_column(given).ok_or_else(|| {
881 let max = source.len_bytes();
882 if given <= max {
883 CodespanError::InvalidCharBoundary { given }
884 } else {
885 CodespanError::IndexTooLarge { given, max }
886 }
887 })
888 }
889
890 fn line_range(&'a self, id: FileId, given: usize) -> CodespanResult<std::ops::Range<usize>> {
892 match self.world.source(id).ok() {
893 Some(source) => {
894 source
895 .line_to_range(given)
896 .ok_or_else(|| CodespanError::LineTooLarge {
897 given,
898 max: source.len_lines(),
899 })
900 }
901 None => Ok(0..0),
902 }
903 }
904}
905
906impl<'a, F: CompilerFeat> codespan_reporting::files::Files<'a> for CompilerWorld<F> {
908 type FileId = FileId;
911
912 type Name = String;
914
915 type Source = Source;
917
918 fn name(&'a self, id: FileId) -> CodespanResult<Self::Name> {
920 CodeSpanReportWorld::new(self).name(id)
921 }
922
923 fn source(&'a self, id: FileId) -> CodespanResult<Self::Source> {
925 CodeSpanReportWorld::new(self).source(id)
926 }
927
928 fn line_index(&'a self, id: FileId, given: usize) -> CodespanResult<usize> {
930 CodeSpanReportWorld::new(self).line_index(id, given)
931 }
932
933 fn column_number(&'a self, id: FileId, _: usize, given: usize) -> CodespanResult<usize> {
935 CodeSpanReportWorld::new(self).column_number(id, 0, given)
936 }
937
938 fn line_range(&'a self, id: FileId, given: usize) -> CodespanResult<std::ops::Range<usize>> {
940 CodeSpanReportWorld::new(self).line_range(id, given)
941 }
942}
943
944#[comemo::memoize]
945fn create_library(inputs: Arc<LazyHash<Dict>>, features: Features) -> Arc<LazyHash<Library>> {
946 let lib = typst::Library::builder()
947 .with_inputs(inputs.deref().deref().clone())
948 .with_features(features)
949 .build();
950
951 Arc::new(LazyHash::new(lib))
952}