Simplify import handling

This commit is contained in:
Laurenz 2024-11-10 13:35:49 +01:00
parent 6c22ba1cbd
commit 5625914872
3 changed files with 79 additions and 75 deletions

View File

@ -3,6 +3,7 @@ use ecow::{eco_format, eco_vec, EcoString};
use typst_library::diag::{
bail, error, warning, At, FileError, SourceResult, Trace, Tracepoint,
};
use typst_library::engine::Engine;
use typst_library::foundations::{Content, Module, Value};
use typst_library::World;
use typst_syntax::ast::{self, AstNode};
@ -28,8 +29,16 @@ impl Eval for ast::ModuleImport<'_> {
}
}
Value::Type(_) => {}
other => {
source = Value::Module(import(vm, other.clone(), source_span, true)?);
Value::Module(_) => {}
Value::Str(path) => {
source = Value::Module(import(&mut vm.engine, path, source_span)?);
}
v => {
bail!(
source_span,
"expected path, module, function, or type, found {}",
v.ty()
)
}
}
@ -139,43 +148,34 @@ impl Eval for ast::ModuleInclude<'_> {
fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
let span = self.source().span();
let source = self.source().eval(vm)?;
let module = import(vm, source, span, false)?;
let module = match source {
Value::Str(path) => import(&mut vm.engine, &path, span)?,
Value::Module(module) => module,
v => bail!(span, "expected path or module, found {}", v.ty()),
};
Ok(module.content())
}
}
/// Process an import of a module relative to the current location.
pub fn import(
vm: &mut Vm,
source: Value,
span: Span,
allow_scopes: bool,
) -> SourceResult<Module> {
let path = match source {
Value::Str(path) => path,
Value::Module(module) => return Ok(module),
v if allow_scopes => {
bail!(span, "expected path, module, function, or type, found {}", v.ty())
}
v => bail!(span, "expected path or module, found {}", v.ty()),
};
// Handle package and file imports.
let path = path.as_str();
if path.starts_with('@') {
let spec = path.parse::<PackageSpec>().at(span)?;
import_package(vm, spec, span)
/// Process an import of a package or file relative to the current location.
pub fn import(engine: &mut Engine, from: &str, span: Span) -> SourceResult<Module> {
if from.starts_with('@') {
let spec = from.parse::<PackageSpec>().at(span)?;
import_package(engine, spec, span)
} else {
import_file(vm, path, span)
import_file(engine, from, span)
}
}
/// Import an external package.
fn import_package(vm: &mut Vm, spec: PackageSpec, span: Span) -> SourceResult<Module> {
fn import_package(
engine: &mut Engine,
spec: PackageSpec,
span: Span,
) -> SourceResult<Module> {
// Evaluate the manifest.
let world = vm.world();
let manifest_id = FileId::new(Some(spec.clone()), VirtualPath::new("typst.toml"));
let bytes = world.file(manifest_id).at(span)?;
let bytes = engine.world.file(manifest_id).at(span)?;
let string = std::str::from_utf8(&bytes).map_err(FileError::from).at(span)?;
let manifest: PackageManifest = toml::from_str(string)
.map_err(|err| eco_format!("package manifest is malformed ({})", err.message()))
@ -184,47 +184,47 @@ fn import_package(vm: &mut Vm, spec: PackageSpec, span: Span) -> SourceResult<Mo
// Evaluate the entry point.
let entrypoint_id = manifest_id.join(&manifest.package.entrypoint);
let source = world.source(entrypoint_id).at(span)?;
let source = engine.world.source(entrypoint_id).at(span)?;
// Prevent cyclic importing.
if vm.engine.route.contains(source.id()) {
if engine.route.contains(source.id()) {
bail!(span, "cyclic import");
}
let point = || Tracepoint::Import;
Ok(eval(
vm.engine.routines,
vm.engine.world,
vm.engine.traced,
TrackedMut::reborrow_mut(&mut vm.engine.sink),
vm.engine.route.track(),
engine.routines,
engine.world,
engine.traced,
TrackedMut::reborrow_mut(&mut engine.sink),
engine.route.track(),
&source,
)
.trace(world, point, span)?
.trace(engine.world, point, span)?
.with_name(manifest.package.name))
}
/// Import a file from a path.
fn import_file(vm: &mut Vm, path: &str, span: Span) -> SourceResult<Module> {
/// Import a file from a path. The path is resolved relative to the given
/// `span`.
fn import_file(engine: &mut Engine, path: &str, span: Span) -> SourceResult<Module> {
// Load the source file.
let world = vm.world();
let id = span.resolve_path(path).at(span)?;
let source = world.source(id).at(span)?;
let source = engine.world.source(id).at(span)?;
// Prevent cyclic importing.
if vm.engine.route.contains(source.id()) {
if engine.route.contains(source.id()) {
bail!(span, "cyclic import");
}
// Evaluate the file.
let point = || Tracepoint::Import;
eval(
vm.engine.routines,
vm.engine.world,
vm.engine.traced,
TrackedMut::reborrow_mut(&mut vm.engine.sink),
vm.engine.route.track(),
engine.routines,
engine.world,
engine.traced,
TrackedMut::reborrow_mut(&mut engine.sink),
engine.route.track(),
&source,
)
.trace(world, point, span)
.trace(engine.world, point, span)
}

View File

@ -1,11 +1,8 @@
use comemo::Track;
use ecow::{eco_vec, EcoString, EcoVec};
use typst::engine::{Engine, Route, Sink, Traced};
use typst::foundations::{Context, Label, Scopes, Styles, Value};
use typst::introspection::Introspector;
use typst::foundations::{Label, Styles, Value};
use typst::model::{BibliographyElem, Document};
use typst::syntax::{ast, LinkedNode, Span, SyntaxKind};
use typst_eval::Vm;
use typst::syntax::{ast, LinkedNode, SyntaxKind};
use crate::IdeWorld;
@ -46,7 +43,7 @@ pub fn analyze_expr(
eco_vec![(val, None)]
}
/// Try to load a module from the current source file.
/// Tries to load a module from the given `source` node.
pub fn analyze_import(world: &dyn IdeWorld, source: &LinkedNode) -> Option<Value> {
// Use span in the node for resolving imports with relative paths.
let source_span = source.span();
@ -55,29 +52,11 @@ pub fn analyze_import(world: &dyn IdeWorld, source: &LinkedNode) -> Option<Value
return Some(source);
}
let introspector = Introspector::default();
let traced = Traced::default();
let mut sink = Sink::new();
let engine = Engine {
routines: &typst::ROUTINES,
world: world.upcast().track(),
introspector: introspector.track(),
traced: traced.track(),
sink: sink.track_mut(),
route: Route::default(),
};
let Value::Str(path) = source else { return None };
let context = Context::none();
let mut vm = Vm::new(
engine,
context.track(),
Scopes::new(Some(world.library())),
Span::detached(),
);
typst_eval::import(&mut vm, source, source_span, true)
.ok()
.map(Value::Module)
crate::utils::with_engine(world, |engine| {
typst_eval::import(engine, &path, source_span).ok().map(Value::Module)
})
}
/// Find all labels and details for them.

View File

@ -1,8 +1,33 @@
use std::fmt::Write;
use comemo::Track;
use ecow::{eco_format, EcoString};
use typst::engine::{Engine, Route, Sink, Traced};
use typst::introspection::Introspector;
use typst::text::{FontInfo, FontStyle};
use crate::IdeWorld;
/// Create a temporary engine and run a task on it.
pub fn with_engine<F, T>(world: &dyn IdeWorld, f: F) -> T
where
F: FnOnce(&mut Engine) -> T,
{
let introspector = Introspector::default();
let traced = Traced::default();
let mut sink = Sink::new();
let mut engine = Engine {
routines: &typst::ROUTINES,
world: world.upcast().track(),
introspector: introspector.track(),
traced: traced.track(),
sink: sink.track_mut(),
route: Route::default(),
};
f(&mut engine)
}
/// Extract the first sentence of plain text of a piece of documentation.
///
/// Removes Markdown formatting.