tinymist_query/syntax/
index.rs1use std::str::FromStr;
2
3use rustc_hash::FxHashSet;
4use tinymist_world::package::PackageSpec;
5
6use crate::{adt::interner::Interned, prelude::*};
7
8#[derive(Default)]
10pub struct IndexInfo {
11 pub(crate) paths: FxHashSet<Interned<str>>,
13 pub(crate) packages: FxHashSet<PackageSpec>,
15 pub(crate) identifiers: FxHashSet<Interned<str>>,
17}
18
19#[typst_macros::time(span = src.root().span())]
21#[comemo::memoize]
22pub fn get_index_info(src: &Source) -> Arc<IndexInfo> {
23 let root = src.root();
24 let mut worker = IndexWorker {
25 info: IndexInfo::default(),
26 };
27 worker.visit(root);
28 Arc::new(worker.info)
29}
30
31struct IndexWorker {
33 info: IndexInfo,
34}
35
36impl IndexWorker {
37 fn visit(&mut self, node: &SyntaxNode) {
39 match node.cast::<ast::Expr>() {
40 Some(ast::Expr::Str(path_str)) => {
41 if path_str.to_untyped().text().len() > 65536 {
42 return;
44 }
45 let path_str = path_str.get();
46
47 if path_str.starts_with('@') {
48 let pkg_spec = PackageSpec::from_str(&path_str).ok();
49 if let Some(pkg_spec) = pkg_spec {
50 self.info.identifiers.insert(pkg_spec.name.clone().into());
51 self.info.packages.insert(pkg_spec);
52 }
53 return;
54 }
55 let path = Path::new(path_str.as_str());
56 let name = path.file_name().unwrap_or_default().to_str();
57 if let Some(name) = name {
58 self.info.paths.insert(name.into());
59 }
60 }
61 Some(ast::Expr::MathIdent(ident)) => {
62 self.info.identifiers.insert(ident.get().into());
63 }
64 Some(ast::Expr::Ident(ident)) => {
65 self.info.identifiers.insert(ident.get().into());
66 }
67 _ => {}
68 }
69
70 for child in node.children() {
71 self.visit(child);
72 }
73 }
74}