tinymist_analysis/syntax/
import.rs1use crate::prelude::*;
4
5pub fn resolve_id_by_path(
7 world: &dyn World,
8 current: TypstFileId,
9 import_path: &str,
10) -> Option<TypstFileId> {
11 if import_path.starts_with('@') {
12 let spec = import_path.parse::<PackageSpec>().ok()?;
13 let manifest_id = TypstFileId::new(Some(spec.clone()), VirtualPath::new("typst.toml"));
15 let bytes = world.file(manifest_id).ok()?;
16 let string = std::str::from_utf8(&bytes).map_err(FileError::from).ok()?;
17 let manifest: PackageManifest = toml::from_str(string).ok()?;
18 manifest.validate(&spec).ok()?;
19
20 return Some(manifest_id.join(&manifest.package.entrypoint));
22 }
23
24 let path = Path::new(import_path);
25 let vpath = if path.is_relative() {
26 current.vpath().join(path)
27 } else {
28 VirtualPath::new(path)
29 };
30
31 Some(TypstFileId::new(current.package().cloned(), vpath))
32}
33
34pub fn find_source_by_expr(
36 world: &dyn World,
37 current: TypstFileId,
38 import_source: ast::Expr,
39) -> Option<Source> {
40 match import_source {
42 ast::Expr::Str(s) => world
43 .source(resolve_id_by_path(world, current, s.get().as_str())?)
44 .ok(),
45 _ => None,
46 }
47}
48
49pub fn cast_include_expr<'a>(name: &str, node: ast::Expr<'a>) -> Option<ast::Expr<'a>> {
51 match node {
52 ast::Expr::Include(inc) => Some(inc.source()),
53 ast::Expr::Code(code) => {
54 let exprs = code.body();
55 if exprs.exprs().count() != 1 {
56 eprintln!("example function must have a single inclusion: {name}");
57 return None;
58 }
59 cast_include_expr(name, exprs.exprs().next().unwrap())
60 }
61 _ => {
62 eprintln!("example function must have a single inclusion: {name}");
63 None
64 }
65 }
66}