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