tinymist_analysis/syntax/
import.rs

1//! Import resolution utilities.
2
3use crate::prelude::*;
4
5/// Resolves a file id by its import path.
6pub 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        // Evaluates the manifest.
14        let manifest_id = TypstFileId::new(RootedPath::new(
15            VirtualRoot::Package(spec.clone()),
16            VirtualPath::new("typst.toml").ok()?,
17        ));
18        let bytes = world.file(manifest_id).ok()?;
19        let string = std::str::from_utf8(&bytes).map_err(FileError::from).ok()?;
20        let manifest: PackageManifest = toml::from_str(string).ok()?;
21        manifest.validate(&spec).ok()?;
22
23        // Evaluates the entry point.
24        return Some(
25            resolve_path_from_id(manifest_id, manifest.package.entrypoint.as_str())
26                .ok()?
27                .intern(),
28        );
29    }
30
31    Some(resolve_path_from_id(current, import_path).ok()?.intern())
32}
33
34/// Finds a source instance by its import node.
35pub fn find_source_by_expr(
36    world: &dyn World,
37    current: TypstFileId,
38    import_source: ast::Expr,
39) -> Option<Source> {
40    // todo: this could be valid: import("path.typ"), where v is parenthesized
41    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
49/// Casts a node to a single include expression.
50pub fn cast_include_expr<'a>(name: &str, node: ast::Expr<'a>) -> Option<ast::Expr<'a>> {
51    match node {
52        ast::Expr::ModuleInclude(inc) => Some(inc.source()),
53        ast::Expr::CodeBlock(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}