summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/typst-cli/src/compile.rs20
-rw-r--r--crates/typst-library/src/compute/construct.rs2
-rw-r--r--crates/typst-library/src/compute/data.rs12
-rw-r--r--crates/typst-library/src/meta/bibliography.rs2
-rw-r--r--crates/typst-library/src/text/raw.rs4
-rw-r--r--crates/typst-library/src/visualize/image.rs2
-rw-r--r--crates/typst-syntax/src/file.rs44
-rw-r--r--crates/typst-syntax/src/node.rs6
-rw-r--r--crates/typst-syntax/src/reparser.rs4
-rw-r--r--crates/typst-syntax/src/source.rs24
-rw-r--r--crates/typst-syntax/src/span.rs69
-rw-r--r--crates/typst/src/diag.rs16
-rw-r--r--crates/typst/src/eval/func.rs11
-rw-r--r--crates/typst/src/eval/mod.rs43
-rw-r--r--crates/typst/src/eval/tracer.rs2
-rw-r--r--crates/typst/src/ide/analyze.rs2
-rw-r--r--crates/typst/src/ide/highlight.rs6
-rw-r--r--crates/typst/src/ide/jump.rs14
-rw-r--r--crates/typst/src/lib.rs16
-rw-r--r--crates/typst/src/model/mod.rs8
20 files changed, 140 insertions, 167 deletions
diff --git a/crates/typst-cli/src/compile.rs b/crates/typst-cli/src/compile.rs
index 4c4c24be..5ae569ba 100644
--- a/crates/typst-cli/src/compile.rs
+++ b/crates/typst-cli/src/compile.rs
@@ -8,8 +8,8 @@ use typst::diag::{bail, Severity, SourceDiagnostic, StrResult};
use typst::doc::Document;
use typst::eval::{eco_format, Tracer};
use typst::geom::Color;
-use typst::syntax::{FileId, Source};
-use typst::World;
+use typst::syntax::{FileId, Source, Span};
+use typst::{World, WorldExt};
use crate::args::{CompileCommand, DiagnosticFormat, OutputFormat};
use crate::watch::Status;
@@ -231,19 +231,16 @@ pub fn print_diagnostics(
.map(|e| (eco_format!("hint: {e}")).into())
.collect(),
)
- .with_labels(vec![Label::primary(
- diagnostic.span.id(),
- world.range(diagnostic.span),
- )]);
+ .with_labels(label(world, diagnostic.span).into_iter().collect());
term::emit(&mut w, &config, world, &diag)?;
// Stacktrace-like helper diagnostics.
for point in &diagnostic.trace {
let message = point.v.to_string();
- let help = Diagnostic::help().with_message(message).with_labels(vec![
- Label::primary(point.span.id(), world.range(point.span)),
- ]);
+ let help = Diagnostic::help()
+ .with_message(message)
+ .with_labels(label(world, point.span).into_iter().collect());
term::emit(&mut w, &config, world, &help)?;
}
@@ -252,6 +249,11 @@ pub fn print_diagnostics(
Ok(())
}
+/// Create a label for a span.
+fn label(world: &SystemWorld, span: Span) -> Option<Label<FileId>> {
+ Some(Label::primary(span.id()?, world.range(span)?))
+}
+
impl<'a> codespan_reporting::files::Files<'a> for SystemWorld {
type FileId = FileId;
type Name = String;
diff --git a/crates/typst-library/src/compute/construct.rs b/crates/typst-library/src/compute/construct.rs
index 59a867a7..23f0c225 100644
--- a/crates/typst-library/src/compute/construct.rs
+++ b/crates/typst-library/src/compute/construct.rs
@@ -935,7 +935,7 @@ pub fn plugin(
vm: &mut Vm,
) -> SourceResult<Plugin> {
let Spanned { v: path, span } = path;
- let id = vm.location().join(&path).at(span)?;
+ let id = vm.resolve_path(&path).at(span)?;
let data = vm.world().file(id).at(span)?;
Plugin::new(data).at(span)
}
diff --git a/crates/typst-library/src/compute/data.rs b/crates/typst-library/src/compute/data.rs
index 944baff3..cd28d61e 100644
--- a/crates/typst-library/src/compute/data.rs
+++ b/crates/typst-library/src/compute/data.rs
@@ -37,7 +37,7 @@ pub fn read(
vm: &mut Vm,
) -> SourceResult<Readable> {
let Spanned { v: path, span } = path;
- let id = vm.location().join(&path).at(span)?;
+ let id = vm.resolve_path(&path).at(span)?;
let data = vm.world().file(id).at(span)?;
Ok(match encoding {
None => Readable::Bytes(data),
@@ -130,7 +130,7 @@ pub fn csv(
vm: &mut Vm,
) -> SourceResult<Array> {
let Spanned { v: path, span } = path;
- let id = vm.location().join(&path).at(span)?;
+ let id = vm.resolve_path(&path).at(span)?;
let data = vm.world().file(id).at(span)?;
csv_decode(Spanned::new(Readable::Bytes(data), span), delimiter)
}
@@ -262,7 +262,7 @@ pub fn json(
vm: &mut Vm,
) -> SourceResult<Value> {
let Spanned { v: path, span } = path;
- let id = vm.location().join(&path).at(span)?;
+ let id = vm.resolve_path(&path).at(span)?;
let data = vm.world().file(id).at(span)?;
json_decode(Spanned::new(Readable::Bytes(data), span))
}
@@ -350,7 +350,7 @@ pub fn toml(
vm: &mut Vm,
) -> SourceResult<Value> {
let Spanned { v: path, span } = path;
- let id = vm.location().join(&path).at(span)?;
+ let id = vm.resolve_path(&path).at(span)?;
let data = vm.world().file(id).at(span)?;
toml_decode(Spanned::new(Readable::Bytes(data), span))
@@ -462,7 +462,7 @@ pub fn yaml(
vm: &mut Vm,
) -> SourceResult<Value> {
let Spanned { v: path, span } = path;
- let id = vm.location().join(&path).at(span)?;
+ let id = vm.resolve_path(&path).at(span)?;
let data = vm.world().file(id).at(span)?;
yaml_decode(Spanned::new(Readable::Bytes(data), span))
}
@@ -568,7 +568,7 @@ pub fn xml(
vm: &mut Vm,
) -> SourceResult<Value> {
let Spanned { v: path, span } = path;
- let id = vm.location().join(&path).at(span)?;
+ let id = vm.resolve_path(&path).at(span)?;
let data = vm.world().file(id).at(span)?;
xml_decode(Spanned::new(Readable::Bytes(data), span))
}
diff --git a/crates/typst-library/src/meta/bibliography.rs b/crates/typst-library/src/meta/bibliography.rs
index ed51058d..164b1c11 100644
--- a/crates/typst-library/src/meta/bibliography.rs
+++ b/crates/typst-library/src/meta/bibliography.rs
@@ -58,7 +58,7 @@ pub struct BibliographyElem {
let data = paths.0
.iter()
.map(|path| {
- let id = vm.location().join(path).at(span)?;
+ let id = vm.resolve_path(path).at(span)?;
vm.world().file(id).at(span)
})
.collect::<SourceResult<Vec<Bytes>>>()?;
diff --git a/crates/typst-library/src/text/raw.rs b/crates/typst-library/src/text/raw.rs
index 063461a4..1b4a4223 100644
--- a/crates/typst-library/src/text/raw.rs
+++ b/crates/typst-library/src/text/raw.rs
@@ -490,7 +490,7 @@ fn parse_syntaxes(
.0
.iter()
.map(|path| {
- let id = vm.location().join(path).at(span)?;
+ let id = vm.resolve_path(path).at(span)?;
vm.world().file(id).at(span)
})
.collect::<SourceResult<Vec<Bytes>>>()?;
@@ -522,7 +522,7 @@ fn parse_theme(
};
// Load theme file.
- let id = vm.location().join(&path).at(span)?;
+ let id = vm.resolve_path(&path).at(span)?;
let data = vm.world().file(id).at(span)?;
// Check that parsing works.
diff --git a/crates/typst-library/src/visualize/image.rs b/crates/typst-library/src/visualize/image.rs
index 6ee930a7..5e6ac2d2 100644
--- a/crates/typst-library/src/visualize/image.rs
+++ b/crates/typst-library/src/visualize/image.rs
@@ -44,7 +44,7 @@ pub struct ImageElem {
#[parse(
let Spanned { v: path, span } =
args.expect::<Spanned<EcoString>>("path to image file")?;
- let id = vm.location().join(&path).at(span)?;
+ let id = vm.resolve_path(&path).at(span)?;
let data = vm.world().file(id).at(span)?;
path
)]
diff --git a/crates/typst-syntax/src/file.rs b/crates/typst-syntax/src/file.rs
index 6b3117cf..f5fa493b 100644
--- a/crates/typst-syntax/src/file.rs
+++ b/crates/typst-syntax/src/file.rs
@@ -16,9 +16,6 @@ use super::is_ident;
static INTERNER: Lazy<RwLock<Interner>> =
Lazy::new(|| RwLock::new(Interner { to_id: HashMap::new(), from_id: Vec::new() }));
-/// The path that we use for detached file ids.
-static DETACHED_PATH: Lazy<VirtualPath> = Lazy::new(|| VirtualPath::new("/unknown"));
-
/// A package-path interner.
struct Interner {
to_id: HashMap<Pair, FileId>,
@@ -48,66 +45,41 @@ impl FileId {
}
let mut interner = INTERNER.write().unwrap();
- let len = interner.from_id.len();
- if len >= usize::from(u16::MAX) {
- panic!("too many file specifications");
- }
+ let num = interner.from_id.len().try_into().expect("out of file ids");
// Create a new entry forever by leaking the pair. We can't leak more
// than 2^16 pair (and typically will leak a lot less), so its not a
// big deal.
- let id = FileId(len as u16);
+ let id = FileId(num);
let leaked = Box::leak(Box::new(pair));
interner.to_id.insert(leaked, id);
interner.from_id.push(leaked);
id
}
- /// Get an id that does not identify any real file.
- pub const fn detached() -> Self {
- Self(u16::MAX)
- }
-
- /// Whether the id is the detached.
- pub const fn is_detached(self) -> bool {
- self.0 == Self::detached().0
- }
-
/// The package the file resides in, if any.
pub fn package(&self) -> Option<&'static PackageSpec> {
- if self.is_detached() {
- None
- } else {
- self.pair().0.as_ref()
- }
+ self.pair().0.as_ref()
}
/// The absolute and normalized path to the file _within_ the project or
/// package.
pub fn vpath(&self) -> &'static VirtualPath {
- if self.is_detached() {
- &DETACHED_PATH
- } else {
- &self.pair().1
- }
+ &self.pair().1
}
/// Resolve a file location relative to this file.
- pub fn join(self, path: &str) -> Result<Self, EcoString> {
- if self.is_detached() {
- Err("cannot access file system from here")?;
- }
-
- Ok(Self::new(self.package().cloned(), self.vpath().join(path)))
+ pub fn join(self, path: &str) -> Self {
+ Self::new(self.package().cloned(), self.vpath().join(path))
}
/// Construct from a raw number.
- pub(crate) const fn from_u16(v: u16) -> Self {
+ pub(crate) const fn from_raw(v: u16) -> Self {
Self(v)
}
/// Extract the raw underlying number.
- pub(crate) const fn as_u16(self) -> u16 {
+ pub(crate) const fn into_raw(self) -> u16 {
self.0
}
diff --git a/crates/typst-syntax/src/node.rs b/crates/typst-syntax/src/node.rs
index 8e4e056e..77c73f58 100644
--- a/crates/typst-syntax/src/node.rs
+++ b/crates/typst-syntax/src/node.rs
@@ -202,7 +202,7 @@ impl SyntaxNode {
return Err(Unnumberable);
}
- let mid = Span::new(id, (within.start + within.end) / 2);
+ let mid = Span::new(id, (within.start + within.end) / 2).unwrap();
match &mut self.0 {
Repr::Leaf(leaf) => leaf.span = mid,
Repr::Inner(inner) => Arc::make_mut(inner).numberize(id, None, within)?,
@@ -424,7 +424,7 @@ impl InnerNode {
let mut start = within.start;
if range.is_none() {
let end = start + stride;
- self.span = Span::new(id, (start + end) / 2);
+ self.span = Span::new(id, (start + end) / 2).unwrap();
self.upper = within.end;
start = end;
}
@@ -448,6 +448,7 @@ impl InnerNode {
mut range: Range<usize>,
replacement: Vec<SyntaxNode>,
) -> NumberingResult {
+ let Some(id) = self.span.id() else { return Err(Unnumberable) };
let superseded = &self.children[range.clone()];
// Compute the new byte length.
@@ -505,7 +506,6 @@ impl InnerNode {
// Try to renumber.
let within = start_number..end_number;
- let id = self.span.id();
if self.numberize(id, Some(renumber), within).is_ok() {
return Ok(());
}
diff --git a/crates/typst-syntax/src/reparser.rs b/crates/typst-syntax/src/reparser.rs
index a4186fa7..e03e1619 100644
--- a/crates/typst-syntax/src/reparser.rs
+++ b/crates/typst-syntax/src/reparser.rs
@@ -21,7 +21,9 @@ pub fn reparse(
try_reparse(text, replaced, replacement_len, None, root, 0).unwrap_or_else(|| {
let id = root.span().id();
*root = parse(text);
- root.numberize(id, Span::FULL).unwrap();
+ if let Some(id) = id {
+ root.numberize(id, Span::FULL).unwrap();
+ }
0..text.len()
})
}
diff --git a/crates/typst-syntax/src/source.rs b/crates/typst-syntax/src/source.rs
index 036499ab..56b27195 100644
--- a/crates/typst-syntax/src/source.rs
+++ b/crates/typst-syntax/src/source.rs
@@ -9,6 +9,7 @@ use comemo::Prehashed;
use super::reparser::reparse;
use super::{is_newline, parse, FileId, LinkedNode, Span, SyntaxNode};
+use crate::VirtualPath;
/// A source file.
///
@@ -44,19 +45,7 @@ impl Source {
/// Create a source file without a real id and path, usually for testing.
pub fn detached(text: impl Into<String>) -> Self {
- Self::new(FileId::detached(), text.into())
- }
-
- /// Create a source file with the same synthetic span for all nodes.
- pub fn synthesized(text: String, span: Span) -> Self {
- let mut root = parse(&text);
- root.synthesize(span);
- Self(Arc::new(Repr {
- id: FileId::detached(),
- lines: lines(&text),
- text: Prehashed::new(text),
- root: Prehashed::new(root),
- }))
+ Self::new(FileId::new(None, VirtualPath::new("main.typ")), text.into())
}
/// The root node of the file's untyped syntax tree.
@@ -151,12 +140,9 @@ impl Source {
/// Get the byte range for the given span in this file.
///
- /// Panics if the span does not point into this source file.
- #[track_caller]
- pub fn range(&self, span: Span) -> Range<usize> {
- self.find(span)
- .expect("span does not point into this source file")
- .range()
+ /// Returns `None` if the span does not point into this source file.
+ pub fn range(&self, span: Span) -> Option<Range<usize>> {
+ Some(self.find(span)?.range())
}
/// Return the index of the UTF-16 code unit at the byte index.
diff --git a/crates/typst-syntax/src/span.rs b/crates/typst-syntax/src/span.rs
index 8715e476..d715af1c 100644
--- a/crates/typst-syntax/src/span.rs
+++ b/crates/typst-syntax/src/span.rs
@@ -28,55 +28,58 @@ pub struct Span(NonZeroU64);
impl Span {
/// The full range of numbers available for span numbering.
- pub const FULL: Range<u64> = 2..(1 << Self::BITS);
+ pub(super) const FULL: Range<u64> = 2..(1 << Self::BITS);
+
+ /// The value reserved for the detached span.
const DETACHED: u64 = 1;
- // Data layout:
- // | 16 bits source id | 48 bits number |
+ /// Data layout:
+ /// | 16 bits source id | 48 bits number |
const BITS: usize = 48;
/// Create a new span from a source id and a unique number.
///
- /// Panics if the `number` is not contained in `FULL`.
- #[track_caller]
- pub const fn new(id: FileId, number: u64) -> Self {
- assert!(
- Self::FULL.start <= number && number < Self::FULL.end,
- "span number outside valid range"
- );
-
- Self::pack(id, number)
- }
+ /// Returns `None` if `number` is not contained in `FULL`.
+ pub(super) const fn new(id: FileId, number: u64) -> Option<Self> {
+ if number < Self::FULL.start || number >= Self::FULL.end {
+ return None;
+ }
- /// A span that does not point into any source file.
- pub const fn detached() -> Self {
- Self::pack(FileId::detached(), Self::DETACHED)
+ let bits = ((id.into_raw() as u64) << Self::BITS) | number;
+ match NonZeroU64::new(bits) {
+ Some(v) => Some(Self(v)),
+ None => unreachable!(),
+ }
}
- /// Pack the components into a span.
- #[track_caller]
- const fn pack(id: FileId, number: u64) -> Span {
- let bits = ((id.as_u16() as u64) << Self::BITS) | number;
- match NonZeroU64::new(bits) {
+ /// Create a span that does not point into any source file.
+ pub const fn detached() -> Self {
+ match NonZeroU64::new(Self::DETACHED) {
Some(v) => Self(v),
- None => panic!("span encoding is zero"),
+ None => unreachable!(),
}
}
+ /// Whether the span is detached.
+ pub const fn is_detached(self) -> bool {
+ self.0.get() == Self::DETACHED
+ }
+
/// The id of the source file the span points into.
- pub const fn id(self) -> FileId {
- FileId::from_u16((self.0.get() >> Self::BITS) as u16)
+ ///
+ /// Returns `None` if the span is detached.
+ pub const fn id(self) -> Option<FileId> {
+ if self.is_detached() {
+ return None;
+ }
+ let bits = (self.0.get() >> Self::BITS) as u16;
+ Some(FileId::from_raw(bits))
}
- /// The unique number of the span within its source file.
+ /// The unique number of the span within its [`Source`](super::Source).
pub const fn number(self) -> u64 {
self.0.get() & ((1 << Self::BITS) - 1)
}
-
- /// Whether the span is detached.
- pub const fn is_detached(self) -> bool {
- self.id().is_detached()
- }
}
/// A value with a span locating it in the source code.
@@ -120,9 +123,9 @@ mod tests {
#[test]
fn test_span_encoding() {
- let id = FileId::from_u16(5);
- let span = Span::new(id, 10);
- assert_eq!(span.id(), id);
+ let id = FileId::from_raw(5);
+ let span = Span::new(id, 10).unwrap();
+ assert_eq!(span.id(), Some(id));
assert_eq!(span.number(), 10);
}
}
diff --git a/crates/typst/src/diag.rs b/crates/typst/src/diag.rs
index 99c61608..7c298ec0 100644
--- a/crates/typst/src/diag.rs
+++ b/crates/typst/src/diag.rs
@@ -9,7 +9,7 @@ use std::string::FromUtf8Error;
use comemo::Tracked;
use crate::syntax::{PackageSpec, Span, Spanned, SyntaxError};
-use crate::World;
+use crate::{World, WorldExt};
/// Early-return with a [`StrResult`] or [`SourceResult`].
///
@@ -206,16 +206,12 @@ impl<T> Trace<T> for SourceResult<T> {
F: Fn() -> Tracepoint,
{
self.map_err(|mut errors| {
- if span.is_detached() {
- return errors;
- }
-
- let trace_range = world.range(span);
- for error in errors.iter_mut().filter(|e| !e.span.is_detached()) {
+ let Some(trace_range) = world.range(span) else { return errors };
+ for error in errors.iter_mut() {
// Skip traces that surround the error.
- if error.span.id() == span.id() {
- let error_range = world.range(error.span);
- if trace_range.start <= error_range.start
+ if let Some(error_range) = world.range(error.span) {
+ if error.span.id() == span.id()
+ && trace_range.start <= error_range.start
&& trace_range.end >= error_range.end
{
continue;
diff --git a/crates/typst/src/eval/func.rs b/crates/typst/src/eval/func.rs
index d8117159..d185b319 100644
--- a/crates/typst/src/eval/func.rs
+++ b/crates/typst/src/eval/func.rs
@@ -94,9 +94,8 @@ impl Func {
}
Repr::Closure(closure) => {
// Determine the route inside the closure.
- let fresh = Route::new(closure.location);
- let route =
- if vm.location.is_detached() { fresh.track() } else { vm.route };
+ let fresh = Route::new(closure.file);
+ let route = if vm.file.is_none() { fresh.track() } else { vm.route };
Closure::call(
self,
@@ -134,7 +133,7 @@ impl Func {
delayed: TrackedMut::reborrow_mut(&mut vt.delayed),
tracer: TrackedMut::reborrow_mut(&mut vt.tracer),
};
- let mut vm = Vm::new(vt, route.track(), FileId::detached(), scopes);
+ let mut vm = Vm::new(vt, route.track(), None, scopes);
let args = Args::new(self.span(), args);
self.call_vm(&mut vm, args)
}
@@ -298,7 +297,7 @@ pub(super) struct Closure {
/// The closure's syntax node. Must be castable to `ast::Closure`.
pub node: SyntaxNode,
/// The source file where the closure was defined.
- pub location: FileId,
+ pub file: Option<FileId>,
/// Default values of named parameters.
pub defaults: Vec<Value>,
/// Captured values from outer scopes.
@@ -351,7 +350,7 @@ impl Closure {
};
// Prepare VM.
- let mut vm = Vm::new(vt, route, this.location, scopes);
+ let mut vm = Vm::new(vt, route, this.file, scopes);
vm.depth = depth;
// Provide the closure itself for recursive calls.
diff --git a/crates/typst/src/eval/mod.rs b/crates/typst/src/eval/mod.rs
index 4eb8d122..b7166992 100644
--- a/crates/typst/src/eval/mod.rs
+++ b/crates/typst/src/eval/mod.rs
@@ -122,7 +122,7 @@ pub fn eval(
// Prepare VM.
let route = Route::insert(route, id);
let scopes = Scopes::new(Some(library));
- let mut vm = Vm::new(vt, route.track(), id, scopes);
+ let mut vm = Vm::new(vt, route.track(), Some(id), scopes);
let root = source.root();
let errors = root.errors();
@@ -189,9 +189,8 @@ pub fn eval_string(
// Prepare VM.
let route = Route::default();
- let id = FileId::detached();
let scopes = Scopes::new(Some(world.library()));
- let mut vm = Vm::new(vt, route.track(), id, scopes);
+ let mut vm = Vm::new(vt, route.track(), None, scopes);
vm.scopes.scopes.push(scope);
// Evaluate the code.
@@ -235,8 +234,8 @@ pub struct Vm<'a> {
items: LangItems,
/// The route of source ids the VM took to reach its current location.
route: Tracked<'a, Route<'a>>,
- /// The current location.
- location: FileId,
+ /// The id of the currently evaluated file.
+ file: Option<FileId>,
/// A control flow event that is currently happening.
flow: Option<FlowEvent>,
/// The stack of scopes.
@@ -252,16 +251,16 @@ impl<'a> Vm<'a> {
fn new(
vt: Vt<'a>,
route: Tracked<'a, Route>,
- location: FileId,
+ file: Option<FileId>,
scopes: Scopes<'a>,
) -> Self {
- let traced = vt.tracer.span(location);
+ let traced = file.and_then(|id| vt.tracer.span(id));
let items = vt.world.library().items.clone();
Self {
vt,
items,
route,
- location,
+ file,
flow: None,
scopes,
depth: 0,
@@ -274,9 +273,21 @@ impl<'a> Vm<'a> {
self.vt.world
}
- /// The location to which paths are relative currently.
- pub fn location(&self) -> FileId {
- self.location
+ /// The id of the currently evaluated file.
+ ///
+ /// Returns `None` if the VM is in a detached context, e.g. when evaluating
+ /// a user-provided string.
+ pub fn file(&self) -> Option<FileId> {
+ self.file
+ }
+
+ /// Resolve a path relative to the currently evaluated file.
+ pub fn resolve_path(&self, path: &str) -> StrResult<FileId> {
+ let Some(file) = self.file else {
+ bail!("cannot access file system from here");
+ };
+
+ Ok(file.join(path))
}
/// Define a variable in the current scope.
@@ -331,8 +342,8 @@ pub struct Route<'a> {
impl<'a> Route<'a> {
/// Create a new route with just one entry.
- pub fn new(id: FileId) -> Self {
- Self { id: Some(id), outer: None }
+ pub fn new(id: Option<FileId>) -> Self {
+ Self { id, outer: None }
}
/// Insert a new id into the route.
@@ -1301,7 +1312,7 @@ impl Eval for ast::Closure<'_> {
// Define the closure.
let closure = Closure {
node: self.to_untyped().clone(),
- location: vm.location,
+ file: vm.file,
defaults,
captured,
};
@@ -1809,7 +1820,7 @@ fn import_package(vm: &mut Vm, spec: PackageSpec, span: Span) -> SourceResult<Mo
manifest.validate(&spec).at(span)?;
// Evaluate the entry point.
- let entrypoint_id = manifest_id.join(&manifest.package.entrypoint).at(span)?;
+ let entrypoint_id = manifest_id.join(&manifest.package.entrypoint);
let source = vm.world().source(entrypoint_id).at(span)?;
let point = || Tracepoint::Import;
Ok(eval(vm.world(), vm.route, TrackedMut::reborrow_mut(&mut vm.vt.tracer), &source)
@@ -1821,7 +1832,7 @@ fn import_package(vm: &mut Vm, spec: PackageSpec, span: Span) -> SourceResult<Mo
fn import_file(vm: &mut Vm, path: &str, span: Span) -> SourceResult<Module> {
// Load the source file.
let world = vm.world();
- let id = vm.location().join(path).at(span)?;
+ let id = vm.resolve_path(path).at(span)?;
let source = world.source(id).at(span)?;
// Prevent cyclic importing.
diff --git a/crates/typst/src/eval/tracer.rs b/crates/typst/src/eval/tracer.rs
index 0be6c189..22e67fb1 100644
--- a/crates/typst/src/eval/tracer.rs
+++ b/crates/typst/src/eval/tracer.rs
@@ -45,7 +45,7 @@ impl Tracer {
impl Tracer {
/// The traced span if it is part of the given source file.
pub fn span(&self, id: FileId) -> Option<Span> {
- if self.span.map(Span::id) == Some(id) {
+ if self.span.and_then(Span::id) == Some(id) {
self.span
} else {
None
diff --git a/crates/typst/src/ide/analyze.rs b/crates/typst/src/ide/analyze.rs
index c143720a..2e64069e 100644
--- a/crates/typst/src/ide/analyze.rs
+++ b/crates/typst/src/ide/analyze.rs
@@ -46,7 +46,7 @@ pub fn analyze_expr(world: &dyn World, node: &LinkedNode) -> EcoVec<Value> {
pub fn analyze_import(world: &dyn World, source: &Source, path: &str) -> Option<Module> {
let route = Route::default();
let mut tracer = Tracer::default();
- let id = source.id().join(path).ok()?;
+ let id = source.id().join(path);
let source = world.source(id).ok()?;
eval(world.track(), route.track(), tracer.track_mut(), &source).ok()
}
diff --git a/crates/typst/src/ide/highlight.rs b/crates/typst/src/ide/highlight.rs
index c9748e92..037106e4 100644
--- a/crates/typst/src/ide/highlight.rs
+++ b/crates/typst/src/ide/highlight.rs
@@ -379,7 +379,7 @@ mod tests {
use std::ops::Range;
use super::*;
- use crate::syntax::Source;
+ use crate::syntax::parse;
#[test]
fn test_highlighting() {
@@ -388,8 +388,8 @@ mod tests {
#[track_caller]
fn test(text: &str, goal: &[(Range<usize>, Tag)]) {
let mut vec = vec![];
- let source = Source::detached(text);
- highlight_tree(&mut vec, &LinkedNode::new(source.root()));
+ let root = parse(text);
+ highlight_tree(&mut vec, &LinkedNode::new(&root));
assert_eq!(vec, goal);
}
diff --git a/crates/typst/src/ide/jump.rs b/crates/typst/src/ide/jump.rs
index b2aeab9d..34d51c07 100644
--- a/crates/typst/src/ide/jump.rs
+++ b/crates/typst/src/ide/jump.rs
@@ -21,9 +21,10 @@ pub enum Jump {
impl Jump {
fn from_span(world: &dyn World, span: Span) -> Option<Self> {
- let source = world.source(span.id()).ok()?;
+ let id = span.id()?;
+ let source = world.source(id).ok()?;
let node = source.find(span)?;
- Some(Self::Source(span.id(), node.offset()))
+ Some(Self::Source(id, node.offset()))
}
}
@@ -67,18 +68,15 @@ pub fn jump_from_click(
FrameItem::Text(text) => {
for glyph in &text.glyphs {
- let (span, span_offset) = glyph.span;
- if span.is_detached() {
- continue;
- }
-
let width = glyph.x_advance.at(text.size);
if is_in_rect(
Point::new(pos.x, pos.y - text.size),
Size::new(width, text.size),
click,
) {
- let source = world.source(span.id()).ok()?;
+ let (span, span_offset) = glyph.span;
+ let Some(id) = span.id() else { continue };
+ let source = world.source(id).ok()?;
let node = source.find(span)?;
let pos = if node.kind() == SyntaxKind::Text {
let range = node.range();
diff --git a/crates/typst/src/lib.rs b/crates/typst/src/lib.rs
index 514aa25e..8d7393a0 100644
--- a/crates/typst/src/lib.rs
+++ b/crates/typst/src/lib.rs
@@ -142,12 +142,18 @@ pub trait World {
fn packages(&self) -> &[(PackageSpec, Option<EcoString>)] {
&[]
}
+}
+/// Helper methods on [`World`] implementations.
+pub trait WorldExt {
/// Get the byte range for a span.
- #[track_caller]
- fn range(&self, span: Span) -> Range<usize> {
- self.source(span.id())
- .expect("span does not point into any source file")
- .range(span)
+ ///
+ /// Returns `None` if the `Span` does not point into any source file.
+ fn range(&self, span: Span) -> Option<Range<usize>>;
+}
+
+impl<T: World> WorldExt for T {
+ fn range(&self, span: Span) -> Option<Range<usize>> {
+ self.source(span.id()?).ok()?.range(span)
}
}
diff --git a/crates/typst/src/model/mod.rs b/crates/typst/src/model/mod.rs
index 69dc6a6b..9a091784 100644
--- a/crates/typst/src/model/mod.rs
+++ b/crates/typst/src/model/mod.rs
@@ -31,6 +31,7 @@ use comemo::{Track, Tracked, TrackedMut, Validate};
use crate::diag::{warning, SourceDiagnostic, SourceResult};
use crate::doc::Document;
use crate::eval::Tracer;
+use crate::syntax::Span;
use crate::World;
/// Typeset content into a fully layouted document.
@@ -88,11 +89,8 @@ pub fn typeset(
if iter >= 5 {
tracer.warn(
- warning!(
- world.main().root().span(),
- "layout did not converge within 5 attempts",
- )
- .with_hint("check if any states or queries are updating themselves"),
+ warning!(Span::detached(), "layout did not converge within 5 attempts",)
+ .with_hint("check if any states or queries are updating themselves"),
);
break;
}