1use std::num::NonZeroUsize;
2use std::ops::DerefMut;
3use std::sync::OnceLock;
4use std::sync::atomic::{AtomicU64, Ordering};
5use std::{collections::HashSet, ops::Deref};
6
7use comemo::{Track, Tracked};
8use lsp_types::Url;
9use parking_lot::Mutex;
10use rustc_hash::FxHashMap;
11use tinymist_analysis::docs::DocString;
12use tinymist_analysis::stats::AllocStats;
13use tinymist_analysis::syntax::classify_def_loosely;
14use tinymist_analysis::ty::{BuiltinTy, InsTy, term_value};
15use tinymist_analysis::{analyze_expr_, analyze_import_};
16use tinymist_lint::{KnownIssues, LintInfo};
17use tinymist_project::{LspComputeGraph, LspWorld, TaskWhen};
18use tinymist_std::hash::{FxDashMap, hash128};
19use tinymist_std::typst::TypstDocument;
20use tinymist_world::debug_loc::DataSource;
21use tinymist_world::package::registry::PackageIndexEntry;
22use tinymist_world::vfs::{PathResolution, WorkspaceResolver};
23use tinymist_world::{DETACHED_ENTRY, EntryReader};
24use typst::diag::{At, FileError, FileResult, SourceDiagnostic, SourceResult, StrResult};
25use typst::foundations::{Bytes, IntoValue, Module, StyleChain, Styles};
26use typst::introspection::Introspector;
27use typst::layout::Position;
28use typst::model::BibliographyElem;
29use typst::syntax::package::PackageManifest;
30use typst::syntax::{Span, VirtualPath};
31use typst_shim::eval::{Eval, eval_compat};
32
33use super::{LspQuerySnapshot, TypeEnv};
34use crate::adt::revision::{RevisionLock, RevisionManager, RevisionManagerLike, RevisionSlot};
35use crate::analysis::prelude::*;
36use crate::analysis::{
37 AnalysisStats, BibInfo, CompletionFeat, Definition, PathKind, QueryStatGuard,
38 SemanticTokenCache, SemanticTokenContext, SemanticTokens, Signature, SignatureTarget, Ty,
39 TypeInfo, analyze_signature, bib_info, definition, post_type_check,
40};
41use crate::docs::{DefDocs, TidyModuleDocs};
42use crate::syntax::{
43 Decl, DefKind, ExprInfo, ExprRoute, LexicalScope, ModuleDependency, SyntaxClass,
44 classify_syntax, construct_module_dependencies, is_mark, resolve_id_by_path,
45 scan_workspace_files,
46};
47use crate::upstream::{Tooltip, tooltip_};
48use crate::{
49 ColorTheme, CompilerQueryRequest, LspPosition, LspRange, LspWorldExt, PositionEncoding,
50};
51
52macro_rules! interned_str {
53 ($name:ident, $value:expr) => {
54 static $name: LazyLock<Interned<str>> = LazyLock::new(|| $value.into());
55 };
56}
57
58#[derive(Default, Clone)]
60pub struct Analysis {
61 pub position_encoding: PositionEncoding,
63 pub allow_overlapping_token: bool,
65 pub allow_multiline_token: bool,
67 pub remove_html: bool,
69 pub support_client_codelens: bool,
71 pub extended_code_action: bool,
79 pub completion_feat: CompletionFeat,
81 pub color_theme: ColorTheme,
83 pub lint: TaskWhen,
85 pub periscope: Option<Arc<dyn PeriscopeProvider + Send + Sync>>,
87 pub workers: Arc<AnalysisGlobalWorkers>,
89 pub local_packages: Arc<Mutex<OnceLock<EcoVec<PackageIndexEntry>>>>,
91 pub tokens_caches: Arc<Mutex<SemanticTokenCache>>,
93 pub caches: AnalysisGlobalCaches,
95 pub analysis_rev_cache: Arc<Mutex<AnalysisRevCache>>,
97 pub stats: Arc<AnalysisStats>,
99}
100
101impl Analysis {
102 pub fn enter(&self, g: LspComputeGraph) -> LocalContextGuard {
104 self.enter_(g, self.lock_revision(None))
105 }
106
107 pub(crate) fn enter_(&self, g: LspComputeGraph, mut lg: AnalysisRevLock) -> LocalContextGuard {
109 let lifetime = self.caches.lifetime.fetch_add(1, Ordering::SeqCst);
110 let slot = self
111 .analysis_rev_cache
112 .lock()
113 .find_revision(g.world().revision(), &lg);
114 let tokens = lg.tokens.take();
115 LocalContextGuard {
116 _rev_lock: lg,
117 local: LocalContext {
118 tokens,
119 caches: AnalysisLocalCaches::default(),
120 shared: Arc::new(SharedContext {
121 slot,
122 lifetime,
123 graph: g,
124 analysis: self.clone(),
125 }),
126 },
127 }
128 }
129
130 pub fn query_snapshot(
132 self: Arc<Self>,
133 snap: LspComputeGraph,
134 req: Option<&CompilerQueryRequest>,
135 ) -> LspQuerySnapshot {
136 let rev_lock = self.lock_revision(req);
137 LspQuerySnapshot {
138 snap,
139 analysis: self,
140 rev_lock,
141 }
142 }
143
144 #[must_use]
146 pub fn lock_revision(&self, req: Option<&CompilerQueryRequest>) -> AnalysisRevLock {
147 let mut grid = self.analysis_rev_cache.lock();
148
149 AnalysisRevLock {
150 tokens: match req {
151 Some(CompilerQueryRequest::SemanticTokensFull(req)) => Some(
152 SemanticTokenCache::acquire(self.tokens_caches.clone(), &req.path, None),
153 ),
154 Some(CompilerQueryRequest::SemanticTokensDelta(req)) => {
155 Some(SemanticTokenCache::acquire(
156 self.tokens_caches.clone(),
157 &req.path,
158 Some(&req.previous_result_id),
159 ))
160 }
161 _ => None,
162 },
163 inner: grid.manager.lock_estimated(),
164 grid: self.analysis_rev_cache.clone(),
165 }
166 }
167
168 pub fn clear_cache(&self) {
170 self.caches.signatures.clear();
171 self.caches.docstrings.clear();
172 self.caches.def_signatures.clear();
173 self.caches.static_signatures.clear();
174 self.caches.terms.clear();
175 *self.local_packages.lock() = OnceLock::default();
176 self.tokens_caches.lock().clear();
177 self.analysis_rev_cache.lock().clear();
178 }
179
180 pub fn report_query_stats(&self) -> String {
182 self.stats.report()
183 }
184
185 pub fn report_alloc_stats(&self) -> String {
187 AllocStats::report()
188 }
189
190 pub fn trigger_suggest(&self, context: bool) -> Option<Interned<str>> {
192 interned_str!(INTERNED, "editor.action.triggerSuggest");
193
194 (self.completion_feat.trigger_suggest && context).then(|| INTERNED.clone())
195 }
196
197 pub fn trigger_parameter_hints(&self, context: bool) -> Option<Interned<str>> {
199 interned_str!(INTERNED, "editor.action.triggerParameterHints");
200 (self.completion_feat.trigger_parameter_hints && context).then(|| INTERNED.clone())
201 }
202
203 pub fn trigger_on_snippet(&self, context: bool) -> Option<Interned<str>> {
210 if !self.completion_feat.trigger_on_snippet_placeholders {
211 return None;
212 }
213
214 self.trigger_suggest(context)
215 }
216
217 pub fn trigger_on_snippet_with_param_hint(&self, context: bool) -> Option<Interned<str>> {
219 interned_str!(INTERNED, "tinymist.triggerSuggestAndParameterHints");
220 if !self.completion_feat.trigger_on_snippet_placeholders {
221 return self.trigger_parameter_hints(context);
222 }
223
224 (self.completion_feat.trigger_suggest_and_parameter_hints && context)
225 .then(|| INTERNED.clone())
226 }
227}
228
229pub trait PeriscopeProvider {
231 fn periscope_at(
233 &self,
234 _ctx: &mut LocalContext,
235 _doc: &TypstDocument,
236 _pos: Position,
237 ) -> Option<String> {
238 None
239 }
240}
241
242pub struct LocalContextGuard {
244 pub local: LocalContext,
246 _rev_lock: AnalysisRevLock,
248}
249
250impl Deref for LocalContextGuard {
251 type Target = LocalContext;
252
253 fn deref(&self) -> &Self::Target {
254 &self.local
255 }
256}
257
258impl DerefMut for LocalContextGuard {
259 fn deref_mut(&mut self) -> &mut Self::Target {
260 &mut self.local
261 }
262}
263
264impl Drop for LocalContextGuard {
266 fn drop(&mut self) {
267 self.gc();
268 }
269}
270
271impl LocalContextGuard {
272 fn gc(&self) {
273 let lifetime = self.lifetime;
274 loop {
275 let latest_clear_lifetime = self.analysis.caches.clear_lifetime.load(Ordering::Relaxed);
276 if latest_clear_lifetime >= lifetime {
277 return;
278 }
279
280 if self.analysis.caches.clear_lifetime.compare_exchange(
281 latest_clear_lifetime,
282 lifetime,
283 Ordering::SeqCst,
284 Ordering::SeqCst,
285 ) != Ok(latest_clear_lifetime)
286 {
287 continue;
288 }
289
290 break;
291 }
292
293 let retainer = |l: u64| lifetime.saturating_sub(l) < 60;
294 let caches = &self.analysis.caches;
295 caches.def_signatures.retain(|(l, _)| retainer(*l));
296 caches.static_signatures.retain(|(l, _)| retainer(*l));
297 caches.terms.retain(|(l, _)| retainer(*l));
298 caches.signatures.retain(|(l, _)| retainer(*l));
299 caches.docstrings.retain(|(l, _)| retainer(*l));
300 }
301}
302
303pub struct LocalContext {
306 pub(crate) tokens: Option<SemanticTokenContext>,
308 pub caches: AnalysisLocalCaches,
310 pub shared: Arc<SharedContext>,
312}
313
314impl Deref for LocalContext {
315 type Target = Arc<SharedContext>;
316
317 fn deref(&self) -> &Self::Target {
318 &self.shared
319 }
320}
321
322impl DerefMut for LocalContext {
323 fn deref_mut(&mut self) -> &mut Self::Target {
324 &mut self.shared
325 }
326}
327
328impl LocalContext {
329 #[cfg(test)]
331 pub fn test_package_list(&mut self, f: impl FnOnce() -> Vec<PackageIndexEntry> + Clone) {
332 self.world().registry.test_package_list(f.clone());
333 self.analysis
334 .local_packages
335 .lock()
336 .get_or_init(|| f().into_iter().collect());
337 }
338
339 #[cfg(test)]
341 pub fn test_completion_files(&mut self, f: impl FnOnce() -> Vec<TypstFileId>) {
342 self.caches.completion_files.get_or_init(f);
343 }
344
345 #[cfg(test)]
347 pub fn test_files(&mut self, f: impl FnOnce() -> Vec<TypstFileId>) {
348 self.caches.root_files.get_or_init(f);
349 }
350
351 pub(crate) fn completion_files(&self, pref: &PathKind) -> impl Iterator<Item = &TypstFileId> {
353 let regexes = pref.ext_matcher();
354 self.caches
355 .completion_files
356 .get_or_init(|| {
357 if let Some(root) = self.world().entry_state().workspace_root() {
358 scan_workspace_files(&root, PathKind::Special.ext_matcher(), |path| {
359 WorkspaceResolver::workspace_file(Some(&root), VirtualPath::new(path))
360 })
361 } else {
362 vec![]
363 }
364 })
365 .iter()
366 .filter(move |fid| {
367 fid.vpath()
368 .as_rooted_path()
369 .extension()
370 .and_then(|path| path.to_str())
371 .is_some_and(|path| regexes.is_match(path))
372 })
373 }
374
375 pub fn source_files(&self) -> &Vec<TypstFileId> {
377 self.caches.root_files.get_or_init(|| {
378 self.completion_files(&PathKind::Source {
379 allow_package: false,
380 })
381 .copied()
382 .collect()
383 })
384 }
385
386 pub fn module_dependencies(&mut self) -> &HashMap<TypstFileId, ModuleDependency> {
388 if self.caches.module_deps.get().is_some() {
389 self.caches.module_deps.get().unwrap()
390 } else {
391 let deps = construct_module_dependencies(self);
394 self.caches.module_deps.get_or_init(|| deps)
395 }
396 }
397
398 pub fn depended_source_files(&self) -> EcoVec<TypstFileId> {
400 let mut ids = self.depended_files();
401 let preference = PathKind::Source {
402 allow_package: false,
403 };
404 ids.retain(|id| preference.is_match(id.vpath().as_rooted_path()));
405 ids
406 }
407
408 pub fn depended_files(&self) -> EcoVec<TypstFileId> {
411 self.world().depended_files()
412 }
413
414 pub fn shared(&self) -> &Arc<SharedContext> {
416 &self.shared
417 }
418
419 pub fn shared_(&self) -> Arc<SharedContext> {
421 self.shared.clone()
422 }
423
424 pub fn fork_for_search(&mut self) -> SearchCtx<'_> {
426 SearchCtx {
427 ctx: self,
428 searched: Default::default(),
429 worklist: Default::default(),
430 }
431 }
432
433 pub(crate) fn preload_package(&self, entry_point: TypstFileId) {
434 self.shared_().preload_package(entry_point);
435 }
436
437 pub(crate) fn with_vm<T>(&self, f: impl FnOnce(&mut typst_shim::eval::Vm) -> T) -> T {
438 crate::upstream::with_vm((self.world() as &dyn World).track(), f)
439 }
440
441 pub(crate) fn const_eval(&self, rr: ast::Expr<'_>) -> Option<Value> {
442 SharedContext::const_eval(rr)
443 }
444
445 pub(crate) fn mini_eval(&self, rr: ast::Expr<'_>) -> Option<Value> {
446 self.const_eval(rr)
447 .or_else(|| self.with_vm(|vm| rr.eval(vm).ok()))
448 }
449
450 pub(crate) fn cached_tokens(&mut self, source: &Source) -> (SemanticTokens, Option<String>) {
451 let tokens = crate::analysis::semantic_tokens::get_semantic_tokens(self, source);
452
453 let result_id = self.tokens.as_ref().map(|t| {
454 let id = t.next.revision;
455 t.next
456 .data
457 .set(tokens.clone())
458 .unwrap_or_else(|_| panic!("unexpected slot overwrite {id}"));
459 id.to_string()
460 });
461 (tokens, result_id)
462 }
463
464 pub(crate) fn expr_stage_by_id(&mut self, fid: TypstFileId) -> Option<ExprInfo> {
466 Some(self.expr_stage(&self.source_by_id(fid).ok()?))
467 }
468
469 pub(crate) fn expr_stage(&mut self, source: &Source) -> ExprInfo {
471 let id = source.id();
472 let cache = &self.caches.modules.entry(id).or_default().expr_stage;
473 cache.get_or_init(|| self.shared.expr_stage(source)).clone()
474 }
475
476 pub(crate) fn type_check(&mut self, source: &Source) -> Arc<TypeInfo> {
478 let id = source.id();
479 let cache = &self.caches.modules.entry(id).or_default().type_check;
480 cache.get_or_init(|| self.shared.type_check(source)).clone()
481 }
482
483 pub(crate) fn lint(
484 &mut self,
485 source: &Source,
486 known_issues: &KnownIssues,
487 ) -> EcoVec<SourceDiagnostic> {
488 self.shared.lint(source, known_issues).diagnostics
489 }
490
491 pub(crate) fn type_check_by_id(&mut self, id: TypstFileId) -> Arc<TypeInfo> {
493 let cache = &self.caches.modules.entry(id).or_default().type_check;
494 cache
495 .clone()
496 .get_or_init(|| {
497 let source = self.source_by_id(id).ok();
498 source
499 .map(|s| self.shared.type_check(&s))
500 .unwrap_or_default()
501 })
502 .clone()
503 }
504
505 pub(crate) fn type_of_span(&mut self, s: Span) -> Option<Ty> {
506 let scheme = self.type_check_by_id(s.id()?);
507 let ty = scheme.type_of_span(s)?;
508 Some(scheme.simplify(ty, false))
509 }
510
511 pub(crate) fn def_docs(&mut self, def: &Definition) -> Option<DefDocs> {
512 match def.decl.kind() {
515 DefKind::Function => {
516 let sig = self.sig_of_def(def.clone())?;
517 let docs = crate::docs::sig_docs(&sig)?;
518 Some(DefDocs::Function(Box::new(docs)))
519 }
520 DefKind::Struct | DefKind::Constant | DefKind::Variable => {
521 let docs = crate::docs::var_docs(self, def.decl.span())?;
522 Some(DefDocs::Variable(docs))
523 }
524 DefKind::Module => {
525 let ei = self.expr_stage_by_id(def.decl.file_id()?)?;
526 Some(DefDocs::Module(TidyModuleDocs {
527 docs: ei.module_docstring.docs.clone().unwrap_or_default(),
528 }))
529 }
530 DefKind::Reference => None,
531 }
532 }
533}
534
535pub struct SharedContext {
537 pub lifetime: u64,
539 pub graph: LspComputeGraph,
543 pub analysis: Analysis,
545 slot: Arc<RevisionSlot<AnalysisRevSlot>>,
547}
548
549impl SharedContext {
550 pub fn revision(&self) -> usize {
552 self.slot.revision
553 }
554
555 pub(crate) fn position_encoding(&self) -> PositionEncoding {
557 self.analysis.position_encoding
558 }
559
560 pub fn world(&self) -> &LspWorld {
562 self.graph.world()
563 }
564
565 pub fn success_doc(&self) -> Option<&TypstDocument> {
567 self.graph.snap.success_doc.as_ref()
568 }
569
570 pub fn to_typst_pos(&self, position: LspPosition, src: &Source) -> Option<usize> {
572 crate::to_typst_position(position, self.analysis.position_encoding, src)
573 }
574
575 pub fn to_typst_pos_offset(
577 &self,
578 source: &Source,
579 position: LspPosition,
580 shift: usize,
581 ) -> Option<usize> {
582 let offset = self.to_typst_pos(position, source)?;
583 Some(ceil_char_boundary(source.text(), offset + shift))
584 }
585
586 pub fn to_lsp_pos(&self, typst_offset: usize, src: &Source) -> LspPosition {
588 crate::to_lsp_position(typst_offset, self.analysis.position_encoding, src)
589 }
590
591 pub fn to_typst_range(&self, position: LspRange, src: &Source) -> Option<Range<usize>> {
593 crate::to_typst_range(position, self.analysis.position_encoding, src)
594 }
595
596 pub fn to_lsp_range(&self, position: Range<usize>, src: &Source) -> LspRange {
598 crate::to_lsp_range(position, src, self.analysis.position_encoding)
599 }
600
601 pub fn to_lsp_range_(&self, position: Range<usize>, fid: TypstFileId) -> Option<LspRange> {
603 let ext = fid
604 .vpath()
605 .as_rootless_path()
606 .extension()
607 .and_then(|ext| ext.to_str());
608 if matches!(ext, Some("yaml" | "yml" | "bib")) {
610 let bytes = self.file_by_id(fid).ok()?;
611 let bytes_len = bytes.len();
612 let loc = loc_info(bytes)?;
613 let start = find_loc(bytes_len, &loc, position.start, self.position_encoding())?;
615 let end = find_loc(bytes_len, &loc, position.end, self.position_encoding())?;
616 return Some(LspRange { start, end });
617 }
618
619 let source = self.source_by_id(fid).ok()?;
620
621 Some(self.to_lsp_range(position, &source))
622 }
623
624 pub fn path_for_id(&self, id: TypstFileId) -> Result<PathResolution, FileError> {
626 self.world().path_for_id(id)
627 }
628
629 pub fn uri_for_id(&self, fid: TypstFileId) -> Result<Url, FileError> {
631 self.world().uri_for_id(fid)
632 }
633
634 pub fn file_id_by_path(&self, path: &Path) -> FileResult<TypstFileId> {
636 self.world().file_id_by_path(path)
637 }
638
639 pub fn file_by_id(&self, fid: TypstFileId) -> FileResult<Bytes> {
641 self.world().file(fid)
642 }
643
644 pub fn source_by_id(&self, fid: TypstFileId) -> FileResult<Source> {
646 self.world().source(fid)
647 }
648
649 pub fn source_by_path(&self, path: &Path) -> FileResult<Source> {
651 self.source_by_id(self.file_id_by_path(path)?)
652 }
653
654 pub fn classify_span<'s>(&self, source: &'s Source, span: Span) -> Option<SyntaxClass<'s>> {
657 let node = LinkedNode::new(source.root()).find(span)?;
658 let cursor = node.offset() + 1;
659 classify_syntax(node, cursor)
660 }
661
662 pub fn classify_for_decl<'s>(
666 &self,
667 source: &'s Source,
668 position: LspPosition,
669 ) -> Option<SyntaxClass<'s>> {
670 let cursor = self.to_typst_pos_offset(source, position, 1)?;
671 let mut node = LinkedNode::new(source.root()).leaf_at_compat(cursor)?;
672
673 if cursor == node.offset() + 1 && is_mark(node.kind()) {
676 let prev_leaf = node.prev_leaf();
677 if let Some(prev_leaf) = prev_leaf
678 && prev_leaf.range().end == node.offset()
679 {
680 node = prev_leaf;
681 }
682 }
683
684 classify_syntax(node, cursor)
685 }
686
687 pub fn font_info(&self, font: typst::text::Font) -> Option<Arc<DataSource>> {
689 self.world().font_resolver.describe_font(&font)
690 }
691
692 pub fn non_preview_packages(&self) -> EcoVec<PackageIndexEntry> {
695 #[cfg(feature = "local-registry")]
696 let it = || {
697 crate::package::list_package(
698 self.world(),
699 crate::package::PackageFilter::ExceptFor(EcoString::inline("preview")),
700 )
701 };
702 #[cfg(not(feature = "local-registry"))]
703 let it = || Default::default();
704 self.analysis.local_packages.lock().get_or_init(it).clone()
705 }
706
707 pub(crate) fn const_eval(rr: ast::Expr<'_>) -> Option<Value> {
708 Some(match rr {
709 ast::Expr::None(_) => Value::None,
710 ast::Expr::Auto(_) => Value::Auto,
711 ast::Expr::Bool(v) => Value::Bool(v.get()),
712 ast::Expr::Int(v) => Value::Int(v.get()),
713 ast::Expr::Float(v) => Value::Float(v.get()),
714 ast::Expr::Numeric(v) => Value::numeric(v.get()),
715 ast::Expr::Str(v) => Value::Str(v.get().into()),
716 _ => return None,
717 })
718 }
719
720 pub fn module_by_id(&self, fid: TypstFileId) -> SourceResult<Module> {
722 let source = self.source_by_id(fid).at(Span::detached())?;
723 self.module_by_src(source)
724 }
725
726 pub fn module_by_str(&self, rr: String) -> Option<Module> {
728 let src = Source::new(*DETACHED_ENTRY, rr);
729 self.module_by_src(src).ok()
730 }
731
732 pub fn module_by_src(&self, source: Source) -> SourceResult<Module> {
734 eval_compat(&self.world(), &source)
735 }
736
737 pub fn module_by_syntax(self: &Arc<Self>, source: &SyntaxNode) -> Option<Value> {
739 self.module_term_by_syntax(source, true)
740 .and_then(|ty| ty.value())
741 }
742
743 pub fn module_term_by_syntax(self: &Arc<Self>, source: &SyntaxNode, value: bool) -> Option<Ty> {
746 let (src, scope) = self.analyze_import(source);
747 if let Some(scope) = scope {
748 return Some(match scope {
749 Value::Module(m) if m.file_id().is_some() => {
750 Ty::Builtin(BuiltinTy::Module(Decl::module(m.file_id()?).into()))
751 }
752 scope => Ty::Value(InsTy::new(scope)),
753 });
754 }
755
756 match src {
757 Some(Value::Str(s)) => {
758 let id = resolve_id_by_path(self.world(), source.span().id()?, s.as_str())?;
759
760 Some(if value {
761 Ty::Value(InsTy::new(Value::Module(self.module_by_id(id).ok()?)))
762 } else {
763 Ty::Builtin(BuiltinTy::Module(Decl::module(id).into()))
764 })
765 }
766 _ => None,
767 }
768 }
769
770 pub(crate) fn expr_stage_by_id(self: &Arc<Self>, fid: TypstFileId) -> Option<ExprInfo> {
772 Some(self.expr_stage(&self.source_by_id(fid).ok()?))
773 }
774
775 pub(crate) fn expr_stage(self: &Arc<Self>, source: &Source) -> ExprInfo {
777 let mut route = ExprRoute::default();
778 self.expr_stage_(source, &mut route)
779 }
780
781 pub(crate) fn expr_stage_(
783 self: &Arc<Self>,
784 source: &Source,
785 route: &mut ExprRoute,
786 ) -> ExprInfo {
787 use crate::syntax::expr_of;
788 let guard = self.query_stat(source.id(), "expr_stage");
789 self.slot.expr_stage.compute(hash128(&source), |prev| {
790 expr_of(self.clone(), source.clone(), route, guard, prev)
791 })
792 }
793
794 pub(crate) fn exports_of(
795 self: &Arc<Self>,
796 source: &Source,
797 route: &mut ExprRoute,
798 ) -> Option<Arc<LazyHash<LexicalScope>>> {
799 if let Some(s) = route.get(&source.id()) {
800 return s.clone();
801 }
802
803 Some(self.expr_stage_(source, route).exports.clone())
804 }
805
806 pub(crate) fn type_check(self: &Arc<Self>, source: &Source) -> Arc<TypeInfo> {
808 let mut route = TypeEnv::default();
809 self.type_check_(source, &mut route)
810 }
811
812 pub(crate) fn type_check_(
814 self: &Arc<Self>,
815 source: &Source,
816 route: &mut TypeEnv,
817 ) -> Arc<TypeInfo> {
818 use crate::analysis::type_check;
819
820 let ei = self.expr_stage(source);
821 let guard = self.query_stat(source.id(), "type_check");
822 self.slot.type_check.compute(hash128(&ei), |prev| {
823 if let Some(cache_hint) = prev.filter(|prev| prev.revision == ei.revision) {
825 return cache_hint;
826 }
827
828 guard.miss();
829 type_check(self.clone(), ei, route)
830 })
831 }
832
833 #[typst_macros::time(span = source.root().span())]
835 pub(crate) fn lint(self: &Arc<Self>, source: &Source, issues: &KnownIssues) -> LintInfo {
836 let ei = self.expr_stage(source);
837 let ti = self.type_check(source);
838 let guard = self.query_stat(source.id(), "lint");
839 self.slot.lint.compute(hash128(&(&ei, &ti, issues)), |_| {
840 guard.miss();
841 tinymist_lint::lint_file(self.world(), &ei, ti, issues.clone())
842 })
843 }
844
845 pub(crate) fn type_of_func(self: &Arc<Self>, func: Func) -> Signature {
846 crate::log_debug_ct!("convert runtime func {func:?}");
847 analyze_signature(self, SignatureTarget::Convert(func)).unwrap()
848 }
849
850 pub(crate) fn type_of_value(self: &Arc<Self>, val: &Value) -> Ty {
851 crate::log_debug_ct!("convert runtime value {val:?}");
852
853 let cache_key = val;
855 let cached = self
856 .analysis
857 .caches
858 .terms
859 .m
860 .get(&hash128(&cache_key))
861 .and_then(|slot| (cache_key == &slot.1.0).then_some(slot.1.1.clone()));
862 if let Some(cached) = cached {
863 return cached;
864 }
865
866 let res = term_value(val);
867
868 self.analysis
869 .caches
870 .terms
871 .m
872 .entry(hash128(&cache_key))
873 .or_insert_with(|| (self.lifetime, (cache_key.clone(), res.clone())));
874
875 res
876 }
877
878 pub(crate) fn def_of_span(self: &Arc<Self>, source: &Source, span: Span) -> Option<Definition> {
880 let syntax = self.classify_span(source, span)?;
881 definition(self, source, syntax)
882 }
883
884 pub(crate) fn def_of_decl(&self, decl: &Interned<Decl>) -> Option<Definition> {
886 match decl.as_ref() {
887 Decl::Func(..) => Some(Definition::new(decl.clone(), None)),
888 Decl::Module(..) => None,
889 _ => None,
890 }
891 }
892
893 pub(crate) fn def_of_syntax(
898 self: &Arc<Self>,
899 source: &Source,
900 syntax: SyntaxClass,
901 ) -> Option<Definition> {
902 definition(self, source, syntax)
903 }
904
905 pub(crate) fn def_of_syntax_or_dyn(
913 self: &Arc<Self>,
914 source: &Source,
915 syntax: SyntaxClass,
916 ) -> Option<Definition> {
917 let def = self.def_of_syntax(source, syntax.clone());
918 match def.as_ref().map(|d| d.decl.kind()) {
919 Some(DefKind::Reference | DefKind::Module | DefKind::Function) => return def,
921 Some(DefKind::Struct | DefKind::Constant | DefKind::Variable) | None => {}
922 }
923
924 let know_ty_well = def
926 .as_ref()
927 .and_then(|d| self.simplified_type_of_span(d.decl.span()))
928 .filter(|ty| !matches!(ty, Ty::Any))
929 .is_some();
930 if know_ty_well {
931 return def;
932 }
933
934 let def_ref = def.as_ref();
935 let def_name = || Some(def_ref?.name().clone());
936 let dyn_def = self
937 .analyze_expr(syntax.node())
938 .iter()
939 .find_map(|(value, _)| {
940 let def = Definition::from_value(value.clone(), def_name)?;
941 None.or_else(|| {
942 let source = self.source_by_id(def.decl.file_id()?).ok()?;
943 let node = LinkedNode::new(source.root()).find(def.decl.span())?;
944 let def_at_the_span = classify_def_loosely(node)?;
945 self.def_of_span(&source, def_at_the_span.name()?.span())
946 })
947 .or(Some(def))
948 });
949
950 dyn_def.or(def)
952 }
953
954 pub(crate) fn simplified_type_of_span(self: &Arc<Self>, span: Span) -> Option<Ty> {
955 let source = self.source_by_id(span.id()?).ok()?;
956 let (ti, ty) = self.type_of_span_(&source, span)?;
957 Some(ti.simplify(ty, false))
958 }
959
960 pub(crate) fn type_of_span(self: &Arc<Self>, span: Span) -> Option<Ty> {
961 let source = self.source_by_id(span.id()?).ok()?;
962 Some(self.type_of_span_(&source, span)?.1)
963 }
964
965 pub(crate) fn type_of_span_(
966 self: &Arc<Self>,
967 source: &Source,
968 span: Span,
969 ) -> Option<(Arc<TypeInfo>, Ty)> {
970 let ti = self.type_check(source);
971 let ty = ti.type_of_span(span)?;
972 Some((ti, ty))
973 }
974
975 pub(crate) fn post_type_of_node(self: &Arc<Self>, node: LinkedNode) -> Option<Ty> {
976 let id = node.span().id()?;
977 let source = self.source_by_id(id).ok()?;
978 let ty_chk = self.type_check(&source);
979
980 let ty = post_type_check(self.clone(), &ty_chk, node.clone())
981 .or_else(|| ty_chk.type_of_span(node.span()))?;
982 Some(ty_chk.simplify(ty, false))
983 }
984
985 pub(crate) fn sig_of_def(self: &Arc<Self>, def: Definition) -> Option<Signature> {
986 crate::log_debug_ct!("check definition func {def:?}");
987 let source = def.decl.file_id().and_then(|id| self.source_by_id(id).ok());
988 analyze_signature(self, SignatureTarget::Def(source, def))
989 }
990
991 pub(crate) fn sig_of_type(self: &Arc<Self>, ti: &TypeInfo, ty: Ty) -> Option<Signature> {
992 super::sig_of_type(self, ti, ty)
993 }
994
995 pub(crate) fn sig_of_type_or_dyn(
996 self: &Arc<Self>,
997 ti: &TypeInfo,
998 callee_ty: Ty,
999 callee: &SyntaxNode,
1000 ) -> Option<Signature> {
1001 self.sig_of_type(ti, callee_ty).or_else(|| {
1002 self.analyze_expr(callee).iter().find_map(|(value, _)| {
1003 let Value::Func(callee) = value else {
1004 return None;
1005 };
1006
1007 analyze_signature(self, SignatureTarget::Runtime(callee.clone()))
1009 })
1010 })
1011 }
1012
1013 pub fn analyze_import(&self, source: &SyntaxNode) -> (Option<Value>, Option<Value>) {
1020 if let Some(v) = source.cast::<ast::Expr>().and_then(Self::const_eval) {
1021 return (Some(v), None);
1022 }
1023 let token = &self.analysis.workers.import;
1024 token.enter(|| analyze_import_(self.world(), source))
1025 }
1026
1027 pub fn analyze_expr(&self, source: &SyntaxNode) -> EcoVec<(Value, Option<Styles>)> {
1029 let token = &self.analysis.workers.expression;
1030 token.enter(|| analyze_expr_(self.world(), source))
1031 }
1032
1033 pub fn analyze_bib(&self, introspector: &Introspector) -> Option<Arc<BibInfo>> {
1035 let world = self.world();
1036 let world = (world as &dyn World).track();
1037
1038 analyze_bib(world, introspector.track())
1039 }
1040
1041 pub fn tooltip(&self, source: &Source, cursor: usize) -> Option<Tooltip> {
1047 let token = &self.analysis.workers.tooltip;
1048 token.enter(|| tooltip_(self.world(), source, cursor))
1049 }
1050
1051 pub fn get_manifest(&self, toml_id: TypstFileId) -> StrResult<PackageManifest> {
1053 crate::package::get_manifest(self.world(), toml_id)
1054 }
1055
1056 pub fn compute_signature(
1058 self: &Arc<Self>,
1059 func: SignatureTarget,
1060 compute: impl FnOnce(&Arc<Self>) -> Option<Signature> + Send + Sync + 'static,
1061 ) -> Option<Signature> {
1062 let res = match func {
1063 SignatureTarget::Def(src, def) => self
1064 .analysis
1065 .caches
1066 .def_signatures
1067 .entry(hash128(&(src, def.clone())), self.lifetime),
1068 SignatureTarget::SyntaxFast(source, span) => {
1069 let cache_key = (source, span, true);
1070 self.analysis
1071 .caches
1072 .static_signatures
1073 .entry(hash128(&cache_key), self.lifetime)
1074 }
1075 SignatureTarget::Syntax(source, span) => {
1076 let cache_key = (source, span);
1077 self.analysis
1078 .caches
1079 .static_signatures
1080 .entry(hash128(&cache_key), self.lifetime)
1081 }
1082 SignatureTarget::Convert(rt) => self
1083 .analysis
1084 .caches
1085 .signatures
1086 .entry(hash128(&(&rt, true)), self.lifetime),
1087 SignatureTarget::Runtime(rt) => self
1088 .analysis
1089 .caches
1090 .signatures
1091 .entry(hash128(&rt), self.lifetime),
1092 };
1093 res.get_or_init(|| compute(self)).clone()
1094 }
1095
1096 pub(crate) fn compute_docstring(
1097 self: &Arc<Self>,
1098 fid: TypstFileId,
1099 docs: String,
1100 kind: DefKind,
1101 ) -> Option<Arc<DocString>> {
1102 let res = self
1103 .analysis
1104 .caches
1105 .docstrings
1106 .entry(hash128(&(fid, &docs, kind)), self.lifetime);
1107 res.get_or_init(|| {
1108 crate::syntax::docs::do_compute_docstring(self, fid, docs, kind).map(Arc::new)
1109 })
1110 .clone()
1111 }
1112
1113 pub fn remove_html(&self, markup: EcoString) -> EcoString {
1115 if !self.analysis.remove_html {
1116 return markup;
1117 }
1118
1119 static REMOVE_HTML_COMMENT_REGEX: LazyLock<regex::Regex> =
1120 LazyLock::new(|| regex::Regex::new(r#"<!--[\s\S]*?-->"#).unwrap());
1121 REMOVE_HTML_COMMENT_REGEX
1122 .replace_all(&markup, "")
1123 .trim()
1124 .into()
1125 }
1126
1127 fn query_stat(&self, id: TypstFileId, query: &'static str) -> QueryStatGuard {
1128 self.analysis.stats.stat(Some(id), query)
1129 }
1130
1131 pub(crate) fn prefetch_type_check(self: &Arc<Self>, _fid: TypstFileId) {
1134 }
1144
1145 pub(crate) fn preload_package(self: Arc<Self>, entry_point: TypstFileId) {
1146 crate::log_debug_ct!("preload package start {entry_point:?}");
1147
1148 #[derive(Clone)]
1149 struct Preloader {
1150 shared: Arc<SharedContext>,
1151 analyzed: Arc<Mutex<HashSet<TypstFileId>>>,
1152 }
1153
1154 impl Preloader {
1155 fn work(&self, fid: TypstFileId) {
1156 crate::log_debug_ct!("preload package {fid:?}");
1157 let source = self.shared.source_by_id(fid).ok().unwrap();
1158 let exprs = self.shared.expr_stage(&source);
1159 self.shared.type_check(&source);
1160 exprs.imports.iter().for_each(|(fid, _)| {
1161 if !self.analyzed.lock().insert(*fid) {
1162 return;
1163 }
1164 self.work(*fid);
1165 })
1166 }
1167 }
1168
1169 let preloader = Preloader {
1170 shared: self,
1171 analyzed: Arc::default(),
1172 };
1173
1174 preloader.work(entry_point);
1175 }
1176}
1177
1178type DeferredCompute<T> = Arc<OnceLock<T>>;
1180
1181#[derive(Clone)]
1182struct IncrCacheMap<K, V> {
1183 revision: usize,
1184 global: Arc<Mutex<FxDashMap<K, (usize, V)>>>,
1185 prev: Arc<Mutex<FxHashMap<K, DeferredCompute<V>>>>,
1186 next: Arc<Mutex<FxHashMap<K, DeferredCompute<V>>>>,
1187}
1188
1189impl<K: Eq + Hash, V> Default for IncrCacheMap<K, V> {
1190 fn default() -> Self {
1191 Self {
1192 revision: 0,
1193 global: Arc::default(),
1194 prev: Arc::default(),
1195 next: Arc::default(),
1196 }
1197 }
1198}
1199
1200impl<K, V> IncrCacheMap<K, V> {
1201 fn compute(&self, key: K, compute: impl FnOnce(Option<V>) -> V) -> V
1202 where
1203 K: Clone + Eq + Hash,
1204 V: Clone,
1205 {
1206 let next = self.next.lock().entry(key.clone()).or_default().clone();
1207
1208 next.get_or_init(|| {
1209 let prev = self.prev.lock().get(&key).cloned();
1210 let prev = prev.and_then(|prev| prev.get().cloned());
1211 let prev = prev.or_else(|| {
1212 let global = self.global.lock();
1213 global.get(&key).map(|global| global.1.clone())
1214 });
1215
1216 let res = compute(prev);
1217
1218 let global = self.global.lock();
1219 let entry = global.entry(key.clone());
1220 use dashmap::mapref::entry::Entry;
1221 match entry {
1222 Entry::Occupied(mut entry) => {
1223 let (revision, _) = entry.get();
1224 if *revision < self.revision {
1225 entry.insert((self.revision, res.clone()));
1226 }
1227 }
1228 Entry::Vacant(entry) => {
1229 entry.insert((self.revision, res.clone()));
1230 }
1231 }
1232
1233 res
1234 })
1235 .clone()
1236 }
1237
1238 fn crawl(&self, revision: usize) -> Self {
1239 Self {
1240 revision,
1241 prev: self.next.clone(),
1242 global: self.global.clone(),
1243 next: Default::default(),
1244 }
1245 }
1246}
1247
1248#[derive(Clone)]
1249struct CacheMap<T> {
1250 m: Arc<FxDashMap<u128, (u64, T)>>,
1251 }
1253
1254impl<T> Default for CacheMap<T> {
1255 fn default() -> Self {
1256 Self {
1257 m: Default::default(),
1258 }
1260 }
1261}
1262
1263impl<T> CacheMap<T> {
1264 fn clear(&self) {
1265 self.m.clear();
1266 }
1267
1268 fn retain(&self, mut f: impl FnMut(&mut (u64, T)) -> bool) {
1269 self.m.retain(|_k, v| f(v));
1270 }
1271}
1272
1273impl<T: Default + Clone> CacheMap<T> {
1274 fn entry(&self, key: u128, lifetime: u64) -> T {
1275 let entry = self.m.entry(key);
1276 let entry = entry.or_insert_with(|| (lifetime, T::default()));
1277 entry.1.clone()
1278 }
1279}
1280
1281#[derive(Default)]
1283pub struct AnalysisGlobalWorkers {
1284 import: RateLimiter,
1286 expression: RateLimiter,
1288 tooltip: RateLimiter,
1290}
1291
1292#[derive(Default, Clone)]
1295pub struct AnalysisGlobalCaches {
1296 lifetime: Arc<AtomicU64>,
1297 clear_lifetime: Arc<AtomicU64>,
1298 def_signatures: CacheMap<DeferredCompute<Option<Signature>>>,
1299 static_signatures: CacheMap<DeferredCompute<Option<Signature>>>,
1300 signatures: CacheMap<DeferredCompute<Option<Signature>>>,
1301 docstrings: CacheMap<DeferredCompute<Option<Arc<DocString>>>>,
1302 terms: CacheMap<(Value, Ty)>,
1303}
1304
1305#[derive(Default)]
1311pub struct AnalysisLocalCaches {
1312 modules: HashMap<TypstFileId, ModuleAnalysisLocalCache>,
1313 completion_files: OnceLock<Vec<TypstFileId>>,
1314 root_files: OnceLock<Vec<TypstFileId>>,
1315 module_deps: OnceLock<HashMap<TypstFileId, ModuleDependency>>,
1316}
1317
1318#[derive(Default)]
1323pub struct ModuleAnalysisLocalCache {
1324 expr_stage: OnceLock<ExprInfo>,
1325 type_check: OnceLock<Arc<TypeInfo>>,
1326}
1327
1328#[derive(Default)]
1331pub struct AnalysisRevCache {
1332 default_slot: AnalysisRevSlot,
1333 manager: RevisionManager<AnalysisRevSlot>,
1334}
1335
1336impl RevisionManagerLike for AnalysisRevCache {
1337 fn gc(&mut self, rev: usize) {
1338 self.manager.gc(rev);
1339
1340 {
1342 let mut max_ei = FxHashMap::default();
1343 let es = self.default_slot.expr_stage.global.lock();
1344 for r in es.iter() {
1345 let rev: &mut usize = max_ei.entry(r.1.fid).or_default();
1346 *rev = (*rev).max(r.1.revision);
1347 }
1348 es.retain(|_, r| r.1.revision == *max_ei.get(&r.1.fid).unwrap_or(&0));
1349 }
1350
1351 {
1352 let mut max_ti = FxHashMap::default();
1353 let ts = self.default_slot.type_check.global.lock();
1354 for r in ts.iter() {
1355 let rev: &mut usize = max_ti.entry(r.1.fid).or_default();
1356 *rev = (*rev).max(r.1.revision);
1357 }
1358 ts.retain(|_, r| r.1.revision == *max_ti.get(&r.1.fid).unwrap_or(&0));
1359 }
1360
1361 {
1362 let mut max_li = FxHashMap::default();
1363 let ts = self.default_slot.lint.global.lock();
1364 for r in ts.iter() {
1365 let rev: &mut usize = max_li.entry(r.1.fid).or_default();
1366 *rev = (*rev).max(r.1.revision);
1367 }
1368 ts.retain(|_, r| r.1.revision == *max_li.get(&r.1.fid).unwrap_or(&0));
1369 }
1370 }
1371}
1372
1373impl AnalysisRevCache {
1374 fn clear(&mut self) {
1375 self.manager.clear();
1376 self.default_slot = Default::default();
1377 }
1378
1379 fn find_revision(
1381 &mut self,
1382 revision: NonZeroUsize,
1383 lg: &AnalysisRevLock,
1384 ) -> Arc<RevisionSlot<AnalysisRevSlot>> {
1385 lg.inner.access(revision);
1386 self.manager.find_revision(revision, |slot_base| {
1387 log::debug!("analysis revision {} is created", revision.get());
1388 slot_base
1389 .map(|slot| AnalysisRevSlot {
1390 revision: slot.revision,
1391 expr_stage: slot.data.expr_stage.crawl(revision.get()),
1392 type_check: slot.data.type_check.crawl(revision.get()),
1393 lint: slot.data.lint.crawl(revision.get()),
1394 })
1395 .unwrap_or_else(|| self.default_slot.clone())
1396 })
1397 }
1398}
1399
1400pub struct AnalysisRevLock {
1402 inner: RevisionLock,
1403 tokens: Option<SemanticTokenContext>,
1404 grid: Arc<Mutex<AnalysisRevCache>>,
1405}
1406
1407impl Drop for AnalysisRevLock {
1408 fn drop(&mut self) {
1409 let mut mu = self.grid.lock();
1410 let gc_revision = mu.manager.unlock(&mut self.inner);
1411
1412 if let Some(gc_revision) = gc_revision {
1413 let grid = self.grid.clone();
1414 rayon::spawn(move || {
1415 grid.lock().gc(gc_revision);
1416 });
1417 }
1418 }
1419}
1420
1421#[derive(Default, Clone)]
1422struct AnalysisRevSlot {
1423 revision: usize,
1424 expr_stage: IncrCacheMap<u128, ExprInfo>,
1425 type_check: IncrCacheMap<u128, Arc<TypeInfo>>,
1426 lint: IncrCacheMap<u128, LintInfo>,
1427}
1428
1429impl Drop for AnalysisRevSlot {
1430 fn drop(&mut self) {
1431 log::debug!("analysis revision {} is dropped", self.revision);
1432 }
1433}
1434
1435fn ceil_char_boundary(text: &str, mut cursor: usize) -> usize {
1436 while cursor < text.len() && !text.is_char_boundary(cursor) {
1438 cursor += 1;
1439 }
1440
1441 cursor.min(text.len())
1442}
1443
1444#[typst_macros::time]
1445#[comemo::memoize]
1446fn analyze_bib(
1447 world: Tracked<dyn World + '_>,
1448 introspector: Tracked<Introspector>,
1449) -> Option<Arc<BibInfo>> {
1450 let bib_elem = BibliographyElem::find(introspector).ok()?;
1451
1452 let csl_style = bib_elem.style.get_cloned(StyleChain::default()).derived;
1455
1456 let Value::Array(paths) = bib_elem.sources.clone().into_value() else {
1457 return None;
1458 };
1459 let elem_fid = bib_elem.span().id()?;
1460 let files = paths
1461 .into_iter()
1462 .flat_map(|path| path.cast().ok())
1463 .flat_map(|bib_path: EcoString| {
1464 let bib_fid = resolve_id_by_path(world.deref(), elem_fid, &bib_path)?;
1465 Some((bib_fid, world.file(bib_fid).ok()?))
1466 });
1467
1468 bib_info(csl_style, files)
1469}
1470
1471#[comemo::memoize]
1472fn loc_info(bytes: Bytes) -> Option<EcoVec<(usize, String)>> {
1473 let mut loc = EcoVec::new();
1474 let mut offset = 0;
1475 for line in bytes.split(|byte| *byte == b'\n') {
1476 loc.push((offset, String::from_utf8(line.to_owned()).ok()?));
1477 offset += line.len() + 1;
1478 }
1479 Some(loc)
1480}
1481
1482fn find_loc(
1483 len: usize,
1484 loc: &EcoVec<(usize, String)>,
1485 mut offset: usize,
1486 encoding: PositionEncoding,
1487) -> Option<LspPosition> {
1488 if offset > len {
1489 offset = len;
1490 }
1491
1492 let r = match loc.binary_search_by_key(&offset, |line| line.0) {
1493 Ok(i) => i,
1494 Err(i) => i - 1,
1495 };
1496
1497 let (start, s) = loc.get(r)?;
1498 let byte_offset = offset.saturating_sub(*start);
1499
1500 let column_prefix = if byte_offset <= s.len() {
1501 &s[..byte_offset]
1502 } else {
1503 let line = (r + 1) as u32;
1504 return Some(LspPosition { line, character: 0 });
1505 };
1506
1507 let line = r as u32;
1508 let character = match encoding {
1509 PositionEncoding::Utf8 => column_prefix.chars().count(),
1510 PositionEncoding::Utf16 => column_prefix.chars().map(|ch| ch.len_utf16()).sum(),
1511 } as u32;
1512
1513 Some(LspPosition { line, character })
1514}
1515
1516pub struct SearchCtx<'a> {
1518 pub ctx: &'a mut LocalContext,
1520 pub searched: HashSet<TypstFileId>,
1522 pub worklist: Vec<TypstFileId>,
1524}
1525
1526impl SearchCtx<'_> {
1527 pub fn push(&mut self, fid: TypstFileId) -> bool {
1529 if self.searched.insert(fid) {
1530 self.worklist.push(fid);
1531 true
1532 } else {
1533 false
1534 }
1535 }
1536
1537 pub fn push_dependents(&mut self, fid: TypstFileId) {
1539 let deps = self.ctx.module_dependencies().get(&fid);
1540 let dependents = deps.map(|dep| dep.dependents.clone()).into_iter().flatten();
1541 for dep in dependents {
1542 self.push(dep);
1543 }
1544 }
1545}
1546
1547#[derive(Default)]
1549pub struct RateLimiter {
1550 token: std::sync::Mutex<()>,
1551}
1552
1553impl RateLimiter {
1554 #[must_use]
1556 pub fn enter<T>(&self, f: impl FnOnce() -> T) -> T {
1557 let _c = self.token.lock().unwrap();
1558 f()
1559 }
1560}