tinymist_analysis/
track_values.rs1use comemo::Track;
4use ecow::*;
5use tinymist_std::typst::{TypstDocument, TypstPagedDocument};
6use typst::World;
7use typst::engine::{Engine, Route, Sink, Traced};
8use typst::foundations::{Context, Label, Scopes, Styles, Value};
9use typst::introspection::Introspector;
10use typst::model::BibliographyElem;
11use typst::syntax::{LinkedNode, Span, SyntaxKind, SyntaxNode, ast};
12use typst_shim::eval::Vm;
13use typst_shim::is_syntax_only;
14
15use crate::stats::GLOBAL_STATS;
16
17pub fn analyze_expr(world: &dyn World, node: &LinkedNode) -> EcoVec<(Value, Option<Styles>)> {
19 if let Some(parent) = node.parent()
20 && parent.kind() == SyntaxKind::FieldAccess
21 && node.index() > 0
22 {
23 return analyze_expr(world, parent);
24 }
25
26 analyze_expr_(world, node.get())
27}
28
29#[typst_macros::time(span = node.span())]
31pub fn analyze_expr_(world: &dyn World, node: &SyntaxNode) -> EcoVec<(Value, Option<Styles>)> {
32 let Some(expr) = node.cast::<ast::Expr>() else {
33 return eco_vec![];
34 };
35
36 let val = match expr {
37 ast::Expr::None(_) => Value::None,
38 ast::Expr::Auto(_) => Value::Auto,
39 ast::Expr::Bool(v) => Value::Bool(v.get()),
40 ast::Expr::Int(v) => Value::Int(v.get()),
41 ast::Expr::Float(v) => Value::Float(v.get()),
42 ast::Expr::Numeric(v) => Value::numeric(v.get()),
43 ast::Expr::Str(v) => Value::Str(v.get().into()),
44 _ => {
45 if node.kind() == SyntaxKind::Contextual
46 && let Some(child) = node.children().last()
47 {
48 return analyze_expr_(world, child);
49 }
50
51 if is_syntax_only() {
54 return eco_vec![];
55 }
56
57 let _guard = GLOBAL_STATS.stat(node.span().id(), "analyze_expr");
58 return typst::trace::<TypstPagedDocument>(world, node.span());
59 }
60 };
61
62 eco_vec![(val, None)]
63}
64
65#[typst_macros::time(span = source.span())]
67pub fn analyze_import_(world: &dyn World, source: &SyntaxNode) -> (Option<Value>, Option<Value>) {
68 let source_span = source.span();
69 let Some((source, _)) = analyze_expr_(world, source).into_iter().next() else {
70 return (None, None);
71 };
72 if source.scope().is_some() {
73 return (Some(source.clone()), Some(source));
74 }
75
76 let _guard = GLOBAL_STATS.stat(source_span.id(), "analyze_import");
77
78 let introspector = Introspector::default();
79 let traced = Traced::default();
80 let mut sink = Sink::new();
81 let engine = Engine {
82 routines: &typst::ROUTINES,
83 world: world.track(),
84 route: Route::default(),
85 introspector: introspector.track(),
86 traced: traced.track(),
87 sink: sink.track_mut(),
88 };
89
90 let context = Context::none();
91 let mut vm = Vm::new(
92 engine,
93 context.track(),
94 Scopes::new(Some(world.library())),
95 Span::detached(),
96 );
97 let module = match source.clone() {
98 Value::Str(path) => typst_shim::eval::import(&mut vm.engine, &path, source_span)
99 .ok()
100 .map(Value::Module),
101 Value::Module(module) => Some(Value::Module(module)),
102 _ => None,
103 };
104
105 (Some(source), module)
106}
107
108pub struct DynLabel {
110 pub label: Label,
112 pub label_desc: Option<EcoString>,
114 pub detail: Option<EcoString>,
116 pub bib_title: Option<EcoString>,
119}
120
121#[typst_macros::time]
128pub fn analyze_labels(document: &TypstDocument) -> (Vec<DynLabel>, usize) {
129 let mut output = vec![];
130
131 let _guard = GLOBAL_STATS.stat(None, "analyze_labels");
132
133 for elem in document.introspector().all() {
135 let Some(label) = elem.label() else { continue };
136 let (is_derived, details) = {
137 let derived = elem
138 .get_by_name("caption")
139 .or_else(|_| elem.get_by_name("body"));
140
141 match derived {
142 Ok(Value::Content(content)) => (true, content.plain_text()),
143 Ok(Value::Str(s)) => (true, s.into()),
144 Ok(_) => (false, elem.plain_text()),
145 Err(_) => (false, elem.plain_text()),
146 }
147 };
148 output.push(DynLabel {
149 label,
150 label_desc: Some(if is_derived {
151 details.clone()
152 } else {
153 eco_format!("{}(..)", elem.func().name())
154 }),
155 detail: Some(details),
156 bib_title: None,
157 });
158 }
159
160 let split = output.len();
161
162 for (label, detail) in BibliographyElem::keys(document.introspector().track()) {
164 output.push(DynLabel {
165 label,
166 label_desc: detail.clone(),
167 detail: detail.clone(),
168 bib_title: detail,
169 });
170 }
171
172 (output, split)
173}