Compare commits

...

13 Commits

Author SHA1 Message Date
Eric Biedert
a6f99ebdcc
Merge bef4e20434334d450a9d3cf3a41ada9c6cde1535 into b036fd97ab2c1cdc003b50eea7850279a1a7601f 2025-07-22 00:23:47 +09:00
Laurenz
b036fd97ab Reformat with 2024 edition 2025-07-21 15:22:29 +02:00
Laurenz
e81a5a6ef2 Yeet if_chain macro 2025-07-21 15:22:29 +02:00
Laurenz
c9c2315ad3 Fix 2024 clippy warnings 2025-07-21 15:22:29 +02:00
Laurenz
4bbd4e195b Migrate to 2024 edition 2025-07-21 15:22:28 +02:00
Laurenz
eed75ca4d6 Bump MSRV to 1.88 2025-07-21 15:22:28 +02:00
Laurenz
a43b7e785c Bump CI Rust to 1.88 2025-07-21 13:48:48 +02:00
pog102
55dad02887
Add Lithuanian translations (#6587) 2025-07-21 10:57:40 +00:00
Eric Biedert
bef4e20434 Add test for location of migrated block
Previously, this would result in a position on the first page.
2025-05-28 13:03:17 +02:00
Eric Biedert
811996eb70 Update references of existing tests
In `grid-header-containing-rowspan`, the first region is now correctly
not stroked.

Not sure what happened in `grid-header-orphan-prevention`, but the "B"
in the first header was too bold before.
2025-05-27 15:27:04 +02:00
Eric Biedert
02f07e7912 Don't label empty orphan frames
Adding a label makes a previously empty frame non-empty, but we want to
keep orphans empty.
2025-05-27 15:27:04 +02:00
Eric Biedert
693edb475d Don't break blocks after empty frame
Instead, spill the whole child into the next region to prevent small
leftovers to influence layout. This is not done when all frames are
empty (e.g. for an explicitly sized block without content or fill).

This helps with the following cases:
- Previously, if a sticky block was followed by a leftover frame, the
  stickiness would be ignored, as the leftover was in fact sticking.
  This is not currently a problem, as sticky blocks aren't really
  breakable at the moment, but probably will be in the future.
- When ignoring stroke and fill for a first empty frame, a nested broken
  block would previously make the first frame not be considered empty
  anymore, which would lead to the leftover frame being filled.
- Similarly, when the fill of an explicitly sized block is ignored in
  the first empty frame, the leftover part would still be considered as
  laid out, making the actually visible block too small.
2025-05-27 15:21:15 +02:00
Eric Biedert
606183cd30 Add tests 2025-05-27 15:21:15 +02:00
281 changed files with 1388 additions and 1507 deletions

View File

@ -40,7 +40,7 @@ jobs:
sudo dpkg --add-architecture i386 sudo dpkg --add-architecture i386
sudo apt update sudo apt update
sudo apt install -y gcc-multilib libssl-dev:i386 pkg-config:i386 sudo apt install -y gcc-multilib libssl-dev:i386 pkg-config:i386
- uses: dtolnay/rust-toolchain@1.87.0 - uses: dtolnay/rust-toolchain@1.88.0
with: with:
targets: ${{ matrix.bits == 32 && 'i686-unknown-linux-gnu' || '' }} targets: ${{ matrix.bits == 32 && 'i686-unknown-linux-gnu' || '' }}
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
@ -73,7 +73,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@1.87.0 - uses: dtolnay/rust-toolchain@1.88.0
with: with:
components: clippy, rustfmt components: clippy, rustfmt
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
@ -88,7 +88,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@1.83.0 - uses: dtolnay/rust-toolchain@1.88.0
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- run: cargo check --workspace - run: cargo check --workspace
@ -99,7 +99,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master - uses: dtolnay/rust-toolchain@master
with: with:
toolchain: nightly-2024-10-29 toolchain: nightly-2025-05-10
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- run: cargo install --locked cargo-fuzz@0.12.0 - run: cargo install --locked cargo-fuzz@0.12.0
- run: cd tests/fuzz && cargo fuzz build --dev - run: cd tests/fuzz && cargo fuzz build --dev
@ -112,6 +112,6 @@ jobs:
- uses: dtolnay/rust-toolchain@master - uses: dtolnay/rust-toolchain@master
with: with:
components: miri components: miri
toolchain: nightly-2024-10-29 toolchain: nightly-2025-05-10
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- run: cargo miri test -p typst-library test_miri - run: cargo miri test -p typst-library test_miri

View File

@ -44,7 +44,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@1.87.0 - uses: dtolnay/rust-toolchain@1.88.0
with: with:
target: ${{ matrix.target }} target: ${{ matrix.target }}

8
Cargo.lock generated
View File

@ -1198,12 +1198,6 @@ dependencies = [
"icu_properties", "icu_properties",
] ]
[[package]]
name = "if_chain"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
[[package]] [[package]]
name = "image" name = "image"
version = "0.25.5" version = "0.25.5"
@ -2943,7 +2937,6 @@ version = "0.13.1"
dependencies = [ dependencies = [
"comemo", "comemo",
"ecow", "ecow",
"if_chain",
"indexmap 2.7.1", "indexmap 2.7.1",
"stacker", "stacker",
"toml", "toml",
@ -2991,7 +2984,6 @@ version = "0.13.1"
dependencies = [ dependencies = [
"comemo", "comemo",
"ecow", "ecow",
"if_chain",
"once_cell", "once_cell",
"pathdiff", "pathdiff",
"serde", "serde",

View File

@ -5,9 +5,9 @@ resolver = "2"
[workspace.package] [workspace.package]
version = "0.13.1" version = "0.13.1"
rust-version = "1.83" # also change in ci.yml rust-version = "1.88" # also change in ci.yml
authors = ["The Typst Project Developers"] authors = ["The Typst Project Developers"]
edition = "2021" edition = "2024"
homepage = "https://typst.app" homepage = "https://typst.app"
repository = "https://github.com/typst/typst" repository = "https://github.com/typst/typst"
license = "Apache-2.0" license = "Apache-2.0"
@ -68,7 +68,6 @@ icu_provider = { version = "1.4", features = ["sync"] }
icu_provider_adapters = "1.4" icu_provider_adapters = "1.4"
icu_provider_blob = "1.4" icu_provider_blob = "1.4"
icu_segmenter = { version = "1.4", features = ["serde"] } icu_segmenter = { version = "1.4", features = ["serde"] }
if_chain = "1"
image = { version = "0.25.5", default-features = false, features = ["png", "jpeg", "gif", "webp"] } image = { version = "0.25.5", default-features = false, features = ["png", "jpeg", "gif", "webp"] }
indexmap = { version = "2", features = ["serde"] } indexmap = { version = "2", features = ["serde"] }
infer = { version = "0.19.0", default-features = false } infer = { version = "0.19.0", default-features = false }

View File

@ -1,10 +1,10 @@
use std::env; use std::env;
use std::fs::{create_dir_all, File}; use std::fs::{File, create_dir_all};
use std::path::Path; use std::path::Path;
use std::process::Command; use std::process::Command;
use clap::{CommandFactory, ValueEnum}; use clap::{CommandFactory, ValueEnum};
use clap_complete::{generate_to, Shell}; use clap_complete::{Shell, generate_to};
use clap_mangen::Man; use clap_mangen::Man;
#[path = "src/args.rs"] #[path = "src/args.rs"]

View File

@ -10,13 +10,13 @@ use ecow::eco_format;
use parking_lot::RwLock; use parking_lot::RwLock;
use pathdiff::diff_paths; use pathdiff::diff_paths;
use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use typst::WorldExt;
use typst::diag::{ use typst::diag::{
bail, At, Severity, SourceDiagnostic, SourceResult, StrResult, Warned, At, Severity, SourceDiagnostic, SourceResult, StrResult, Warned, bail,
}; };
use typst::foundations::{Datetime, Smart}; use typst::foundations::{Datetime, Smart};
use typst::layout::{Frame, Page, PageRanges, PagedDocument}; use typst::layout::{Frame, Page, PageRanges, PagedDocument};
use typst::syntax::{FileId, Lines, Span}; use typst::syntax::{FileId, Lines, Span};
use typst::WorldExt;
use typst_html::HtmlDocument; use typst_html::HtmlDocument;
use typst_pdf::{PdfOptions, PdfStandards, Timestamp}; use typst_pdf::{PdfOptions, PdfStandards, Timestamp};
@ -513,7 +513,9 @@ fn write_make_deps(
}) })
.collect::<Result<Vec<_>, _>>() .collect::<Result<Vec<_>, _>>()
else { else {
bail!("failed to create make dependencies file because output path was not valid unicode") bail!(
"failed to create make dependencies file because output path was not valid unicode"
)
}; };
if output_paths.is_empty() { if output_paths.is_empty() {
bail!("failed to create make dependencies file because output was stdout") bail!("failed to create make dependencies file because output was stdout")

View File

@ -8,8 +8,8 @@ use codespan_reporting::term::termcolor::WriteColor;
use typst::utils::format_duration; use typst::utils::format_duration;
use typst_kit::download::{DownloadState, Downloader, Progress}; use typst_kit::download::{DownloadState, Downloader, Progress};
use crate::terminal::{self, TermOut};
use crate::ARGS; use crate::ARGS;
use crate::terminal::{self, TermOut};
/// Prints download progress by writing `downloading {0}` followed by repeatedly /// Prints download progress by writing `downloading {0}` followed by repeatedly
/// updating the last terminal line. /// updating the last terminal line.

View File

@ -4,7 +4,7 @@ use std::path::Path;
use codespan_reporting::term::termcolor::{Color, ColorSpec, WriteColor}; use codespan_reporting::term::termcolor::{Color, ColorSpec, WriteColor};
use ecow::eco_format; use ecow::eco_format;
use fs_extra::dir::CopyOptions; use fs_extra::dir::CopyOptions;
use typst::diag::{bail, FileError, StrResult}; use typst::diag::{FileError, StrResult, bail};
use typst::syntax::package::{ use typst::syntax::package::{
PackageManifest, PackageSpec, TemplateInfo, VersionlessPackageSpec, PackageManifest, PackageSpec, TemplateInfo, VersionlessPackageSpec,
}; };

View File

@ -21,8 +21,8 @@ use std::io::{self, Write};
use std::process::ExitCode; use std::process::ExitCode;
use std::sync::LazyLock; use std::sync::LazyLock;
use clap::error::ErrorKind;
use clap::Parser; use clap::Parser;
use clap::error::ErrorKind;
use codespan_reporting::term; use codespan_reporting::term;
use codespan_reporting::term::termcolor::WriteColor; use codespan_reporting::term::termcolor::WriteColor;
use typst::diag::HintedStrResult; use typst::diag::HintedStrResult;
@ -102,7 +102,7 @@ fn print_error(msg: &str) -> io::Result<()> {
#[cfg(not(feature = "self-update"))] #[cfg(not(feature = "self-update"))]
mod update { mod update {
use typst::diag::{bail, StrResult}; use typst::diag::{StrResult, bail};
use crate::args::UpdateCommand; use crate::args::UpdateCommand;

View File

@ -1,12 +1,12 @@
use comemo::Track; use comemo::Track;
use ecow::{eco_format, EcoString}; use ecow::{EcoString, eco_format};
use serde::Serialize; use serde::Serialize;
use typst::diag::{bail, HintedStrResult, StrResult, Warned}; use typst::World;
use typst::diag::{HintedStrResult, StrResult, Warned, bail};
use typst::engine::Sink; use typst::engine::Sink;
use typst::foundations::{Content, IntoValue, LocatableSelector, Scope}; use typst::foundations::{Content, IntoValue, LocatableSelector, Scope};
use typst::layout::PagedDocument; use typst::layout::PagedDocument;
use typst::syntax::{Span, SyntaxMode}; use typst::syntax::{Span, SyntaxMode};
use typst::World;
use typst_eval::eval_string; use typst_eval::eval_string;
use crate::args::{QueryCommand, SerializationFormat}; use crate::args::{QueryCommand, SerializationFormat};

View File

@ -5,7 +5,7 @@ use std::sync::Arc;
use ecow::eco_format; use ecow::eco_format;
use parking_lot::{Condvar, Mutex, MutexGuard}; use parking_lot::{Condvar, Mutex, MutexGuard};
use tiny_http::{Header, Request, Response, StatusCode}; use tiny_http::{Header, Request, Response, StatusCode};
use typst::diag::{bail, StrResult}; use typst::diag::{StrResult, bail};
use crate::args::{Input, ServerArgs}; use crate::args::{Input, ServerArgs};
@ -162,7 +162,7 @@ impl<T> Bucket<T> {
} }
/// Retrieves the current data in the bucket. /// Retrieves the current data in the bucket.
fn get(&self) -> MutexGuard<T> { fn get(&self) -> MutexGuard<'_, T> {
self.mutex.lock() self.mutex.lock()
} }

View File

@ -2,9 +2,9 @@ use std::fs::File;
use std::io::BufWriter; use std::io::BufWriter;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use typst::diag::{bail, StrResult};
use typst::syntax::Span;
use typst::World; use typst::World;
use typst::diag::{StrResult, bail};
use typst::syntax::Span;
use crate::args::{CliArguments, Command}; use crate::args::{CliArguments, Command};
use crate::world::SystemWorld; use crate::world::SystemWorld;

View File

@ -6,7 +6,7 @@ use ecow::eco_format;
use semver::Version; use semver::Version;
use serde::Deserialize; use serde::Deserialize;
use tempfile::NamedTempFile; use tempfile::NamedTempFile;
use typst::diag::{bail, StrResult}; use typst::diag::{StrResult, bail};
use typst_kit::download::Downloader; use typst_kit::download::Downloader;
use xz2::bufread::XzDecoder; use xz2::bufread::XzDecoder;
use zip::ZipArchive; use zip::ZipArchive;

View File

@ -10,12 +10,12 @@ use codespan_reporting::term::{self, termcolor};
use ecow::eco_format; use ecow::eco_format;
use notify::{Event, RecommendedWatcher, RecursiveMode, Watcher as _}; use notify::{Event, RecommendedWatcher, RecursiveMode, Watcher as _};
use same_file::is_same_file; use same_file::is_same_file;
use typst::diag::{bail, warning, StrResult}; use typst::diag::{StrResult, bail, warning};
use typst::syntax::Span; use typst::syntax::Span;
use typst::utils::format_duration; use typst::utils::format_duration;
use crate::args::{Input, Output, WatchCommand}; use crate::args::{Input, Output, WatchCommand};
use crate::compile::{compile_once, print_diagnostics, CompileConfig}; use crate::compile::{CompileConfig, compile_once, print_diagnostics};
use crate::timings::Timer; use crate::timings::Timer;
use crate::world::{SystemWorld, WorldCreationError}; use crate::world::{SystemWorld, WorldCreationError};
use crate::{print_error, terminal}; use crate::{print_error, terminal};

View File

@ -5,7 +5,7 @@ use std::sync::{LazyLock, OnceLock};
use std::{fmt, fs, io, mem}; use std::{fmt, fs, io, mem};
use chrono::{DateTime, Datelike, FixedOffset, Local, Utc}; use chrono::{DateTime, Datelike, FixedOffset, Local, Utc};
use ecow::{eco_format, EcoString}; use ecow::{EcoString, eco_format};
use parking_lot::Mutex; use parking_lot::Mutex;
use typst::diag::{FileError, FileResult}; use typst::diag::{FileError, FileResult};
use typst::foundations::{Bytes, Datetime, Dict, IntoValue}; use typst::foundations::{Bytes, Datetime, Dict, IntoValue};
@ -361,22 +361,22 @@ impl<T: Clone> SlotCell<T> {
f: impl FnOnce(Vec<u8>, Option<T>) -> FileResult<T>, f: impl FnOnce(Vec<u8>, Option<T>) -> FileResult<T>,
) -> FileResult<T> { ) -> FileResult<T> {
// If we accessed the file already in this compilation, retrieve it. // If we accessed the file already in this compilation, retrieve it.
if mem::replace(&mut self.accessed, true) { if mem::replace(&mut self.accessed, true)
if let Some(data) = &self.data { && let Some(data) = &self.data
{
return data.clone(); return data.clone();
} }
}
// Read and hash the file. // Read and hash the file.
let result = timed!("loading file", load()); let result = timed!("loading file", load());
let fingerprint = timed!("hashing file", typst::utils::hash128(&result)); let fingerprint = timed!("hashing file", typst::utils::hash128(&result));
// If the file contents didn't change, yield the old processed data. // If the file contents didn't change, yield the old processed data.
if mem::replace(&mut self.fingerprint, fingerprint) == fingerprint { if mem::replace(&mut self.fingerprint, fingerprint) == fingerprint
if let Some(data) = &self.data { && let Some(data) = &self.data
{
return data.clone(); return data.clone();
} }
}
let prev = self.data.take().and_then(Result::ok); let prev = self.data.take().and_then(Result::ok);
let value = result.and_then(|data| f(data, prev)); let value = result.and_then(|data| f(data, prev));

View File

@ -20,7 +20,6 @@ typst-timing = { workspace = true }
typst-utils = { workspace = true } typst-utils = { workspace = true }
comemo = { workspace = true } comemo = { workspace = true }
ecow = { workspace = true } ecow = { workspace = true }
if_chain = { workspace = true }
indexmap = { workspace = true } indexmap = { workspace = true }
toml = { workspace = true } toml = { workspace = true }
unicode-segmentation = { workspace = true } unicode-segmentation = { workspace = true }

View File

@ -1,9 +1,9 @@
use ecow::eco_format; use ecow::eco_format;
use typst_library::diag::{bail, At, Hint, SourceResult, Trace, Tracepoint}; use typst_library::diag::{At, Hint, SourceResult, Trace, Tracepoint, bail};
use typst_library::foundations::{Dict, Value}; use typst_library::foundations::{Dict, Value};
use typst_syntax::ast::{self, AstNode}; use typst_syntax::ast::{self, AstNode};
use crate::{call_method_access, is_accessor_method, Eval, Vm}; use crate::{Eval, Vm, call_method_access, is_accessor_method};
/// Access an expression mutably. /// Access an expression mutably.
pub(crate) trait Access { pub(crate) trait Access {
@ -29,11 +29,11 @@ impl Access for ast::Expr<'_> {
impl Access for ast::Ident<'_> { impl Access for ast::Ident<'_> {
fn access<'a>(self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> { fn access<'a>(self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
let span = self.span(); let span = self.span();
if vm.inspected == Some(span) { if vm.inspected == Some(span)
if let Ok(binding) = vm.scopes.get(&self) { && let Ok(binding) = vm.scopes.get(&self)
{
vm.trace(binding.read().clone()); vm.trace(binding.read().clone());
} }
}
vm.scopes vm.scopes
.get_mut(&self) .get_mut(&self)
.and_then(|b| b.write().map_err(Into::into)) .and_then(|b| b.write().map_err(Into::into))

View File

@ -1,7 +1,7 @@
use std::collections::HashSet; use std::collections::HashSet;
use ecow::eco_format; use ecow::eco_format;
use typst_library::diag::{bail, error, At, SourceDiagnostic, SourceResult}; use typst_library::diag::{At, SourceDiagnostic, SourceResult, bail, error};
use typst_library::foundations::{Array, Dict, Value}; use typst_library::foundations::{Array, Dict, Value};
use typst_syntax::ast::{self, AstNode}; use typst_syntax::ast::{self, AstNode};

View File

@ -1,8 +1,9 @@
use comemo::{Tracked, TrackedMut}; use comemo::{Tracked, TrackedMut};
use ecow::{eco_format, EcoString, EcoVec}; use ecow::{EcoString, EcoVec, eco_format};
use typst_library::World;
use typst_library::diag::{ use typst_library::diag::{
bail, error, At, HintedStrResult, HintedString, SourceDiagnostic, SourceResult, At, HintedStrResult, HintedString, SourceDiagnostic, SourceResult, Trace, Tracepoint,
Trace, Tracepoint, bail, error,
}; };
use typst_library::engine::{Engine, Sink, Traced}; use typst_library::engine::{Engine, Sink, Traced};
use typst_library::foundations::{ use typst_library::foundations::{
@ -12,12 +13,11 @@ use typst_library::foundations::{
use typst_library::introspection::Introspector; use typst_library::introspection::Introspector;
use typst_library::math::LrElem; use typst_library::math::LrElem;
use typst_library::routines::Routines; use typst_library::routines::Routines;
use typst_library::World;
use typst_syntax::ast::{self, AstNode, Ident}; use typst_syntax::ast::{self, AstNode, Ident};
use typst_syntax::{Span, Spanned, SyntaxNode}; use typst_syntax::{Span, Spanned, SyntaxNode};
use typst_utils::LazyHash; use typst_utils::LazyHash;
use crate::{call_method_mut, is_mutating_method, Access, Eval, FlowEvent, Route, Vm}; use crate::{Access, Eval, FlowEvent, Route, Vm, call_method_mut, is_mutating_method};
impl Eval for ast::FuncCall<'_> { impl Eval for ast::FuncCall<'_> {
type Output = Value; type Output = Value;

View File

@ -1,9 +1,9 @@
use ecow::{eco_vec, EcoVec}; use ecow::{EcoVec, eco_vec};
use typst_library::diag::{bail, error, warning, At, SourceResult}; use typst_library::diag::{At, SourceResult, bail, error, warning};
use typst_library::engine::Engine; use typst_library::engine::Engine;
use typst_library::foundations::{ use typst_library::foundations::{
ops, Array, Capturer, Closure, Content, ContextElem, Dict, Func, NativeElement, Array, Capturer, Closure, Content, ContextElem, Dict, Func, NativeElement, Selector,
Selector, Str, Value, Str, Value, ops,
}; };
use typst_library::introspection::{Counter, State}; use typst_library::introspection::{Counter, State};
use typst_syntax::ast::{self, AstNode}; use typst_syntax::ast::{self, AstNode};
@ -324,22 +324,18 @@ impl Eval for ast::FieldAccess<'_> {
}; };
// Check whether this is a get rule field access. // Check whether this is a get rule field access.
if_chain::if_chain! { if let Value::Func(func) = &value
if let Value::Func(func) = &value; && let Some(element) = func.element()
if let Some(element) = func.element(); && let Some(id) = element.field_id(&field)
if let Some(id) = element.field_id(&field); && let styles = vm.context.styles().at(field.span())
let styles = vm.context.styles().at(field.span()); && let Ok(value) = element
if let Ok(value) = element.field_from_styles( .field_from_styles(id, styles.as_ref().map(|&s| s).unwrap_or_default())
id, {
styles.as_ref().map(|&s| s).unwrap_or_default(),
);
then {
// Only validate the context once we know that this is indeed // Only validate the context once we know that this is indeed
// a field from the style chain. // a field from the style chain.
let _ = styles?; let _ = styles?;
return Ok(value); return Ok(value);
} }
}
Err(err) Err(err)
} }

View File

@ -1,10 +1,10 @@
use typst_library::diag::{bail, error, At, SourceDiagnostic, SourceResult}; use typst_library::diag::{At, SourceDiagnostic, SourceResult, bail, error};
use typst_library::foundations::{ops, IntoValue, Value}; use typst_library::foundations::{IntoValue, Value, ops};
use typst_syntax::ast::{self, AstNode}; use typst_syntax::ast::{self, AstNode};
use typst_syntax::{Span, SyntaxKind, SyntaxNode}; use typst_syntax::{Span, SyntaxKind, SyntaxNode};
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use crate::{destructure, Eval, Vm}; use crate::{Eval, Vm, destructure};
/// The maximum number of loop iterations. /// The maximum number of loop iterations.
const MAX_ITERATIONS: usize = 10_000; const MAX_ITERATIONS: usize = 10_000;

View File

@ -1,16 +1,16 @@
use comemo::TrackedMut; use comemo::TrackedMut;
use ecow::{eco_format, eco_vec, EcoString}; use ecow::{EcoString, eco_format, eco_vec};
use typst_library::World;
use typst_library::diag::{ use typst_library::diag::{
bail, error, warning, At, FileError, SourceResult, Trace, Tracepoint, At, FileError, SourceResult, Trace, Tracepoint, bail, error, warning,
}; };
use typst_library::engine::Engine; use typst_library::engine::Engine;
use typst_library::foundations::{Binding, Content, Module, Value}; use typst_library::foundations::{Binding, Content, Module, Value};
use typst_library::World;
use typst_syntax::ast::{self, AstNode, BareImportError}; use typst_syntax::ast::{self, AstNode, BareImportError};
use typst_syntax::package::{PackageManifest, PackageSpec}; use typst_syntax::package::{PackageManifest, PackageSpec};
use typst_syntax::{FileId, Span, VirtualPath}; use typst_syntax::{FileId, Span, VirtualPath};
use crate::{eval, Eval, Vm}; use crate::{Eval, Vm, eval};
impl Eval for ast::ModuleImport<'_> { impl Eval for ast::ModuleImport<'_> {
type Output = Value; type Output = Value;
@ -46,15 +46,15 @@ impl Eval for ast::ModuleImport<'_> {
// If there is a rename, import the source itself under that name. // If there is a rename, import the source itself under that name.
let new_name = self.new_name(); let new_name = self.new_name();
if let Some(new_name) = new_name { if let Some(new_name) = new_name {
if let ast::Expr::Ident(ident) = self.source() { if let ast::Expr::Ident(ident) = self.source()
if ident.as_str() == new_name.as_str() { && ident.as_str() == new_name.as_str()
{
// Warn on `import x as x` // Warn on `import x as x`
vm.engine.sink.warn(warning!( vm.engine.sink.warn(warning!(
new_name.span(), new_name.span(),
"unnecessary import rename to same name", "unnecessary import rename to same name",
)); ));
} }
}
// Define renamed module on the scope. // Define renamed module on the scope.
vm.define(new_name, source.clone()); vm.define(new_name, source.clone());
@ -142,8 +142,8 @@ impl Eval for ast::ModuleImport<'_> {
// it. // it.
// Warn on `import ...: x as x` // Warn on `import ...: x as x`
if let ast::ImportItem::Renamed(renamed_item) = &item { if let ast::ImportItem::Renamed(renamed_item) = &item
if renamed_item.original_name().as_str() && renamed_item.original_name().as_str()
== renamed_item.new_name().as_str() == renamed_item.new_name().as_str()
{ {
vm.engine.sink.warn(warning!( vm.engine.sink.warn(warning!(
@ -151,7 +151,6 @@ impl Eval for ast::ModuleImport<'_> {
"unnecessary import rename to same name", "unnecessary import rename to same name",
)); ));
} }
}
vm.bind(item.bound_name(), binding.clone()); vm.bind(item.bound_name(), binding.clone());
} }

View File

@ -14,7 +14,7 @@ mod methods;
mod rules; mod rules;
mod vm; mod vm;
pub use self::call::{eval_closure, CapturesVisitor}; pub use self::call::{CapturesVisitor, eval_closure};
pub use self::flow::FlowEvent; pub use self::flow::FlowEvent;
pub use self::import::import; pub use self::import::import;
pub use self::vm::Vm; pub use self::vm::Vm;
@ -24,14 +24,14 @@ use self::binding::*;
use self::methods::*; use self::methods::*;
use comemo::{Track, Tracked, TrackedMut}; use comemo::{Track, Tracked, TrackedMut};
use typst_library::diag::{bail, SourceResult}; use typst_library::World;
use typst_library::diag::{SourceResult, bail};
use typst_library::engine::{Engine, Route, Sink, Traced}; use typst_library::engine::{Engine, Route, Sink, Traced};
use typst_library::foundations::{Context, Module, NativeElement, Scope, Scopes, Value}; use typst_library::foundations::{Context, Module, NativeElement, Scope, Scopes, Value};
use typst_library::introspection::Introspector; use typst_library::introspection::Introspector;
use typst_library::math::EquationElem; use typst_library::math::EquationElem;
use typst_library::routines::Routines; use typst_library::routines::Routines;
use typst_library::World; use typst_syntax::{Source, Span, SyntaxMode, ast, parse, parse_code, parse_math};
use typst_syntax::{ast, parse, parse_code, parse_math, Source, Span, SyntaxMode};
/// Evaluate a source file and return the resulting module. /// Evaluate a source file and return the resulting module.
#[comemo::memoize] #[comemo::memoize]

View File

@ -1,4 +1,4 @@
use typst_library::diag::{warning, At, SourceResult}; use typst_library::diag::{At, SourceResult, warning};
use typst_library::foundations::{ use typst_library::foundations::{
Content, Label, NativeElement, Repr, Smart, Symbol, Unlabellable, Value, Content, Label, NativeElement, Repr, Smart, Symbol, Unlabellable, Value,
}; };

View File

@ -1,8 +1,8 @@
use typst_library::diag::{At, HintedStrResult, SourceResult}; use typst_library::diag::{At, HintedStrResult, SourceResult};
use typst_library::foundations::{ops, IntoValue, Value}; use typst_library::foundations::{IntoValue, Value, ops};
use typst_syntax::ast::{self, AstNode}; use typst_syntax::ast::{self, AstNode};
use crate::{access_dict, Access, Eval, Vm}; use crate::{Access, Eval, Vm, access_dict};
impl Eval for ast::Unary<'_> { impl Eval for ast::Unary<'_> {
type Output = Value; type Output = Value;
@ -76,13 +76,13 @@ fn apply_assignment(
// An assignment to a dictionary field is different from a normal access // An assignment to a dictionary field is different from a normal access
// since it can create the field instead of just modifying it. // since it can create the field instead of just modifying it.
if binary.op() == ast::BinOp::Assign { if binary.op() == ast::BinOp::Assign
if let ast::Expr::FieldAccess(access) = lhs { && let ast::Expr::FieldAccess(access) = lhs
{
let dict = access_dict(vm, access)?; let dict = access_dict(vm, access)?;
dict.insert(access.field().get().clone().into(), rhs); dict.insert(access.field().get().clone().into(), rhs);
return Ok(Value::None); return Ok(Value::None);
} }
}
let location = binary.lhs().access(vm)?; let location = binary.lhs().access(vm)?;
let lhs = std::mem::take(&mut *location); let lhs = std::mem::take(&mut *location);

View File

@ -1,4 +1,4 @@
use typst_library::diag::{warning, At, SourceResult}; use typst_library::diag::{At, SourceResult, warning};
use typst_library::foundations::{ use typst_library::foundations::{
Element, Func, Recipe, Selector, ShowableSelector, Styles, Transformation, Element, Func, Recipe, Selector, ShowableSelector, Styles, Transformation,
}; };
@ -12,11 +12,11 @@ impl Eval for ast::SetRule<'_> {
type Output = Styles; type Output = Styles;
fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> { fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
if let Some(condition) = self.condition() { if let Some(condition) = self.condition()
if !condition.eval(vm)?.cast::<bool>().at(condition.span())? { && !condition.eval(vm)?.cast::<bool>().at(condition.span())?
{
return Ok(Styles::new()); return Ok(Styles::new());
} }
}
let target = self.target(); let target = self.target();
let target = target let target = target
@ -58,12 +58,11 @@ impl Eval for ast::ShowRule<'_> {
/// Migration hint for `show par: set block(spacing: ..)`. /// Migration hint for `show par: set block(spacing: ..)`.
fn check_show_par_set_block(vm: &mut Vm, recipe: &Recipe) { fn check_show_par_set_block(vm: &mut Vm, recipe: &Recipe) {
if_chain::if_chain! { if let Some(Selector::Elem(elem, _)) = recipe.selector()
if let Some(Selector::Elem(elem, _)) = recipe.selector(); && *elem == Element::of::<ParElem>()
if *elem == Element::of::<ParElem>(); && let Transformation::Style(styles) = recipe.transform()
if let Transformation::Style(styles) = recipe.transform(); && (styles.has(BlockElem::above) || styles.has(BlockElem::below))
if styles.has(BlockElem::above) || styles.has(BlockElem::below); {
then {
vm.engine.sink.warn(warning!( vm.engine.sink.warn(warning!(
recipe.span(), recipe.span(),
"`show par: set block(spacing: ..)` has no effect anymore"; "`show par: set block(spacing: ..)` has no effect anymore";
@ -72,4 +71,3 @@ fn check_show_par_set_block(vm: &mut Vm, recipe: &Recipe) {
)) ))
} }
} }
}

View File

@ -1,10 +1,10 @@
use comemo::Tracked; use comemo::Tracked;
use typst_library::World;
use typst_library::diag::warning; use typst_library::diag::warning;
use typst_library::engine::Engine; use typst_library::engine::Engine;
use typst_library::foundations::{Binding, Context, IntoValue, Scopes, Value}; use typst_library::foundations::{Binding, Context, IntoValue, Scopes, Value};
use typst_library::World;
use typst_syntax::ast::{self, AstNode};
use typst_syntax::Span; use typst_syntax::Span;
use typst_syntax::ast::{self, AstNode};
use crate::FlowEvent; use crate::FlowEvent;

View File

@ -1,4 +1,4 @@
use typst_library::diag::{warning, SourceResult}; use typst_library::diag::{SourceResult, warning};
use typst_library::engine::Engine; use typst_library::engine::Engine;
use typst_library::foundations::{Content, StyleChain, Target, TargetElem}; use typst_library::foundations::{Content, StyleChain, Target, TargetElem};
use typst_library::introspection::{SplitLocator, TagElem}; use typst_library::introspection::{SplitLocator, TagElem};
@ -8,7 +8,7 @@ use typst_library::routines::Pair;
use typst_library::text::{LinebreakElem, SmartQuoteElem, SpaceElem, TextElem}; use typst_library::text::{LinebreakElem, SmartQuoteElem, SpaceElem, TextElem};
use crate::fragment::html_fragment; use crate::fragment::html_fragment;
use crate::{attr, tag, FrameElem, HtmlElem, HtmlElement, HtmlFrame, HtmlNode}; use crate::{FrameElem, HtmlElem, HtmlElement, HtmlFrame, HtmlNode, attr, tag};
/// Converts realized content into HTML nodes. /// Converts realized content into HTML nodes.
pub fn convert_to_nodes<'a>( pub fn convert_to_nodes<'a>(

View File

@ -2,7 +2,8 @@ use std::collections::HashSet;
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use comemo::{Tracked, TrackedMut}; use comemo::{Tracked, TrackedMut};
use typst_library::diag::{bail, SourceResult}; use typst_library::World;
use typst_library::diag::{SourceResult, bail};
use typst_library::engine::{Engine, Route, Sink, Traced}; use typst_library::engine::{Engine, Route, Sink, Traced};
use typst_library::foundations::{Content, StyleChain}; use typst_library::foundations::{Content, StyleChain};
use typst_library::introspection::{ use typst_library::introspection::{
@ -11,11 +12,10 @@ use typst_library::introspection::{
use typst_library::layout::{Point, Position, Transform}; use typst_library::layout::{Point, Position, Transform};
use typst_library::model::DocumentInfo; use typst_library::model::DocumentInfo;
use typst_library::routines::{Arenas, RealizationKind, Routines}; use typst_library::routines::{Arenas, RealizationKind, Routines};
use typst_library::World;
use typst_syntax::Span; use typst_syntax::Span;
use typst_utils::NonZeroExt; use typst_utils::NonZeroExt;
use crate::{attr, tag, HtmlDocument, HtmlElement, HtmlNode}; use crate::{HtmlDocument, HtmlElement, HtmlNode, attr, tag};
/// Produce an HTML document from content. /// Produce an HTML document from content.
/// ///

View File

@ -1,8 +1,8 @@
use std::fmt::{self, Debug, Display, Formatter}; use std::fmt::{self, Debug, Display, Formatter};
use ecow::{EcoString, EcoVec}; use ecow::{EcoString, EcoVec};
use typst_library::diag::{bail, HintedStrResult, StrResult}; use typst_library::diag::{HintedStrResult, StrResult, bail};
use typst_library::foundations::{cast, Dict, Repr, Str, StyleChain}; use typst_library::foundations::{Dict, Repr, Str, StyleChain, cast};
use typst_library::introspection::{Introspector, Tag}; use typst_library::introspection::{Introspector, Tag};
use typst_library::layout::{Abs, Frame, Point}; use typst_library::layout::{Abs, Frame, Point};
use typst_library::model::DocumentInfo; use typst_library::model::DocumentInfo;

View File

@ -1,12 +1,12 @@
use std::fmt::Write; use std::fmt::Write;
use typst_library::diag::{bail, At, SourceResult, StrResult}; use typst_library::diag::{At, SourceResult, StrResult, bail};
use typst_library::foundations::Repr; use typst_library::foundations::Repr;
use typst_library::introspection::Introspector; use typst_library::introspection::Introspector;
use typst_syntax::Span; use typst_syntax::Span;
use crate::{ use crate::{
attr, charsets, tag, HtmlDocument, HtmlElement, HtmlFrame, HtmlNode, HtmlTag, HtmlDocument, HtmlElement, HtmlFrame, HtmlNode, HtmlTag, attr, charsets, tag,
}; };
/// Encodes an HTML document into a string. /// Encodes an HTML document into a string.
@ -262,11 +262,7 @@ impl RawMode {
{ {
// Template literals can be multi-line, so indent may change // Template literals can be multi-line, so indent may change
// the semantics of the JavaScript. // the semantics of the JavaScript.
if text.contains('`') { if text.contains('`') { Self::Wrap } else { Self::Indent }
Self::Wrap
} else {
Self::Indent
}
} }
tag::style => Self::Indent, tag::style => Self::Indent,
_ => Self::Keep, _ => Self::Keep,

View File

@ -4,8 +4,8 @@ use typst_library::engine::{Engine, Route, Sink, Traced};
use typst_library::foundations::{Content, StyleChain}; use typst_library::foundations::{Content, StyleChain};
use typst_library::introspection::{Introspector, Locator, LocatorLink}; use typst_library::introspection::{Introspector, Locator, LocatorLink};
use typst_library::routines::{Arenas, FragmentKind, RealizationKind, Routines};
use typst_library::World; use typst_library::World;
use typst_library::routines::{Arenas, FragmentKind, RealizationKind, Routines};
use crate::HtmlNode; use crate::HtmlNode;

View File

@ -19,8 +19,8 @@ pub use self::encode::html;
pub use self::rules::register; pub use self::rules::register;
use ecow::EcoString; use ecow::EcoString;
use typst_library::foundations::{Content, Module, Scope};
use typst_library::Category; use typst_library::Category;
use typst_library::foundations::{Content, Module, Scope};
use typst_macros::elem; use typst_macros::elem;
/// Creates the module with all HTML definitions. /// Creates the module with all HTML definitions.
@ -86,11 +86,7 @@ impl HtmlElem {
attr: HtmlAttr, attr: HtmlAttr,
value: Option<impl Into<EcoString>>, value: Option<impl Into<EcoString>>,
) -> Self { ) -> Self {
if let Some(value) = value { if let Some(value) = value { self.with_attr(attr, value) } else { self }
self.with_attr(attr, value)
} else {
self
}
} }
/// Adds CSS styles to an element. /// Adds CSS styles to an element.

View File

@ -1,14 +1,14 @@
use std::collections::{HashMap, HashSet, VecDeque}; use std::collections::{HashMap, HashSet, VecDeque};
use comemo::Track; use comemo::Track;
use ecow::{eco_format, EcoString}; use ecow::{EcoString, eco_format};
use typst_library::foundations::{Label, NativeElement}; use typst_library::foundations::{Label, NativeElement};
use typst_library::introspection::{Introspector, Location, Tag}; use typst_library::introspection::{Introspector, Location, Tag};
use typst_library::layout::{Frame, FrameItem, Point}; use typst_library::layout::{Frame, FrameItem, Point};
use typst_library::model::{Destination, LinkElem}; use typst_library::model::{Destination, LinkElem};
use typst_utils::PicoStr; use typst_utils::PicoStr;
use crate::{attr, tag, HtmlElement, HtmlNode}; use crate::{HtmlElement, HtmlNode, attr, tag};
/// Searches for links within a frame. /// Searches for links within a frame.
/// ///

View File

@ -1,12 +1,12 @@
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use ecow::{eco_format, EcoVec}; use ecow::{EcoVec, eco_format};
use typst_library::diag::{warning, At}; use typst_library::diag::{At, warning};
use typst_library::foundations::{ use typst_library::foundations::{
Content, NativeElement, NativeRuleMap, ShowFn, Smart, StyleChain, Target, Content, NativeElement, NativeRuleMap, ShowFn, Smart, StyleChain, Target,
}; };
use typst_library::introspection::{Counter, Locator}; use typst_library::introspection::{Counter, Locator};
use typst_library::layout::resolve::{table_to_cellgrid, Cell, CellGrid, Entry}; use typst_library::layout::resolve::{Cell, CellGrid, Entry, table_to_cellgrid};
use typst_library::layout::{OuterVAlignment, Sizing}; use typst_library::layout::{OuterVAlignment, Sizing};
use typst_library::model::{ use typst_library::model::{
Attribution, CiteElem, CiteGroup, Destination, EmphElem, EnumElem, FigureCaption, Attribution, CiteElem, CiteGroup, Destination, EmphElem, EnumElem, FigureCaption,
@ -19,7 +19,7 @@ use typst_library::text::{
}; };
use typst_library::visualize::ImageElem; use typst_library::visualize::ImageElem;
use crate::{attr, css, tag, FrameElem, HtmlAttrs, HtmlElem, HtmlTag}; use crate::{FrameElem, HtmlAttrs, HtmlElem, HtmlTag, attr, css, tag};
/// Registers show rules for the [HTML target](Target::Html). /// Registers show rules for the [HTML target](Target::Html).
pub fn register(rules: &mut NativeRuleMap) { pub fn register(rules: &mut NativeRuleMap) {
@ -238,13 +238,11 @@ const QUOTE_RULE: ShowFn<QuoteElem> = |elem, _, styles| {
if block { if block {
let mut blockquote = HtmlElem::new(tag::blockquote).with_body(Some(realized)); let mut blockquote = HtmlElem::new(tag::blockquote).with_body(Some(realized));
if let Some(Attribution::Content(attribution)) = attribution { if let Some(Attribution::Content(attribution)) = attribution
if let Some(link) = attribution.to_packed::<LinkElem>() { && let Some(link) = attribution.to_packed::<LinkElem>()
if let LinkTarget::Dest(Destination::Url(url)) = &link.dest { && let LinkTarget::Dest(Destination::Url(url)) = &link.dest
blockquote = {
blockquote.with_attr(attr::cite, url.clone().into_inner()); blockquote = blockquote.with_attr(attr::cite, url.clone().into_inner());
}
}
} }
realized = blockquote.pack().spanned(span); realized = blockquote.pack().spanned(span);

View File

@ -9,9 +9,9 @@ use std::sync::LazyLock;
use bumpalo::Bump; use bumpalo::Bump;
use comemo::Tracked; use comemo::Tracked;
use ecow::{eco_format, eco_vec, EcoString}; use ecow::{EcoString, eco_format, eco_vec};
use typst_assets::html as data; use typst_assets::html as data;
use typst_library::diag::{bail, At, Hint, HintedStrResult, SourceResult}; use typst_library::diag::{At, Hint, HintedStrResult, SourceResult, bail};
use typst_library::engine::Engine; use typst_library::engine::Engine;
use typst_library::foundations::{ use typst_library::foundations::{
Args, Array, AutoValue, CastInfo, Content, Context, Datetime, Dict, Duration, Args, Array, AutoValue, CastInfo, Content, Context, Datetime, Dict, Duration,
@ -22,7 +22,7 @@ use typst_library::layout::{Axes, Axis, Dir, Length};
use typst_library::visualize::Color; use typst_library::visualize::Color;
use typst_macros::cast; use typst_macros::cast;
use crate::{css, tag, HtmlAttr, HtmlAttrs, HtmlElem, HtmlTag}; use crate::{HtmlAttr, HtmlAttrs, HtmlElem, HtmlTag, css, tag};
/// Hook up all typed HTML definitions. /// Hook up all typed HTML definitions.
pub(super) fn define(html: &mut Scope) { pub(super) fn define(html: &mut Scope) {

View File

@ -17,7 +17,6 @@ typst = { workspace = true }
typst-eval = { workspace = true } typst-eval = { workspace = true }
comemo = { workspace = true } comemo = { workspace = true }
ecow = { workspace = true } ecow = { workspace = true }
if_chain = { workspace = true }
pathdiff = { workspace = true } pathdiff = { workspace = true }
serde = { workspace = true } serde = { workspace = true }
unscanny = { workspace = true } unscanny = { workspace = true }

View File

@ -1,11 +1,11 @@
use std::collections::HashSet; use std::collections::HashSet;
use comemo::Track; use comemo::Track;
use ecow::{eco_vec, EcoString, EcoVec}; use ecow::{EcoString, EcoVec, eco_vec};
use typst::foundations::{Label, Styles, Value}; use typst::foundations::{Label, Styles, Value};
use typst::layout::PagedDocument; use typst::layout::PagedDocument;
use typst::model::{BibliographyElem, FigureElem}; use typst::model::{BibliographyElem, FigureElem};
use typst::syntax::{ast, LinkedNode, SyntaxKind}; use typst::syntax::{LinkedNode, SyntaxKind, ast};
use crate::IdeWorld; use crate::IdeWorld;
@ -27,17 +27,18 @@ pub fn analyze_expr(
ast::Expr::Numeric(v) => Value::numeric(v.get()), ast::Expr::Numeric(v) => Value::numeric(v.get()),
ast::Expr::Str(v) => Value::Str(v.get().into()), ast::Expr::Str(v) => Value::Str(v.get().into()),
_ => { _ => {
if node.kind() == SyntaxKind::Contextual { if node.kind() == SyntaxKind::Contextual
if let Some(child) = node.children().next_back() { && let Some(child) = node.children().next_back()
{
return analyze_expr(world, &child); return analyze_expr(world, &child);
} }
}
if let Some(parent) = node.parent() { if let Some(parent) = node.parent()
if parent.kind() == SyntaxKind::FieldAccess && node.index() > 0 { && parent.kind() == SyntaxKind::FieldAccess
&& node.index() > 0
{
return analyze_expr(world, parent); return analyze_expr(world, parent);
} }
}
return typst::trace::<PagedDocument>(world.upcast(), node.span()); return typst::trace::<PagedDocument>(world.upcast(), node.span());
} }

View File

@ -2,18 +2,17 @@ use std::cmp::Reverse;
use std::collections::{BTreeMap, HashSet}; use std::collections::{BTreeMap, HashSet};
use std::ffi::OsStr; use std::ffi::OsStr;
use ecow::{eco_format, EcoString}; use ecow::{EcoString, eco_format};
use if_chain::if_chain;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use typst::foundations::{ use typst::foundations::{
fields_on, repr, AutoValue, CastInfo, Func, Label, NoneValue, ParamInfo, Repr, AutoValue, CastInfo, Func, Label, NoneValue, ParamInfo, Repr, StyleChain, Styles,
StyleChain, Styles, Type, Value, Type, Value, fields_on, repr,
}; };
use typst::layout::{Alignment, Dir, PagedDocument}; use typst::layout::{Alignment, Dir, PagedDocument};
use typst::syntax::ast::AstNode; use typst::syntax::ast::AstNode;
use typst::syntax::{ use typst::syntax::{
ast, is_id_continue, is_id_start, is_ident, FileId, LinkedNode, Side, Source, FileId, LinkedNode, Side, Source, SyntaxKind, ast, is_id_continue, is_id_start,
SyntaxKind, is_ident,
}; };
use typst::text::{FontFlags, RawElem}; use typst::text::{FontFlags, RawElem};
use typst::visualize::Color; use typst::visualize::Color;
@ -22,7 +21,7 @@ use unscanny::Scanner;
use crate::utils::{ use crate::utils::{
check_value_recursively, globals, plain_docs_sentence, summarize_font_family, check_value_recursively, globals, plain_docs_sentence, summarize_font_family,
}; };
use crate::{analyze_expr, analyze_import, analyze_labels, named_items, IdeWorld}; use crate::{IdeWorld, analyze_expr, analyze_import, analyze_labels, named_items};
/// Autocomplete a cursor position in a source file. /// Autocomplete a cursor position in a source file.
/// ///
@ -145,27 +144,23 @@ fn complete_markup(ctx: &mut CompletionContext) -> bool {
} }
// Behind a half-completed binding: "#let x = |". // Behind a half-completed binding: "#let x = |".
if_chain! { if let Some(prev) = ctx.leaf.prev_leaf()
if let Some(prev) = ctx.leaf.prev_leaf(); && prev.kind() == SyntaxKind::Eq
if prev.kind() == SyntaxKind::Eq; && prev.parent_kind() == Some(SyntaxKind::LetBinding)
if prev.parent_kind() == Some(SyntaxKind::LetBinding); {
then {
ctx.from = ctx.cursor; ctx.from = ctx.cursor;
code_completions(ctx, false); code_completions(ctx, false);
return true; return true;
} }
}
// Behind a half-completed context block: "#context |". // Behind a half-completed context block: "#context |".
if_chain! { if let Some(prev) = ctx.leaf.prev_leaf()
if let Some(prev) = ctx.leaf.prev_leaf(); && prev.kind() == SyntaxKind::Context
if prev.kind() == SyntaxKind::Context; {
then {
ctx.from = ctx.cursor; ctx.from = ctx.cursor;
code_completions(ctx, false); code_completions(ctx, false);
return true; return true;
} }
}
// Directly after a raw block. // Directly after a raw block.
let mut s = Scanner::new(ctx.text); let mut s = Scanner::new(ctx.text);
@ -373,38 +368,35 @@ fn complete_field_accesses(ctx: &mut CompletionContext) -> bool {
); );
// Behind an expression plus dot: "emoji.|". // Behind an expression plus dot: "emoji.|".
if_chain! { if (ctx.leaf.kind() == SyntaxKind::Dot
if ctx.leaf.kind() == SyntaxKind::Dot
|| (matches!(ctx.leaf.kind(), SyntaxKind::Text | SyntaxKind::MathText) || (matches!(ctx.leaf.kind(), SyntaxKind::Text | SyntaxKind::MathText)
&& ctx.leaf.text() == "."); && ctx.leaf.text() == "."))
if ctx.leaf.range().end == ctx.cursor; && ctx.leaf.range().end == ctx.cursor
if let Some(prev) = ctx.leaf.prev_sibling(); && let Some(prev) = ctx.leaf.prev_sibling()
if !in_markup || prev.range().end == ctx.leaf.range().start; && (!in_markup || prev.range().end == ctx.leaf.range().start)
if prev.is::<ast::Expr>(); && prev.is::<ast::Expr>()
if prev.parent_kind() != Some(SyntaxKind::Markup) || && (prev.parent_kind() != Some(SyntaxKind::Markup)
prev.prev_sibling_kind() == Some(SyntaxKind::Hash); || prev.prev_sibling_kind() == Some(SyntaxKind::Hash))
if let Some((value, styles)) = analyze_expr(ctx.world, &prev).into_iter().next(); && let Some((value, styles)) = analyze_expr(ctx.world, &prev).into_iter().next()
then { {
ctx.from = ctx.cursor; ctx.from = ctx.cursor;
field_access_completions(ctx, &value, &styles); field_access_completions(ctx, &value, &styles);
return true; return true;
} }
}
// Behind a started field access: "emoji.fa|". // Behind a started field access: "emoji.fa|".
if_chain! { if ctx.leaf.kind() == SyntaxKind::Ident
if ctx.leaf.kind() == SyntaxKind::Ident; && let Some(prev) = ctx.leaf.prev_sibling()
if let Some(prev) = ctx.leaf.prev_sibling(); && prev.kind() == SyntaxKind::Dot
if prev.kind() == SyntaxKind::Dot; && let Some(prev_prev) = prev.prev_sibling()
if let Some(prev_prev) = prev.prev_sibling(); && prev_prev.is::<ast::Expr>()
if prev_prev.is::<ast::Expr>(); && let Some((value, styles)) =
if let Some((value, styles)) = analyze_expr(ctx.world, &prev_prev).into_iter().next(); analyze_expr(ctx.world, &prev_prev).into_iter().next()
then { {
ctx.from = ctx.leaf.offset(); ctx.from = ctx.leaf.offset();
field_access_completions(ctx, &value, &styles); field_access_completions(ctx, &value, &styles);
return true; return true;
} }
}
false false
} }
@ -507,14 +499,11 @@ fn complete_open_labels(ctx: &mut CompletionContext) -> bool {
fn complete_imports(ctx: &mut CompletionContext) -> bool { fn complete_imports(ctx: &mut CompletionContext) -> bool {
// In an import path for a file or package: // In an import path for a file or package:
// "#import "|", // "#import "|",
if_chain! { if let Some(SyntaxKind::ModuleImport | SyntaxKind::ModuleInclude) =
if matches!( ctx.leaf.parent_kind()
ctx.leaf.parent_kind(), && let Some(ast::Expr::Str(str)) = ctx.leaf.cast()
Some(SyntaxKind::ModuleImport | SyntaxKind::ModuleInclude) {
);
if let Some(ast::Expr::Str(str)) = ctx.leaf.cast();
let value = str.get(); let value = str.get();
then {
ctx.from = ctx.leaf.offset(); ctx.from = ctx.leaf.offset();
if value.starts_with('@') { if value.starts_with('@') {
let all_versions = value.contains(':'); let all_versions = value.contains(':');
@ -524,41 +513,36 @@ fn complete_imports(ctx: &mut CompletionContext) -> bool {
} }
return true; return true;
} }
}
// Behind an import list: // Behind an import list:
// "#import "path.typ": |", // "#import "path.typ": |",
// "#import "path.typ": a, b, |". // "#import "path.typ": a, b, |".
if_chain! { if let Some(prev) = ctx.leaf.prev_sibling()
if let Some(prev) = ctx.leaf.prev_sibling(); && let Some(ast::Expr::ModuleImport(import)) = prev.get().cast()
if let Some(ast::Expr::ModuleImport(import)) = prev.get().cast(); && let Some(ast::Imports::Items(items)) = import.imports()
if let Some(ast::Imports::Items(items)) = import.imports(); && let Some(source) = prev.children().find(|child| child.is::<ast::Expr>())
if let Some(source) = prev.children().find(|child| child.is::<ast::Expr>()); {
then {
ctx.from = ctx.cursor; ctx.from = ctx.cursor;
import_item_completions(ctx, items, &source); import_item_completions(ctx, items, &source);
return true; return true;
} }
}
// Behind a half-started identifier in an import list: // Behind a half-started identifier in an import list:
// "#import "path.typ": thi|", // "#import "path.typ": thi|",
if_chain! { if ctx.leaf.kind() == SyntaxKind::Ident
if ctx.leaf.kind() == SyntaxKind::Ident; && let Some(parent) = ctx.leaf.parent()
if let Some(parent) = ctx.leaf.parent(); && parent.kind() == SyntaxKind::ImportItemPath
if parent.kind() == SyntaxKind::ImportItemPath; && let Some(grand) = parent.parent()
if let Some(grand) = parent.parent(); && grand.kind() == SyntaxKind::ImportItems
if grand.kind() == SyntaxKind::ImportItems; && let Some(great) = grand.parent()
if let Some(great) = grand.parent(); && let Some(ast::Expr::ModuleImport(import)) = great.get().cast()
if let Some(ast::Expr::ModuleImport(import)) = great.get().cast(); && let Some(ast::Imports::Items(items)) = import.imports()
if let Some(ast::Imports::Items(items)) = import.imports(); && let Some(source) = great.children().find(|child| child.is::<ast::Expr>())
if let Some(source) = great.children().find(|child| child.is::<ast::Expr>()); {
then {
ctx.from = ctx.leaf.offset(); ctx.from = ctx.leaf.offset();
import_item_completions(ctx, items, &source); import_item_completions(ctx, items, &source);
return true; return true;
} }
}
false false
} }
@ -607,16 +591,14 @@ fn complete_rules(ctx: &mut CompletionContext) -> bool {
} }
// Behind a half-completed show rule: "show strong: |". // Behind a half-completed show rule: "show strong: |".
if_chain! { if let Some(prev) = ctx.leaf.prev_leaf()
if let Some(prev) = ctx.leaf.prev_leaf(); && matches!(prev.kind(), SyntaxKind::Colon)
if matches!(prev.kind(), SyntaxKind::Colon); && matches!(prev.parent_kind(), Some(SyntaxKind::ShowRule))
if matches!(prev.parent_kind(), Some(SyntaxKind::ShowRule)); {
then {
ctx.from = ctx.cursor; ctx.from = ctx.cursor;
show_rule_recipe_completions(ctx); show_rule_recipe_completions(ctx);
return true; return true;
} }
}
false false
} }
@ -682,26 +664,23 @@ fn show_rule_recipe_completions(ctx: &mut CompletionContext) {
/// Complete call and set rule parameters. /// Complete call and set rule parameters.
fn complete_params(ctx: &mut CompletionContext) -> bool { fn complete_params(ctx: &mut CompletionContext) -> bool {
// Ensure that we are in a function call or set rule's argument list. // Ensure that we are in a function call or set rule's argument list.
let (callee, set, args, args_linked) = if_chain! { let (callee, set, args, args_linked) = if let Some(parent) = ctx.leaf.parent()
if let Some(parent) = ctx.leaf.parent(); && let Some(parent) = match parent.kind() {
if let Some(parent) = match parent.kind() {
SyntaxKind::Named => parent.parent(), SyntaxKind::Named => parent.parent(),
_ => Some(parent), _ => Some(parent),
}; }
if let Some(args) = parent.get().cast::<ast::Args>(); && let Some(args) = parent.get().cast::<ast::Args>()
if let Some(grand) = parent.parent(); && let Some(grand) = parent.parent()
if let Some(expr) = grand.get().cast::<ast::Expr>(); && let Some(expr) = grand.get().cast::<ast::Expr>()
let set = matches!(expr, ast::Expr::SetRule(_)); && let set = matches!(expr, ast::Expr::SetRule(_))
if let Some(callee) = match expr { && let Some(callee) = match expr {
ast::Expr::FuncCall(call) => Some(call.callee()), ast::Expr::FuncCall(call) => Some(call.callee()),
ast::Expr::SetRule(set) => Some(set.target()), ast::Expr::SetRule(set) => Some(set.target()),
_ => None, _ => None,
}; } {
then {
(callee, set, args, parent) (callee, set, args, parent)
} else { } else {
return false; return false;
}
}; };
// Find the piece of syntax that decides what we're completing. // Find the piece of syntax that decides what we're completing.
@ -718,11 +697,10 @@ fn complete_params(ctx: &mut CompletionContext) -> bool {
} }
// Parameter values: "func(param:|)", "func(param: |)". // Parameter values: "func(param:|)", "func(param: |)".
if_chain! { if let SyntaxKind::Colon = deciding.kind()
if deciding.kind() == SyntaxKind::Colon; && let Some(prev) = deciding.prev_leaf()
if let Some(prev) = deciding.prev_leaf(); && let Some(param) = prev.get().cast::<ast::Ident>()
if let Some(param) = prev.get().cast::<ast::Ident>(); {
then {
if let Some(next) = deciding.next_leaf() { if let Some(next) = deciding.next_leaf() {
ctx.from = ctx.cursor.min(next.offset()); ctx.from = ctx.cursor.min(next.offset());
} }
@ -730,13 +708,11 @@ fn complete_params(ctx: &mut CompletionContext) -> bool {
named_param_value_completions(ctx, callee, &param); named_param_value_completions(ctx, callee, &param);
return true; return true;
} }
}
// Parameters: "func(|)", "func(hi|)", "func(12,|)". // Parameters: "func(|)", "func(hi|)", "func(12,|)".
if_chain! { if let SyntaxKind::LeftParen | SyntaxKind::Comma = deciding.kind()
if matches!(deciding.kind(), SyntaxKind::LeftParen | SyntaxKind::Comma); && (deciding.kind() != SyntaxKind::Comma || deciding.range().end < ctx.cursor)
if deciding.kind() != SyntaxKind::Comma || deciding.range().end < ctx.cursor; {
then {
if let Some(next) = deciding.next_leaf() { if let Some(next) = deciding.next_leaf() {
ctx.from = ctx.cursor.min(next.offset()); ctx.from = ctx.cursor.min(next.offset());
} }
@ -744,7 +720,6 @@ fn complete_params(ctx: &mut CompletionContext) -> bool {
param_completions(ctx, callee, set, args, args_linked); param_completions(ctx, callee, set, args, args_linked);
return true; return true;
} }
}
false false
} }
@ -1104,15 +1079,13 @@ fn code_completions(ctx: &mut CompletionContext, hash: bool) {
fn is_in_equation_show_rule(leaf: &LinkedNode<'_>) -> bool { fn is_in_equation_show_rule(leaf: &LinkedNode<'_>) -> bool {
let mut node = leaf; let mut node = leaf;
while let Some(parent) = node.parent() { while let Some(parent) = node.parent() {
if_chain! { if let Some(expr) = parent.get().cast::<ast::Expr>()
if let Some(expr) = parent.get().cast::<ast::Expr>(); && let ast::Expr::ShowRule(show) = expr
if let ast::Expr::ShowRule(show) = expr; && let Some(ast::Expr::FieldAccess(field)) = show.selector()
if let Some(ast::Expr::FieldAccess(field)) = show.selector(); && field.field().as_str() == "equation"
if field.field().as_str() == "equation"; {
then {
return true; return true;
} }
}
node = parent; node = parent;
} }
false false
@ -1382,11 +1355,12 @@ impl<'a> CompletionContext<'a> {
} }
} else if at { } else if at {
apply = Some(eco_format!("at(\"{label}\")")); apply = Some(eco_format!("at(\"{label}\")"));
} else if label.starts_with('"') && self.after.starts_with('"') { } else if label.starts_with('"')
if let Some(trimmed) = label.strip_suffix('"') { && self.after.starts_with('"')
&& let Some(trimmed) = label.strip_suffix('"')
{
apply = Some(trimmed.into()); apply = Some(trimmed.into());
} }
}
self.completions.push(Completion { self.completions.push(Completion {
kind: kind.unwrap_or_else(|| match value { kind: kind.unwrap_or_else(|| match value {
@ -1571,7 +1545,7 @@ mod tests {
use typst::layout::PagedDocument; use typst::layout::PagedDocument;
use super::{autocomplete, Completion, CompletionKind}; use super::{Completion, CompletionKind, autocomplete};
use crate::tests::{FilePos, TestWorld, WorldLike}; use crate::tests::{FilePos, TestWorld, WorldLike};
/// Quote a string. /// Quote a string.

View File

@ -1,12 +1,12 @@
use typst::foundations::{Label, Selector, Value}; use typst::foundations::{Label, Selector, Value};
use typst::layout::PagedDocument; use typst::layout::PagedDocument;
use typst::syntax::{ast, LinkedNode, Side, Source, Span}; use typst::syntax::{LinkedNode, Side, Source, Span, ast};
use typst::utils::PicoStr; use typst::utils::PicoStr;
use crate::utils::globals; use crate::utils::globals;
use crate::{ use crate::{
analyze_expr, analyze_import, deref_target, named_items, DerefTarget, IdeWorld, DerefTarget, IdeWorld, NamedItem, analyze_expr, analyze_import, deref_target,
NamedItem, named_items,
}; };
/// A definition of some item. /// A definition of some item.
@ -90,11 +90,11 @@ mod tests {
use std::borrow::Borrow; use std::borrow::Borrow;
use std::ops::Range; use std::ops::Range;
use typst::WorldExt;
use typst::foundations::{IntoValue, NativeElement}; use typst::foundations::{IntoValue, NativeElement};
use typst::syntax::Side; use typst::syntax::Side;
use typst::WorldExt;
use super::{definition, Definition}; use super::{Definition, definition};
use crate::tests::{FilePos, TestWorld, WorldLike}; use crate::tests::{FilePos, TestWorld, WorldLike};
type Response = (TestWorld, Option<Definition>); type Response = (TestWorld, Option<Definition>);

View File

@ -1,10 +1,10 @@
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use typst::WorldExt;
use typst::layout::{Frame, FrameItem, PagedDocument, Point, Position, Size}; use typst::layout::{Frame, FrameItem, PagedDocument, Point, Position, Size};
use typst::model::{Destination, Url}; use typst::model::{Destination, Url};
use typst::syntax::{FileId, LinkedNode, Side, Source, Span, SyntaxKind}; use typst::syntax::{FileId, LinkedNode, Side, Source, Span, SyntaxKind};
use typst::visualize::{Curve, CurveItem, FillRule, Geometry}; use typst::visualize::{Curve, CurveItem, FillRule, Geometry};
use typst::WorldExt;
use crate::IdeWorld; use crate::IdeWorld;
@ -36,8 +36,9 @@ pub fn jump_from_click(
) -> Option<Jump> { ) -> Option<Jump> {
// Try to find a link first. // Try to find a link first.
for (pos, item) in frame.items() { for (pos, item) in frame.items() {
if let FrameItem::Link(dest, size) = item { if let FrameItem::Link(dest, size) = item
if is_in_rect(*pos, *size, click) { && is_in_rect(*pos, *size, click)
{
return Some(match dest { return Some(match dest {
Destination::Url(url) => Jump::Url(url.clone()), Destination::Url(url) => Jump::Url(url.clone()),
Destination::Position(pos) => Jump::Position(*pos), Destination::Position(pos) => Jump::Position(*pos),
@ -47,18 +48,17 @@ pub fn jump_from_click(
}); });
} }
} }
}
// If there's no link, search for a jump target. // If there's no link, search for a jump target.
for (mut pos, item) in frame.items().rev() { for &(mut pos, ref item) in frame.items().rev() {
match item { match item {
FrameItem::Group(group) => { FrameItem::Group(group) => {
let pos = click - pos; let pos = click - pos;
if let Some(clip) = &group.clip { if let Some(clip) = &group.clip
if !clip.contains(FillRule::NonZero, pos) { && !clip.contains(FillRule::NonZero, pos)
{
continue; continue;
} }
}
// Realistic transforms should always be invertible. // Realistic transforms should always be invertible.
// An example of one that isn't is a scale of 0, which would // An example of one that isn't is a scale of 0, which would
// not be clickable anyway. // not be clickable anyway.
@ -177,12 +177,12 @@ pub fn jump_from_cursor(
/// Find the position of a span in a frame. /// Find the position of a span in a frame.
fn find_in_frame(frame: &Frame, span: Span) -> Option<Point> { fn find_in_frame(frame: &Frame, span: Span) -> Option<Point> {
for (mut pos, item) in frame.items() { for &(mut pos, ref item) in frame.items() {
if let FrameItem::Group(group) = item { if let FrameItem::Group(group) = item
if let Some(point) = find_in_frame(&group.frame, span) { && let Some(point) = find_in_frame(&group.frame, span)
{
return Some(pos + point.transform(group.transform)); return Some(pos + point.transform(group.transform));
} }
}
if let FrameItem::Text(text) = item { if let FrameItem::Text(text) = item {
for glyph in &text.glyphs { for glyph in &text.glyphs {
@ -222,7 +222,7 @@ mod tests {
use typst::layout::{Abs, Point, Position}; use typst::layout::{Abs, Point, Position};
use super::{jump_from_click, jump_from_cursor, Jump}; use super::{Jump, jump_from_click, jump_from_cursor};
use crate::tests::{FilePos, TestWorld, WorldLike}; use crate::tests::{FilePos, TestWorld, WorldLike};
fn point(x: f64, y: f64) -> Point { fn point(x: f64, y: f64) -> Point {

View File

@ -9,16 +9,16 @@ mod tooltip;
mod utils; mod utils;
pub use self::analyze::{analyze_expr, analyze_import, analyze_labels}; pub use self::analyze::{analyze_expr, analyze_import, analyze_labels};
pub use self::complete::{autocomplete, Completion, CompletionKind}; pub use self::complete::{Completion, CompletionKind, autocomplete};
pub use self::definition::{definition, Definition}; pub use self::definition::{Definition, definition};
pub use self::jump::{jump_from_click, jump_from_cursor, Jump}; pub use self::jump::{Jump, jump_from_click, jump_from_cursor};
pub use self::matchers::{deref_target, named_items, DerefTarget, NamedItem}; pub use self::matchers::{DerefTarget, NamedItem, deref_target, named_items};
pub use self::tooltip::{tooltip, Tooltip}; pub use self::tooltip::{Tooltip, tooltip};
use ecow::EcoString; use ecow::EcoString;
use typst::syntax::package::PackageSpec;
use typst::syntax::FileId;
use typst::World; use typst::World;
use typst::syntax::FileId;
use typst::syntax::package::PackageSpec;
/// Extends the `World` for IDE functionality. /// Extends the `World` for IDE functionality.
pub trait IdeWorld: World { pub trait IdeWorld: World {

View File

@ -1,9 +1,9 @@
use ecow::EcoString; use ecow::EcoString;
use typst::foundations::{Module, Value}; use typst::foundations::{Module, Value};
use typst::syntax::ast::AstNode; use typst::syntax::ast::AstNode;
use typst::syntax::{ast, LinkedNode, Span, SyntaxKind}; use typst::syntax::{LinkedNode, Span, SyntaxKind, ast};
use crate::{analyze_import, IdeWorld}; use crate::{IdeWorld, analyze_import};
/// Find the named items starting from the given position. /// Find the named items starting from the given position.
pub fn named_items<T>( pub fn named_items<T>(
@ -59,11 +59,11 @@ pub fn named_items<T>(
}; };
// Seeing the module itself. // Seeing the module itself.
if let Some((name, span)) = name_and_span { if let Some((name, span)) = name_and_span
if let Some(res) = recv(NamedItem::Module(&name, span, module)) { && let Some(res) = recv(NamedItem::Module(&name, span, module))
{
return Some(res); return Some(res);
} }
}
// Seeing the imported items. // Seeing the imported items.
match imports { match imports {
@ -124,8 +124,9 @@ pub fn named_items<T>(
} }
if let Some(parent) = node.parent() { if let Some(parent) = node.parent() {
if let Some(v) = parent.cast::<ast::ForLoop>() { if let Some(v) = parent.cast::<ast::ForLoop>()
if node.prev_sibling_kind() != Some(SyntaxKind::In) { && node.prev_sibling_kind() != Some(SyntaxKind::In)
{
let pattern = v.pattern(); let pattern = v.pattern();
for ident in pattern.bindings() { for ident in pattern.bindings() {
if let Some(res) = recv(NamedItem::Var(ident)) { if let Some(res) = recv(NamedItem::Var(ident)) {
@ -133,7 +134,6 @@ pub fn named_items<T>(
} }
} }
} }
}
if let Some(v) = parent.cast::<ast::Closure>().filter(|v| { if let Some(v) = parent.cast::<ast::Closure>().filter(|v| {
// Check if the node is in the body of the closure. // Check if the node is in the body of the closure.
@ -155,15 +155,15 @@ pub fn named_items<T>(
} }
} }
ast::Param::Spread(s) => { ast::Param::Spread(s) => {
if let Some(sink_ident) = s.sink_ident() { if let Some(sink_ident) = s.sink_ident()
if let Some(t) = recv(NamedItem::Var(sink_ident)) { && let Some(t) = recv(NamedItem::Var(sink_ident))
{
return Some(t); return Some(t);
} }
} }
} }
} }
} }
}
ancestor = Some(parent.clone()); ancestor = Some(parent.clone());
continue; continue;
@ -216,7 +216,7 @@ impl<'a> NamedItem<'a> {
/// Categorize an expression into common classes IDE functionality can operate /// Categorize an expression into common classes IDE functionality can operate
/// on. /// on.
pub fn deref_target(node: LinkedNode) -> Option<DerefTarget<'_>> { pub fn deref_target(node: LinkedNode<'_>) -> Option<DerefTarget<'_>> {
// Move to the first ancestor that is an expression. // Move to the first ancestor that is an expression.
let mut ancestor = node; let mut ancestor = node;
while !ancestor.is::<ast::Expr>() { while !ancestor.is::<ast::Expr>() {

View File

@ -9,7 +9,7 @@ use typst::layout::{Abs, Margin, PageElem};
use typst::syntax::package::{PackageSpec, PackageVersion}; use typst::syntax::package::{PackageSpec, PackageVersion};
use typst::syntax::{FileId, Source, VirtualPath}; use typst::syntax::{FileId, Source, VirtualPath};
use typst::text::{Font, FontBook, TextElem, TextSize}; use typst::text::{Font, FontBook, TextElem, TextSize};
use typst::utils::{singleton, LazyHash}; use typst::utils::{LazyHash, singleton};
use typst::{Feature, Library, LibraryExt, World}; use typst::{Feature, Library, LibraryExt, World};
use crate::IdeWorld; use crate::IdeWorld;

View File

@ -1,17 +1,16 @@
use std::fmt::Write; use std::fmt::Write;
use ecow::{eco_format, EcoString}; use ecow::{EcoString, eco_format};
use if_chain::if_chain;
use typst::engine::Sink; use typst::engine::Sink;
use typst::foundations::{repr, Binding, Capturer, CastInfo, Repr, Value}; use typst::foundations::{Binding, Capturer, CastInfo, Repr, Value, repr};
use typst::layout::{Length, PagedDocument}; use typst::layout::{Length, PagedDocument};
use typst::syntax::ast::AstNode; use typst::syntax::ast::AstNode;
use typst::syntax::{ast, LinkedNode, Side, Source, SyntaxKind}; use typst::syntax::{LinkedNode, Side, Source, SyntaxKind, ast};
use typst::utils::{round_with_precision, Numeric}; use typst::utils::{Numeric, round_with_precision};
use typst_eval::CapturesVisitor; use typst_eval::CapturesVisitor;
use crate::utils::{plain_docs_sentence, summarize_font_family}; use crate::utils::{plain_docs_sentence, summarize_font_family};
use crate::{analyze_expr, analyze_import, analyze_labels, IdeWorld}; use crate::{IdeWorld, analyze_expr, analyze_import, analyze_labels};
/// Describe the item under the cursor. /// Describe the item under the cursor.
/// ///
@ -66,12 +65,12 @@ fn expr_tooltip(world: &dyn IdeWorld, leaf: &LinkedNode) -> Option<Tooltip> {
return Some(Tooltip::Text(plain_docs_sentence(docs))); return Some(Tooltip::Text(plain_docs_sentence(docs)));
} }
if let &Value::Length(length) = value { if let &Value::Length(length) = value
if let Some(tooltip) = length_tooltip(length) { && let Some(tooltip) = length_tooltip(length)
{
return Some(tooltip); return Some(tooltip);
} }
} }
}
if expr.is_literal() { if expr.is_literal() {
return None; return None;
@ -93,11 +92,11 @@ fn expr_tooltip(world: &dyn IdeWorld, leaf: &LinkedNode) -> Option<Tooltip> {
last = Some((value, 1)); last = Some((value, 1));
} }
if let Some((_, count)) = last { if let Some((_, count)) = last
if count > 1 { && count > 1
{
write!(pieces.last_mut().unwrap(), " (×{count})").unwrap(); write!(pieces.last_mut().unwrap(), " (×{count})").unwrap();
} }
}
if iter.next().is_some() { if iter.next().is_some() {
pieces.push("...".into()); pieces.push("...".into());
@ -109,20 +108,18 @@ fn expr_tooltip(world: &dyn IdeWorld, leaf: &LinkedNode) -> Option<Tooltip> {
/// Tooltips for imports. /// Tooltips for imports.
fn import_tooltip(world: &dyn IdeWorld, leaf: &LinkedNode) -> Option<Tooltip> { fn import_tooltip(world: &dyn IdeWorld, leaf: &LinkedNode) -> Option<Tooltip> {
if_chain! { if leaf.kind() == SyntaxKind::Star
if leaf.kind() == SyntaxKind::Star; && let Some(parent) = leaf.parent()
if let Some(parent) = leaf.parent(); && let Some(import) = parent.cast::<ast::ModuleImport>()
if let Some(import) = parent.cast::<ast::ModuleImport>(); && let Some(node) = parent.find(import.source().span())
if let Some(node) = parent.find(import.source().span()); && let Some(value) = analyze_import(world, &node)
if let Some(value) = analyze_import(world, &node); && let Some(scope) = value.scope()
if let Some(scope) = value.scope(); {
then {
let names: Vec<_> = let names: Vec<_> =
scope.iter().map(|(name, ..)| eco_format!("`{name}`")).collect(); scope.iter().map(|(name, ..)| eco_format!("`{name}`")).collect();
let list = repr::separated_list(&names, "and"); let list = repr::separated_list(&names, "and");
return Some(Tooltip::Text(eco_format!("This star imports {list}"))); return Some(Tooltip::Text(eco_format!("This star imports {list}")));
} }
}
None None
} }
@ -190,51 +187,46 @@ fn label_tooltip(document: &PagedDocument, leaf: &LinkedNode) -> Option<Tooltip>
/// Tooltips for components of a named parameter. /// Tooltips for components of a named parameter.
fn named_param_tooltip(world: &dyn IdeWorld, leaf: &LinkedNode) -> Option<Tooltip> { fn named_param_tooltip(world: &dyn IdeWorld, leaf: &LinkedNode) -> Option<Tooltip> {
let (func, named) = if_chain! { let (func, named) =
// Ensure that we are in a named pair in the arguments to a function // Ensure that we are in a named pair in the arguments to a function
// call or set rule. // call or set rule.
if let Some(parent) = leaf.parent(); if let Some(parent) = leaf.parent()
if let Some(named) = parent.cast::<ast::Named>(); && let Some(named) = parent.cast::<ast::Named>()
if let Some(grand) = parent.parent(); && let Some(grand) = parent.parent()
if matches!(grand.kind(), SyntaxKind::Args); && matches!(grand.kind(), SyntaxKind::Args)
if let Some(grand_grand) = grand.parent(); && let Some(grand_grand) = grand.parent()
if let Some(expr) = grand_grand.cast::<ast::Expr>(); && let Some(expr) = grand_grand.cast::<ast::Expr>()
if let Some(ast::Expr::Ident(callee)) = match expr { && let Some(ast::Expr::Ident(callee)) = match expr {
ast::Expr::FuncCall(call) => Some(call.callee()), ast::Expr::FuncCall(call) => Some(call.callee()),
ast::Expr::SetRule(set) => Some(set.target()), ast::Expr::SetRule(set) => Some(set.target()),
_ => None, _ => None,
}; }
// Find metadata about the function. // Find metadata about the function.
if let Some(Value::Func(func)) = world && let Some(Value::Func(func)) = world
.library() .library()
.global .global
.scope() .scope()
.get(&callee) .get(&callee)
.map(Binding::read); .map(Binding::read)
then { (func, named) } { (func, named) }
else { return None; } else { return None; };
};
// Hovering over the parameter name. // Hovering over the parameter name.
if_chain! { if leaf.index() == 0
if leaf.index() == 0; && let Some(ident) = leaf.cast::<ast::Ident>()
if let Some(ident) = leaf.cast::<ast::Ident>(); && let Some(param) = func.param(&ident)
if let Some(param) = func.param(&ident); {
then {
return Some(Tooltip::Text(plain_docs_sentence(param.docs))); return Some(Tooltip::Text(plain_docs_sentence(param.docs)));
} }
}
// Hovering over a string parameter value. // Hovering over a string parameter value.
if_chain! { if let Some(string) = leaf.cast::<ast::Str>()
if let Some(string) = leaf.cast::<ast::Str>(); && let Some(param) = func.param(&named.name())
if let Some(param) = func.param(&named.name()); && let Some(docs) = find_string_doc(&param.input, &string.get())
if let Some(docs) = find_string_doc(&param.input, &string.get()); {
then {
return Some(Tooltip::Text(docs.into())); return Some(Tooltip::Text(docs.into()));
} }
}
None None
} }
@ -252,27 +244,24 @@ fn find_string_doc(info: &CastInfo, string: &str) -> Option<&'static str> {
/// Tooltip for font. /// Tooltip for font.
fn font_tooltip(world: &dyn IdeWorld, leaf: &LinkedNode) -> Option<Tooltip> { fn font_tooltip(world: &dyn IdeWorld, leaf: &LinkedNode) -> Option<Tooltip> {
if_chain! {
// Ensure that we are on top of a string. // Ensure that we are on top of a string.
if let Some(string) = leaf.cast::<ast::Str>(); if let Some(string) = leaf.cast::<ast::Str>()
let lower = string.get().to_lowercase(); && let lower = string.get().to_lowercase()
// Ensure that we are in the arguments to the text function. // Ensure that we are in the arguments to the text function.
if let Some(parent) = leaf.parent(); && let Some(parent) = leaf.parent()
if let Some(named) = parent.cast::<ast::Named>(); && let Some(named) = parent.cast::<ast::Named>()
if named.name().as_str() == "font"; && named.name().as_str() == "font"
// Find the font family. // Find the font family.
if let Some((_, iter)) = world && let Some((_, iter)) = world
.book() .book()
.families() .families()
.find(|&(family, _)| family.to_lowercase().as_str() == lower.as_str()); .find(|&(family, _)| family.to_lowercase().as_str() == lower.as_str())
{
then {
let detail = summarize_font_family(iter.collect()); let detail = summarize_font_family(iter.collect());
return Some(Tooltip::Text(detail)); return Some(Tooltip::Text(detail));
} }
};
None None
} }
@ -283,7 +272,7 @@ mod tests {
use typst::syntax::Side; use typst::syntax::Side;
use super::{tooltip, Tooltip}; use super::{Tooltip, tooltip};
use crate::tests::{FilePos, TestWorld, WorldLike}; use crate::tests::{FilePos, TestWorld, WorldLike};
type Response = Option<Tooltip>; type Response = Option<Tooltip>;

View File

@ -2,7 +2,7 @@ use std::fmt::Write;
use std::ops::ControlFlow; use std::ops::ControlFlow;
use comemo::Track; use comemo::Track;
use ecow::{eco_format, EcoString}; use ecow::{EcoString, eco_format};
use typst::engine::{Engine, Route, Sink, Traced}; use typst::engine::{Engine, Route, Sink, Traced};
use typst::foundations::{Scope, Value}; use typst::foundations::{Scope, Value};
use typst::introspection::Introspector; use typst::introspection::Introspector;
@ -119,11 +119,7 @@ pub fn globals<'a>(world: &'a dyn IdeWorld, leaf: &LinkedNode) -> &'a Scope {
.is_none_or(|prev| !matches!(prev.kind(), SyntaxKind::Hash)); .is_none_or(|prev| !matches!(prev.kind(), SyntaxKind::Hash));
let library = world.library(); let library = world.library();
if in_math { if in_math { library.math.scope() } else { library.global.scope() }
library.math.scope()
} else {
library.global.scope()
}
} }
/// Checks whether the given value or any of its constituent parts satisfy the /// Checks whether the given value or any of its constituent parts satisfy the

View File

@ -7,7 +7,7 @@ use std::path::{Path, PathBuf};
use ecow::eco_format; use ecow::eco_format;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use serde::Deserialize; use serde::Deserialize;
use typst_library::diag::{bail, PackageError, PackageResult, StrResult}; use typst_library::diag::{PackageError, PackageResult, StrResult, bail};
use typst_syntax::package::{PackageSpec, PackageVersion, VersionlessPackageSpec}; use typst_syntax::package::{PackageSpec, PackageVersion, VersionlessPackageSpec};
use crate::download::{Downloader, Progress}; use crate::download::{Downloader, Progress};
@ -189,7 +189,7 @@ impl PackageStorage {
} }
} }
Err(err) => { Err(err) => {
return Err(PackageError::NetworkFailed(Some(eco_format!("{err}")))) return Err(PackageError::NetworkFailed(Some(eco_format!("{err}"))));
} }
}; };

View File

@ -206,13 +206,11 @@ pub fn layout_multi_block(
let has_inset = !inset.is_zero(); let has_inset = !inset.is_zero();
let is_explicit = matches!(body, None | Some(BlockBody::Content(_))); let is_explicit = matches!(body, None | Some(BlockBody::Content(_)));
// Skip filling/stroking the first frame if it is empty and a non-empty // Skip filling, stroking and labeling the first frame if it is empty and
// one follows. // a non-empty one follows.
let mut skip_first = false; let mut skip_first = false;
if let [first, rest @ ..] = fragment.as_slice() { if let [first, rest @ ..] = fragment.as_slice() {
skip_first = has_fill_or_stroke skip_first = first.is_empty() && rest.iter().any(|frame| !frame.is_empty());
&& first.is_empty()
&& rest.iter().any(|frame| !frame.is_empty());
} }
// Post-process to apply insets, clipping, fills, and strokes. // Post-process to apply insets, clipping, fills, and strokes.
@ -244,7 +242,8 @@ pub fn layout_multi_block(
// Assign label to each frame in the fragment. // Assign label to each frame in the fragment.
if let Some(label) = elem.label() { if let Some(label) = elem.label() {
for frame in fragment.iter_mut() { // Skip empty orphan frames, as a label would make them non-empty.
for frame in fragment.iter_mut().skip(if skip_first { 1 } else { 0 }) {
frame.label(label); frame.label(label);
} }
} }
@ -407,11 +406,11 @@ fn distribute<'a>(
// If there is still something remaining, apply it to the // If there is still something remaining, apply it to the
// last region (it will overflow, but there's nothing else // last region (it will overflow, but there's nothing else
// we can do). // we can do).
if !remaining.approx_empty() { if !remaining.approx_empty()
if let Some(last) = buf.last_mut() { && let Some(last) = buf.last_mut()
{
*last += remaining; *last += remaining;
} }
}
// Distribute the heights to the first region and the // Distribute the heights to the first region and the
// backlog. There is no last region, since the height is // backlog. There is no last region, since the height is

View File

@ -2,10 +2,11 @@ use std::cell::{LazyCell, RefCell};
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::hash::Hash; use std::hash::Hash;
use bumpalo::boxed::Box as BumpBox;
use bumpalo::Bump; use bumpalo::Bump;
use bumpalo::boxed::Box as BumpBox;
use comemo::{Track, Tracked, TrackedMut}; use comemo::{Track, Tracked, TrackedMut};
use typst_library::diag::{bail, warning, SourceResult}; use typst_library::World;
use typst_library::diag::{SourceResult, bail, warning};
use typst_library::engine::{Engine, Route, Sink, Traced}; use typst_library::engine::{Engine, Route, Sink, Traced};
use typst_library::foundations::{Packed, Resolve, Smart, StyleChain}; use typst_library::foundations::{Packed, Resolve, Smart, StyleChain};
use typst_library::introspection::{ use typst_library::introspection::{
@ -19,10 +20,9 @@ use typst_library::layout::{
use typst_library::model::ParElem; use typst_library::model::ParElem;
use typst_library::routines::{Pair, Routines}; use typst_library::routines::{Pair, Routines};
use typst_library::text::TextElem; use typst_library::text::TextElem;
use typst_library::World;
use typst_utils::SliceExt; use typst_utils::SliceExt;
use super::{layout_multi_block, layout_single_block, FlowMode}; use super::{FlowMode, layout_multi_block, layout_single_block};
use crate::inline::ParSituation; use crate::inline::ParSituation;
use crate::modifiers::layout_and_modify; use crate::modifiers::layout_and_modify;
@ -459,6 +459,7 @@ impl<'a> MultiChild<'a> {
regions: Regions, regions: Regions,
) -> SourceResult<(Frame, Option<MultiSpill<'a, 'b>>)> { ) -> SourceResult<(Frame, Option<MultiSpill<'a, 'b>>)> {
let fragment = self.layout_full(engine, regions)?; let fragment = self.layout_full(engine, regions)?;
let exist_non_empty_frame = fragment.iter().any(|f| !f.is_empty());
// Extract the first frame. // Extract the first frame.
let mut frames = fragment.into_iter(); let mut frames = fragment.into_iter();
@ -468,6 +469,7 @@ impl<'a> MultiChild<'a> {
let mut spill = None; let mut spill = None;
if frames.next().is_some() { if frames.next().is_some() {
spill = Some(MultiSpill { spill = Some(MultiSpill {
exist_non_empty_frame,
multi: self, multi: self,
full: regions.full, full: regions.full,
first: regions.size.y, first: regions.size.y,
@ -539,6 +541,7 @@ fn layout_multi_impl(
/// The spilled remains of a `MultiChild` that broke across two regions. /// The spilled remains of a `MultiChild` that broke across two regions.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct MultiSpill<'a, 'b> { pub struct MultiSpill<'a, 'b> {
pub(super) exist_non_empty_frame: bool,
multi: &'b MultiChild<'a>, multi: &'b MultiChild<'a>,
first: Abs, first: Abs,
full: Abs, full: Abs,
@ -684,11 +687,11 @@ impl<T> CachedCell<T> {
let input_hash = typst_utils::hash128(&input); let input_hash = typst_utils::hash128(&input);
let mut slot = self.0.borrow_mut(); let mut slot = self.0.borrow_mut();
if let Some((hash, output)) = &*slot { if let Some((hash, output)) = &*slot
if *hash == input_hash { && *hash == input_hash
{
return output.clone(); return output.clone();
} }
}
let output = f(input); let output = f(input);
*slot = Some((input_hash, output.clone())); *slot = Some((input_hash, output.clone()));

View File

@ -18,7 +18,7 @@ use typst_syntax::Span;
use typst_utils::{NonZeroExt, Numeric}; use typst_utils::{NonZeroExt, Numeric};
use super::{ use super::{
distribute, Config, FlowMode, FlowResult, LineNumberConfig, PlacedChild, Stop, Work, Config, FlowMode, FlowResult, LineNumberConfig, PlacedChild, Stop, Work, distribute,
}; };
/// Composes the contents of a single page/region. A region can have multiple /// Composes the contents of a single page/region. A region can have multiple
@ -319,11 +319,7 @@ impl<'a, 'b> Composer<'a, 'b, '_, '_> {
let used = base.y - remaining; let used = base.y - remaining;
let half = need / 2.0; let half = need / 2.0;
let ratio = (used + half) / base.y; let ratio = (used + half) / base.y;
if ratio <= 0.5 { if ratio <= 0.5 { FixedAlignment::Start } else { FixedAlignment::End }
FixedAlignment::Start
} else {
FixedAlignment::End
}
}); });
// Select the insertion area where we'll put this float. // Select the insertion area where we'll put this float.

View File

@ -283,6 +283,13 @@ impl<'a, 'b> Distributor<'a, 'b, '_, '_, '_> {
// Lay out the block. // Lay out the block.
let (frame, spill) = multi.layout(self.composer.engine, self.regions)?; let (frame, spill) = multi.layout(self.composer.engine, self.regions)?;
if frame.is_empty() && spill.as_ref().is_some_and(|s| s.exist_non_empty_frame) {
// If the first frame is empty, but there are non-empty frames in
// the spill, the whole child should be put in the next region to
// avoid any invisible orphans at the end of this region.
return Err(Stop::Finish(false));
}
self.frame(frame, multi.align, multi.sticky, true)?; self.frame(frame, multi.align, multi.sticky, true)?;
// If the block didn't fully fit into the current region, save it into // If the block didn't fully fit into the current region, save it into

View File

@ -14,7 +14,8 @@ use std::rc::Rc;
use bumpalo::Bump; use bumpalo::Bump;
use comemo::{Track, Tracked, TrackedMut}; use comemo::{Track, Tracked, TrackedMut};
use ecow::EcoVec; use ecow::EcoVec;
use typst_library::diag::{bail, At, SourceDiagnostic, SourceResult}; use typst_library::World;
use typst_library::diag::{At, SourceDiagnostic, SourceResult, bail};
use typst_library::engine::{Engine, Route, Sink, Traced}; use typst_library::engine::{Engine, Route, Sink, Traced};
use typst_library::foundations::{Content, Packed, Resolve, StyleChain}; use typst_library::foundations::{Content, Packed, Resolve, StyleChain};
use typst_library::introspection::{ use typst_library::introspection::{
@ -27,14 +28,13 @@ use typst_library::layout::{
use typst_library::model::{FootnoteElem, FootnoteEntry, LineNumberingScope, ParLine}; use typst_library::model::{FootnoteElem, FootnoteEntry, LineNumberingScope, ParLine};
use typst_library::routines::{Arenas, FragmentKind, Pair, RealizationKind, Routines}; use typst_library::routines::{Arenas, FragmentKind, Pair, RealizationKind, Routines};
use typst_library::text::TextElem; use typst_library::text::TextElem;
use typst_library::World;
use typst_utils::{NonZeroExt, Numeric}; use typst_utils::{NonZeroExt, Numeric};
use self::block::{layout_multi_block, layout_single_block}; use self::block::{layout_multi_block, layout_single_block};
use self::collect::{ use self::collect::{
collect, Child, LineChild, MultiChild, MultiSpill, PlacedChild, SingleChild, Child, LineChild, MultiChild, MultiSpill, PlacedChild, SingleChild, collect,
}; };
use self::compose::{compose, Composer}; use self::compose::{Composer, compose};
use self::distribute::distribute; use self::distribute::distribute;
/// Lays out content into a single region, producing a single frame. /// Lays out content into a single region, producing a single frame.

View File

@ -1,6 +1,6 @@
use std::fmt::Debug; use std::fmt::Debug;
use typst_library::diag::{bail, SourceResult}; use typst_library::diag::{SourceResult, bail};
use typst_library::engine::Engine; use typst_library::engine::Engine;
use typst_library::foundations::{Resolve, StyleChain}; use typst_library::foundations::{Resolve, StyleChain};
use typst_library::layout::grid::resolve::{ use typst_library::layout::grid::resolve::{
@ -16,8 +16,8 @@ use typst_syntax::Span;
use typst_utils::Numeric; use typst_utils::Numeric;
use super::{ use super::{
generate_line_segments, hline_stroke_at_column, layout_cell, vline_stroke_at_row, LineSegment, Rowspan, UnbreakableRowGroup, generate_line_segments,
LineSegment, Rowspan, UnbreakableRowGroup, hline_stroke_at_column, layout_cell, vline_stroke_at_row,
}; };
/// Performs grid layout. /// Performs grid layout.
@ -274,32 +274,33 @@ impl<'a> GridLayouter<'a> {
pub fn layout(mut self, engine: &mut Engine) -> SourceResult<Fragment> { pub fn layout(mut self, engine: &mut Engine) -> SourceResult<Fragment> {
self.measure_columns(engine)?; self.measure_columns(engine)?;
if let Some(footer) = &self.grid.footer { if let Some(footer) = &self.grid.footer
if footer.repeated { && footer.repeated
{
// Ensure rows in the first region will be aware of the // Ensure rows in the first region will be aware of the
// possible presence of the footer. // possible presence of the footer.
self.prepare_footer(footer, engine, 0)?; self.prepare_footer(footer, engine, 0)?;
self.regions.size.y -= self.current.footer_height; self.regions.size.y -= self.current.footer_height;
self.current.initial_after_repeats = self.regions.size.y; self.current.initial_after_repeats = self.regions.size.y;
} }
}
let mut y = 0; let mut y = 0;
let mut consecutive_header_count = 0; let mut consecutive_header_count = 0;
while y < self.grid.rows.len() { while y < self.grid.rows.len() {
if let Some(next_header) = self.upcoming_headers.get(consecutive_header_count) if let Some(next_header) = self.upcoming_headers.get(consecutive_header_count)
&& next_header.range.contains(&y)
{ {
if next_header.range.contains(&y) {
self.place_new_headers(&mut consecutive_header_count, engine)?; self.place_new_headers(&mut consecutive_header_count, engine)?;
y = next_header.range.end; y = next_header.range.end;
// Skip header rows during normal layout. // Skip header rows during normal layout.
continue; continue;
} }
}
if let Some(footer) = &self.grid.footer { if let Some(footer) = &self.grid.footer
if footer.repeated && y >= footer.start { && footer.repeated
&& y >= footer.start
{
if y == footer.start { if y == footer.start {
self.layout_footer(footer, engine, self.finished.len())?; self.layout_footer(footer, engine, self.finished.len())?;
self.flush_orphans(); self.flush_orphans();
@ -307,7 +308,6 @@ impl<'a> GridLayouter<'a> {
y = footer.end; y = footer.end;
continue; continue;
} }
}
self.layout_row(y, engine, 0)?; self.layout_row(y, engine, 0)?;
@ -1228,7 +1228,7 @@ impl<'a> GridLayouter<'a> {
.skip(parent.y) .skip(parent.y)
.take(rowspan) .take(rowspan)
.rev() .rev()
.find(|(_, &row)| row == Sizing::Auto) .find(|&(_, &row)| row == Sizing::Auto)
.map(|(y, _)| y); .map(|(y, _)| y);
if last_spanned_auto_row != Some(y) { if last_spanned_auto_row != Some(y) {
@ -1283,15 +1283,13 @@ impl<'a> GridLayouter<'a> {
// remeasure. // remeasure.
if let Some([first, rest @ ..]) = if let Some([first, rest @ ..]) =
frames.get(measurement_data.frames_in_previous_regions..) frames.get(measurement_data.frames_in_previous_regions..)
{ && can_skip
if can_skip
&& breakable && breakable
&& first.is_empty() && first.is_empty()
&& rest.iter().any(|frame| !frame.is_empty()) && rest.iter().any(|frame| !frame.is_empty())
{ {
return Ok(None); return Ok(None);
} }
}
// Skip frames from previous regions if applicable. // Skip frames from previous regions if applicable.
let mut sizes = frames let mut sizes = frames
@ -1529,8 +1527,9 @@ impl<'a> GridLayouter<'a> {
// The latest rows have orphan prevention (headers) and no other rows // The latest rows have orphan prevention (headers) and no other rows
// were placed, so remove those rows and try again in a new region, // were placed, so remove those rows and try again in a new region,
// unless this is the last region. // unless this is the last region.
if let Some(orphan_snapshot) = self.current.lrows_orphan_snapshot.take() { if let Some(orphan_snapshot) = self.current.lrows_orphan_snapshot.take()
if !last { && !last
{
self.current.lrows.truncate(orphan_snapshot); self.current.lrows.truncate(orphan_snapshot);
self.current.repeated_header_rows = self.current.repeated_header_rows =
self.current.repeated_header_rows.min(orphan_snapshot); self.current.repeated_header_rows.min(orphan_snapshot);
@ -1540,7 +1539,6 @@ impl<'a> GridLayouter<'a> {
self.current.last_repeated_header_end = 0; self.current.last_repeated_header_end = 0;
} }
} }
}
if self if self
.current .current
@ -1571,8 +1569,7 @@ impl<'a> GridLayouter<'a> {
&& self.current.could_progress_at_top; && self.current.could_progress_at_top;
let mut laid_out_footer_start = None; let mut laid_out_footer_start = None;
if !footer_would_be_widow { if !footer_would_be_widow && let Some(footer) = &self.grid.footer {
if let Some(footer) = &self.grid.footer {
// Don't layout the footer if it would be alone with the header // Don't layout the footer if it would be alone with the header
// in the page (hence the widow check), and don't layout it // in the page (hence the widow check), and don't layout it
// twice (check below). // twice (check below).
@ -1587,7 +1584,6 @@ impl<'a> GridLayouter<'a> {
self.layout_footer(footer, engine, self.finished.len())?; self.layout_footer(footer, engine, self.finished.len())?;
} }
} }
}
// Determine the height of existing rows in the region. // Determine the height of existing rows in the region.
let mut used = Abs::zero(); let mut used = Abs::zero();

View File

@ -1,8 +1,8 @@
use std::sync::Arc; use std::sync::Arc;
use typst_library::foundations::{AlternativeFold, Fold}; use typst_library::foundations::{AlternativeFold, Fold};
use typst_library::layout::grid::resolve::{CellGrid, Line, Repeatable};
use typst_library::layout::Abs; use typst_library::layout::Abs;
use typst_library::layout::grid::resolve::{CellGrid, Line, Repeatable};
use typst_library::visualize::Stroke; use typst_library::visualize::Stroke;
use super::RowPiece; use super::RowPiece;
@ -291,14 +291,14 @@ pub fn vline_stroke_at_row(
// We would then analyze the cell one column after (if at a gutter // We would then analyze the cell one column after (if at a gutter
// column), and/or one row below (if at a gutter row), in order to // column), and/or one row below (if at a gutter row), in order to
// check if it would be merged with a cell before the vline. // check if it would be merged with a cell before the vline.
if let Some(parent) = grid.effective_parent_cell_position(x, y) { if let Some(parent) = grid.effective_parent_cell_position(x, y)
if parent.x < x { && parent.x < x
{
// There is a colspan cell going through this vline's position, // There is a colspan cell going through this vline's position,
// so don't draw it here. // so don't draw it here.
return None; return None;
} }
} }
}
let (left_cell_stroke, left_cell_prioritized) = x let (left_cell_stroke, left_cell_prioritized) = x
.checked_sub(1) .checked_sub(1)
@ -416,8 +416,9 @@ pub fn hline_stroke_at_column(
// We would then analyze the cell one column after (if at a gutter // We would then analyze the cell one column after (if at a gutter
// column), and/or one row below (if at a gutter row), in order to // column), and/or one row below (if at a gutter row), in order to
// check if it would be merged with a cell before the hline. // check if it would be merged with a cell before the hline.
if let Some(parent) = grid.effective_parent_cell_position(x, y) { if let Some(parent) = grid.effective_parent_cell_position(x, y)
if parent.y < y { && parent.y < y
{
// Get the first 'y' spanned by the possible rowspan in this region. // Get the first 'y' spanned by the possible rowspan in this region.
// The 'parent.y' row and any other spanned rows above 'y' could be // The 'parent.y' row and any other spanned rows above 'y' could be
// missing from this region, which could have lead the check above // missing from this region, which could have lead the check above
@ -438,7 +439,6 @@ pub fn hline_stroke_at_column(
} }
} }
} }
}
// When the hline is at the top of the region and this isn't the first // When the hline is at the top of the region and this isn't the first
// region, fold with the top stroke of the topmost cell at this column, // region, fold with the top stroke of the topmost cell at this column,

View File

@ -9,13 +9,13 @@ use typst_library::diag::SourceResult;
use typst_library::engine::Engine; use typst_library::engine::Engine;
use typst_library::foundations::{Packed, StyleChain}; use typst_library::foundations::{Packed, StyleChain};
use typst_library::introspection::Locator; use typst_library::introspection::Locator;
use typst_library::layout::grid::resolve::{grid_to_cellgrid, table_to_cellgrid, Cell}; use typst_library::layout::grid::resolve::{Cell, grid_to_cellgrid, table_to_cellgrid};
use typst_library::layout::{Fragment, GridElem, Regions}; use typst_library::layout::{Fragment, GridElem, Regions};
use typst_library::model::TableElem; use typst_library::model::TableElem;
use self::layouter::RowPiece; use self::layouter::RowPiece;
use self::lines::{ use self::lines::{
generate_line_segments, hline_stroke_at_column, vline_stroke_at_row, LineSegment, LineSegment, generate_line_segments, hline_stroke_at_column, vline_stroke_at_row,
}; };
use self::rowspans::{Rowspan, UnbreakableRowGroup}; use self::rowspans::{Rowspan, UnbreakableRowGroup};

View File

@ -240,8 +240,10 @@ impl<'a> GridLayouter<'a> {
self.current.initial_after_repeats = self.regions.size.y; self.current.initial_after_repeats = self.regions.size.y;
} }
if let Some(footer) = &self.grid.footer { if let Some(footer) = &self.grid.footer
if footer.repeated && skipped_region { && footer.repeated
&& skipped_region
{
// Simulate the footer again; the region's 'full' might have // Simulate the footer again; the region's 'full' might have
// changed. // changed.
self.regions.size.y += self.current.footer_height; self.regions.size.y += self.current.footer_height;
@ -250,7 +252,6 @@ impl<'a> GridLayouter<'a> {
.height; .height;
self.regions.size.y -= self.current.footer_height; self.regions.size.y -= self.current.footer_height;
} }
}
let repeating_header_rows = let repeating_header_rows =
total_header_row_count(self.repeating_headers.iter().copied()); total_header_row_count(self.repeating_headers.iter().copied());

View File

@ -4,8 +4,8 @@ use typst_library::foundations::Resolve;
use typst_library::layout::grid::resolve::Repeatable; use typst_library::layout::grid::resolve::Repeatable;
use typst_library::layout::{Abs, Axes, Frame, Point, Region, Regions, Size, Sizing}; use typst_library::layout::{Abs, Axes, Frame, Point, Region, Regions, Size, Sizing};
use super::layouter::{points, Row}; use super::layouter::{Row, points};
use super::{layout_cell, Cell, GridLayouter}; use super::{Cell, GridLayouter, layout_cell};
/// All information needed to layout a single rowspan. /// All information needed to layout a single rowspan.
pub struct Rowspan { pub struct Rowspan {
@ -238,8 +238,10 @@ impl GridLayouter<'_> {
// current row is dynamic and depends on the amount of upcoming // current row is dynamic and depends on the amount of upcoming
// unbreakable cells (with or without a rowspan setting). // unbreakable cells (with or without a rowspan setting).
let mut amount_unbreakable_rows = None; let mut amount_unbreakable_rows = None;
if let Some(footer) = &self.grid.footer { if let Some(footer) = &self.grid.footer
if !footer.repeated && current_row >= footer.start { && !footer.repeated
&& current_row >= footer.start
{
// Non-repeated footer, so keep it unbreakable. // Non-repeated footer, so keep it unbreakable.
// //
// TODO(subfooters): This will become unnecessary // TODO(subfooters): This will become unnecessary
@ -247,7 +249,6 @@ impl GridLayouter<'_> {
// have widow prevention. // have widow prevention.
amount_unbreakable_rows = Some(self.grid.rows.len() - footer.start); amount_unbreakable_rows = Some(self.grid.rows.len() - footer.start);
} }
}
let row_group = self.simulate_unbreakable_row_group( let row_group = self.simulate_unbreakable_row_group(
current_row, current_row,
@ -1268,9 +1269,9 @@ fn subtract_end_sizes(sizes: &mut Vec<Abs>, mut subtract: Abs) {
while subtract > Abs::zero() && sizes.last().is_some_and(|&size| size <= subtract) { while subtract > Abs::zero() && sizes.last().is_some_and(|&size| size <= subtract) {
subtract -= sizes.pop().unwrap(); subtract -= sizes.pop().unwrap();
} }
if subtract > Abs::zero() { if subtract > Abs::zero()
if let Some(last_size) = sizes.last_mut() { && let Some(last_size) = sizes.last_mut()
{
*last_size -= subtract; *last_size -= subtract;
} }
} }
}

View File

@ -6,14 +6,14 @@ use typst_library::layout::{
}; };
use typst_library::routines::Pair; use typst_library::routines::Pair;
use typst_library::text::{ use typst_library::text::{
is_default_ignorable, LinebreakElem, SmartQuoteElem, SmartQuoter, SmartQuotes, LinebreakElem, SmartQuoteElem, SmartQuoter, SmartQuotes, SpaceElem, TextElem,
SpaceElem, TextElem, is_default_ignorable,
}; };
use typst_syntax::Span; use typst_syntax::Span;
use typst_utils::Numeric; use typst_utils::Numeric;
use super::*; use super::*;
use crate::modifiers::{layout_and_modify, FrameModifiers, FrameModify}; use crate::modifiers::{FrameModifiers, FrameModify, layout_and_modify};
// The characters by which spacing, inline content and pins are replaced in the // The characters by which spacing, inline content and pins are replaced in the
// full text. // full text.
@ -274,12 +274,12 @@ impl<'a> Collector<'a> {
let segment_len = self.full.len() - prev; let segment_len = self.full.len() - prev;
// Merge adjacent text segments with the same styles. // Merge adjacent text segments with the same styles.
if let Some(Segment::Text(last_len, last_styles)) = self.segments.last_mut() { if let Some(Segment::Text(last_len, last_styles)) = self.segments.last_mut()
if *last_styles == styles { && *last_styles == styles
{
*last_len += segment_len; *last_len += segment_len;
return; return;
} }
}
self.segments.push(Segment::Text(segment_len, styles)); self.segments.push(Segment::Text(segment_len, styles));
} }

View File

@ -6,7 +6,7 @@ use typst_library::foundations::Resolve;
use typst_library::introspection::{SplitLocator, Tag}; use typst_library::introspection::{SplitLocator, Tag};
use typst_library::layout::{Abs, Dir, Em, Fr, Frame, FrameItem, Point}; use typst_library::layout::{Abs, Dir, Em, Fr, Frame, FrameItem, Point};
use typst_library::model::ParLineMarker; use typst_library::model::ParLineMarker;
use typst_library::text::{variant, Lang, TextElem}; use typst_library::text::{Lang, TextElem, variant};
use typst_utils::Numeric; use typst_utils::Numeric;
use super::*; use super::*;
@ -155,18 +155,18 @@ pub fn line<'a>(
let mut items = collect_items(engine, p, range, trim); let mut items = collect_items(engine, p, range, trim);
// Add a hyphen at the line start, if a previous dash should be repeated. // Add a hyphen at the line start, if a previous dash should be repeated.
if pred.is_some_and(|pred| should_repeat_hyphen(pred, full)) { if pred.is_some_and(|pred| should_repeat_hyphen(pred, full))
if let Some(shaped) = items.first_text_mut() { && let Some(shaped) = items.first_text_mut()
{
shaped.prepend_hyphen(engine, p.config.fallback); shaped.prepend_hyphen(engine, p.config.fallback);
} }
}
// Add a hyphen at the line end, if we ended on a soft hyphen. // Add a hyphen at the line end, if we ended on a soft hyphen.
if dash == Some(Dash::Soft) { if dash == Some(Dash::Soft)
if let Some(shaped) = items.last_text_mut() { && let Some(shaped) = items.last_text_mut()
{
shaped.push_hyphen(engine, p.config.fallback); shaped.push_hyphen(engine, p.config.fallback);
} }
}
// Deal with CJ characters at line boundaries. // Deal with CJ characters at line boundaries.
adjust_cj_at_line_boundaries(p, full, &mut items); adjust_cj_at_line_boundaries(p, full, &mut items);
@ -218,11 +218,11 @@ fn collect_items<'a>(
} }
// Add fallback text to expand the line height, if necessary. // Add fallback text to expand the line height, if necessary.
if !items.iter().any(|item| matches!(item, Item::Text(_))) { if !items.iter().any(|item| matches!(item, Item::Text(_)))
if let Some(fallback) = fallback { && let Some(fallback) = fallback
{
items.push(fallback, usize::MAX); items.push(fallback, usize::MAX);
} }
}
items items
} }
@ -461,9 +461,9 @@ pub fn commit(
} }
// Handle hanging punctuation to the left. // Handle hanging punctuation to the left.
if let Some(Item::Text(text)) = line.items.first() { if let Some(Item::Text(text)) = line.items.first()
if let Some(glyph) = text.glyphs.first() { && let Some(glyph) = text.glyphs.first()
if !text.dir.is_positive() && !text.dir.is_positive()
&& text.styles.get(TextElem::overhang) && text.styles.get(TextElem::overhang)
&& (line.items.len() > 1 || text.glyphs.len() > 1) && (line.items.len() > 1 || text.glyphs.len() > 1)
{ {
@ -471,21 +471,17 @@ pub fn commit(
offset -= amount; offset -= amount;
remaining += amount; remaining += amount;
} }
}
}
// Handle hanging punctuation to the right. // Handle hanging punctuation to the right.
if let Some(Item::Text(text)) = line.items.last() { if let Some(Item::Text(text)) = line.items.last()
if let Some(glyph) = text.glyphs.last() { && let Some(glyph) = text.glyphs.last()
if text.dir.is_positive() && text.dir.is_positive()
&& text.styles.get(TextElem::overhang) && text.styles.get(TextElem::overhang)
&& (line.items.len() > 1 || text.glyphs.len() > 1) && (line.items.len() > 1 || text.glyphs.len() > 1)
{ {
let amount = overhang(glyph.c) * glyph.x_advance.at(glyph.size); let amount = overhang(glyph.c) * glyph.x_advance.at(glyph.size);
remaining += amount; remaining += amount;
} }
}
}
// Determine how much additional space is needed. The justification_ratio is // Determine how much additional space is needed. The justification_ratio is
// for the first step justification, extra_justification is for the last // for the first step justification, extra_justification is for the last

View File

@ -2,8 +2,8 @@ use std::ops::{Add, Sub};
use std::sync::LazyLock; use std::sync::LazyLock;
use az::SaturatingAs; use az::SaturatingAs;
use icu_properties::maps::{CodePointMapData, CodePointMapDataBorrowed};
use icu_properties::LineBreak; use icu_properties::LineBreak;
use icu_properties::maps::{CodePointMapData, CodePointMapDataBorrowed};
use icu_provider::AsDeserializingBufferProvider; use icu_provider::AsDeserializingBufferProvider;
use icu_provider_adapters::fork::ForkByKeyProvider; use icu_provider_adapters::fork::ForkByKeyProvider;
use icu_provider_blob::BlobDataProvider; use icu_provider_blob::BlobDataProvider;
@ -11,7 +11,7 @@ use icu_segmenter::LineSegmenter;
use typst_library::engine::Engine; use typst_library::engine::Engine;
use typst_library::layout::{Abs, Em}; use typst_library::layout::{Abs, Em};
use typst_library::model::Linebreaks; use typst_library::model::Linebreaks;
use typst_library::text::{is_default_ignorable, Lang, TextElem}; use typst_library::text::{Lang, TextElem, is_default_ignorable};
use typst_syntax::link_prefix; use typst_syntax::link_prefix;
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
@ -136,13 +136,13 @@ fn linebreak_simple<'a>(
// If the line doesn't fit anymore, we push the last fitting attempt // If the line doesn't fit anymore, we push the last fitting attempt
// into the stack and rebuild the line from the attempt's end. The // into the stack and rebuild the line from the attempt's end. The
// resulting line cannot be broken up further. // resulting line cannot be broken up further.
if !width.fits(attempt.width) { if !width.fits(attempt.width)
if let Some((last_attempt, last_end)) = last.take() { && let Some((last_attempt, last_end)) = last.take()
{
lines.push(last_attempt); lines.push(last_attempt);
start = last_end; start = last_end;
attempt = line(engine, p, start..end, breakpoint, lines.last()); attempt = line(engine, p, start..end, breakpoint, lines.last());
} }
}
// Finish the current line if there is a mandatory line break (i.e. due // Finish the current line if there is a mandatory line break (i.e. due
// to "\n") or if the line doesn't fit horizontally already since then // to "\n") or if the line doesn't fit horizontally already since then
@ -894,11 +894,7 @@ impl CostMetrics {
/// we allow less because otherwise we get an invalid layout fairly often, /// we allow less because otherwise we get an invalid layout fairly often,
/// which makes our bound useless. /// which makes our bound useless.
fn min_ratio(&self, approx: bool) -> f64 { fn min_ratio(&self, approx: bool) -> f64 {
if approx { if approx { self.min_approx_ratio } else { self.min_ratio }
self.min_approx_ratio
} else {
self.min_ratio
}
} }
} }

View File

@ -12,6 +12,7 @@ pub use self::box_::layout_box;
pub use self::shaping::create_shape_plan; pub use self::shaping::create_shape_plan;
use comemo::{Track, Tracked, TrackedMut}; use comemo::{Track, Tracked, TrackedMut};
use typst_library::World;
use typst_library::diag::SourceResult; use typst_library::diag::SourceResult;
use typst_library::engine::{Engine, Route, Sink, Traced}; use typst_library::engine::{Engine, Route, Sink, Traced};
use typst_library::foundations::{Packed, Smart, StyleChain}; use typst_library::foundations::{Packed, Smart, StyleChain};
@ -23,18 +24,17 @@ use typst_library::model::{
}; };
use typst_library::routines::{Arenas, Pair, RealizationKind, Routines}; use typst_library::routines::{Arenas, Pair, RealizationKind, Routines};
use typst_library::text::{Costs, Lang, TextElem}; use typst_library::text::{Costs, Lang, TextElem};
use typst_library::World;
use typst_utils::{Numeric, SliceExt}; use typst_utils::{Numeric, SliceExt};
use self::collect::{collect, Item, Segment, SpanMapper}; use self::collect::{Item, Segment, SpanMapper, collect};
use self::deco::decorate; use self::deco::decorate;
use self::finalize::finalize; use self::finalize::finalize;
use self::line::{apply_shift, commit, line, Line}; use self::line::{Line, apply_shift, commit, line};
use self::linebreak::{linebreak, Breakpoint}; use self::linebreak::{Breakpoint, linebreak};
use self::prepare::{prepare, Preparation}; use self::prepare::{Preparation, prepare};
use self::shaping::{ use self::shaping::{
cjk_punct_style, is_of_cj_script, shape_range, ShapedGlyph, ShapedText, BEGIN_PUNCT_PAT, END_PUNCT_PAT, ShapedGlyph, ShapedText, cjk_punct_style,
BEGIN_PUNCT_PAT, END_PUNCT_PAT, is_of_cj_script, shape_range,
}; };
/// Range of a substring of text. /// Range of a substring of text.
@ -190,11 +190,7 @@ fn configuration(
Config { Config {
justify, justify,
linebreaks: base.linebreaks.unwrap_or_else(|| { linebreaks: base.linebreaks.unwrap_or_else(|| {
if justify { if justify { Linebreaks::Optimized } else { Linebreaks::Simple }
Linebreaks::Optimized
} else {
Linebreaks::Simple
}
}), }),
first_line_indent: { first_line_indent: {
let FirstLineIndent { amount, all } = base.first_line_indent; let FirstLineIndent { amount, all } = base.first_line_indent;

View File

@ -4,21 +4,21 @@ use std::sync::Arc;
use az::SaturatingAs; use az::SaturatingAs;
use rustybuzz::{BufferFlags, Feature, ShapePlan, UnicodeBuffer}; use rustybuzz::{BufferFlags, Feature, ShapePlan, UnicodeBuffer};
use ttf_parser::gsub::SubstitutionSubtable;
use ttf_parser::Tag; use ttf_parser::Tag;
use ttf_parser::gsub::SubstitutionSubtable;
use typst_library::World;
use typst_library::engine::Engine; use typst_library::engine::Engine;
use typst_library::foundations::{Smart, StyleChain}; use typst_library::foundations::{Smart, StyleChain};
use typst_library::layout::{Abs, Dir, Em, Frame, FrameItem, Point, Size}; use typst_library::layout::{Abs, Dir, Em, Frame, FrameItem, Point, Size};
use typst_library::text::{ use typst_library::text::{
families, features, is_default_ignorable, language, variant, Font, FontFamily, Font, FontFamily, FontVariant, Glyph, Lang, Region, ShiftSettings, TextEdgeBounds,
FontVariant, Glyph, Lang, Region, ShiftSettings, TextEdgeBounds, TextElem, TextItem, TextElem, TextItem, families, features, is_default_ignorable, language, variant,
}; };
use typst_library::World;
use typst_utils::SliceExt; use typst_utils::SliceExt;
use unicode_bidi::{BidiInfo, Level as BidiLevel}; use unicode_bidi::{BidiInfo, Level as BidiLevel};
use unicode_script::{Script, UnicodeScript}; use unicode_script::{Script, UnicodeScript};
use super::{decorate, Item, Range, SpanMapper}; use super::{Item, Range, SpanMapper, decorate};
use crate::modifiers::FrameModifyText; use crate::modifiers::FrameModifyText;
/// The result of shaping text. /// The result of shaping text.
@ -539,11 +539,7 @@ impl<'a> ShapedText<'a> {
// Find any glyph with the text index. // Find any glyph with the text index.
let found = self.glyphs.binary_search_by(|g: &ShapedGlyph| { let found = self.glyphs.binary_search_by(|g: &ShapedGlyph| {
let ordering = g.range.start.cmp(&text_index); let ordering = g.range.start.cmp(&text_index);
if ltr { if ltr { ordering } else { ordering.reverse() }
ordering
} else {
ordering.reverse()
}
}); });
let mut idx = match found { let mut idx = match found {

View File

@ -24,11 +24,7 @@ pub fn layout_list(
let body_indent = elem.body_indent.get(styles); let body_indent = elem.body_indent.get(styles);
let tight = elem.tight.get(styles); let tight = elem.tight.get(styles);
let gutter = elem.spacing.get(styles).unwrap_or_else(|| { let gutter = elem.spacing.get(styles).unwrap_or_else(|| {
if tight { if tight { styles.get(ParElem::leading) } else { styles.get(ParElem::spacing) }
styles.get(ParElem::leading)
} else {
styles.get(ParElem::spacing)
}
}); });
let Depth(depth) = styles.get(ListElem::depth); let Depth(depth) = styles.get(ListElem::depth);
@ -88,22 +84,15 @@ pub fn layout_enum(
let body_indent = elem.body_indent.get(styles); let body_indent = elem.body_indent.get(styles);
let tight = elem.tight.get(styles); let tight = elem.tight.get(styles);
let gutter = elem.spacing.get(styles).unwrap_or_else(|| { let gutter = elem.spacing.get(styles).unwrap_or_else(|| {
if tight { if tight { styles.get(ParElem::leading) } else { styles.get(ParElem::spacing) }
styles.get(ParElem::leading)
} else {
styles.get(ParElem::spacing)
}
}); });
let mut cells = vec![]; let mut cells = vec![];
let mut locator = locator.split(); let mut locator = locator.split();
let mut number = elem.start.get(styles).unwrap_or_else(|| { let mut number = elem
if reversed { .start
elem.children.len() as u64 .get(styles)
} else { .unwrap_or_else(|| if reversed { elem.children.len() as u64 } else { 1 });
1
}
});
let mut parents = styles.get_cloned(EnumElem::parents); let mut parents = styles.get_cloned(EnumElem::parents);
let full = elem.full.get(styles); let full = elem.full.get(styles);

View File

@ -4,8 +4,8 @@ use typst_library::layout::{Em, Frame, Point, Size};
use typst_library::math::AccentElem; use typst_library::math::AccentElem;
use super::{ use super::{
style_cramped, style_dtls, style_flac, FrameFragment, GlyphFragment, MathContext, FrameFragment, GlyphFragment, MathContext, MathFragment, style_cramped, style_dtls,
MathFragment, style_flac,
}; };
/// How much the accent can be shorter than the base. /// How much the accent can be shorter than the base.

View File

@ -7,8 +7,8 @@ use typst_library::math::{
use typst_utils::OptionExt; use typst_utils::OptionExt;
use super::{ use super::{
stretch_fragment, style_for_subscript, style_for_superscript, FrameFragment, Limits, FrameFragment, Limits, MathContext, MathFragment, stretch_fragment,
MathContext, MathFragment, style_for_subscript, style_for_superscript,
}; };
macro_rules! measure { macro_rules! measure {

View File

@ -7,8 +7,8 @@ use typst_library::visualize::{FixedStroke, Geometry};
use typst_syntax::Span; use typst_syntax::Span;
use super::{ use super::{
style_for_denominator, style_for_numerator, FrameFragment, GlyphFragment, DELIM_SHORT_FALL, FrameFragment, GlyphFragment, MathContext, style_for_denominator,
MathContext, DELIM_SHORT_FALL, style_for_numerator,
}; };
const FRAC_AROUND: Em = Em::new(0.1); const FRAC_AROUND: Em = Em::new(0.1);

View File

@ -2,18 +2,18 @@ use std::fmt::{self, Debug, Formatter};
use az::SaturatingAs; use az::SaturatingAs;
use rustybuzz::{BufferFlags, UnicodeBuffer}; use rustybuzz::{BufferFlags, UnicodeBuffer};
use ttf_parser::math::{GlyphAssembly, GlyphConstruction, GlyphPart};
use ttf_parser::GlyphId; use ttf_parser::GlyphId;
use typst_library::diag::{bail, warning, SourceResult}; use ttf_parser::math::{GlyphAssembly, GlyphConstruction, GlyphPart};
use typst_library::diag::{SourceResult, bail, warning};
use typst_library::foundations::StyleChain; use typst_library::foundations::StyleChain;
use typst_library::introspection::Tag; use typst_library::introspection::Tag;
use typst_library::layout::{ use typst_library::layout::{
Abs, Axes, Axis, Corner, Em, Frame, FrameItem, Point, Size, VAlignment, Abs, Axes, Axis, Corner, Em, Frame, FrameItem, Point, Size, VAlignment,
}; };
use typst_library::math::{EquationElem, MathSize}; use typst_library::math::{EquationElem, MathSize};
use typst_library::text::{features, language, Font, Glyph, TextElem, TextItem}; use typst_library::text::{Font, Glyph, TextElem, TextItem, features, language};
use typst_syntax::Span; use typst_syntax::Span;
use typst_utils::{default_math_class, Get}; use typst_utils::{Get, default_math_class};
use unicode_math_class::MathClass; use unicode_math_class::MathClass;
use super::MathContext; use super::MathContext;
@ -681,7 +681,11 @@ fn min_connector_overlap(font: &Font) -> Option<Em> {
.map(|variants| font.to_em(variants.min_connector_overlap)) .map(|variants| font.to_em(variants.min_connector_overlap))
} }
fn glyph_construction(font: &Font, id: GlyphId, axis: Axis) -> Option<GlyphConstruction> { fn glyph_construction(
font: &Font,
id: GlyphId,
axis: Axis,
) -> Option<GlyphConstruction<'_>> {
font.ttf() font.ttf()
.tables() .tables()
.math? .math?
@ -810,7 +814,10 @@ fn assemble(
/// Return an iterator over the assembly's parts with extenders repeated the /// Return an iterator over the assembly's parts with extenders repeated the
/// specified number of times. /// specified number of times.
fn parts(assembly: GlyphAssembly, repeat: usize) -> impl Iterator<Item = GlyphPart> + '_ { fn parts(
assembly: GlyphAssembly<'_>,
repeat: usize,
) -> impl Iterator<Item = GlyphPart> + '_ {
assembly.parts.into_iter().flat_map(move |part| { assembly.parts.into_iter().flat_map(move |part| {
let count = if part.part_flags.extender() { repeat } else { 1 }; let count = if part.part_flags.extender() { repeat } else { 1 };
std::iter::repeat_n(part, count) std::iter::repeat_n(part, count)

View File

@ -5,7 +5,7 @@ use typst_library::math::{EquationElem, LrElem, MidElem};
use typst_utils::SliceExt; use typst_utils::SliceExt;
use unicode_math_class::MathClass; use unicode_math_class::MathClass;
use super::{stretch_fragment, MathContext, MathFragment, DELIM_SHORT_FALL}; use super::{DELIM_SHORT_FALL, MathContext, MathFragment, stretch_fragment};
/// Lays out an [`LrElem`]. /// Lays out an [`LrElem`].
#[typst_macros::time(name = "math.lr", span = elem.span())] #[typst_macros::time(name = "math.lr", span = elem.span())]
@ -21,11 +21,11 @@ pub fn layout_lr(
} }
// Extract implicit LrElem. // Extract implicit LrElem.
if let Some(lr) = body.to_packed::<LrElem>() { if let Some(lr) = body.to_packed::<LrElem>()
if lr.size.get(styles).is_one() { && lr.size.get(styles).is_one()
{
body = &lr.body; body = &lr.body;
} }
}
let mut fragments = ctx.layout_into_fragments(body, styles)?; let mut fragments = ctx.layout_into_fragments(body, styles)?;
@ -55,13 +55,13 @@ pub fn layout_lr(
// Handle MathFragment::Glyph fragments that should be scaled up. // Handle MathFragment::Glyph fragments that should be scaled up.
for fragment in inner_fragments.iter_mut() { for fragment in inner_fragments.iter_mut() {
if let MathFragment::Glyph(ref mut glyph) = fragment { if let MathFragment::Glyph(glyph) = fragment
if glyph.mid_stretched == Some(false) { && glyph.mid_stretched == Some(false)
{
glyph.mid_stretched = Some(true); glyph.mid_stretched = Some(true);
scale(ctx, fragment, relative_to, height); scale(ctx, fragment, relative_to, height);
} }
} }
}
// Remove weak SpacingFragment immediately after the opening or immediately // Remove weak SpacingFragment immediately after the opening or immediately
// before the closing. // before the closing.
@ -95,7 +95,7 @@ pub fn layout_mid(
let mut fragments = ctx.layout_into_fragments(&elem.body, styles)?; let mut fragments = ctx.layout_into_fragments(&elem.body, styles)?;
for fragment in &mut fragments { for fragment in &mut fragments {
if let MathFragment::Glyph(ref mut glyph) = fragment { if let MathFragment::Glyph(glyph) = fragment {
glyph.mid_stretched = Some(false); glyph.mid_stretched = Some(false);
glyph.class = MathClass::Relation; glyph.class = MathClass::Relation;
} }

View File

@ -1,4 +1,4 @@
use typst_library::diag::{bail, warning, SourceResult}; use typst_library::diag::{SourceResult, bail, warning};
use typst_library::foundations::{Content, Packed, Resolve, StyleChain}; use typst_library::foundations::{Content, Packed, Resolve, StyleChain};
use typst_library::layout::{ use typst_library::layout::{
Abs, Axes, Em, FixedAlignment, Frame, FrameItem, Point, Ratio, Rel, Size, Abs, Axes, Em, FixedAlignment, Frame, FrameItem, Point, Ratio, Rel, Size,
@ -9,8 +9,8 @@ use typst_library::visualize::{FillRule, FixedStroke, Geometry, LineCap, Shape};
use typst_syntax::Span; use typst_syntax::Span;
use super::{ use super::{
alignments, style_for_denominator, AlignmentResult, FrameFragment, GlyphFragment, AlignmentResult, DELIM_SHORT_FALL, FrameFragment, GlyphFragment, LeftRightAlternator,
LeftRightAlternator, MathContext, DELIM_SHORT_FALL, MathContext, alignments, style_for_denominator,
}; };
const VERTICAL_PADDING: Ratio = Ratio::new(0.1); const VERTICAL_PADDING: Ratio = Ratio::new(0.1);

View File

@ -13,7 +13,8 @@ mod stretch;
mod text; mod text;
mod underover; mod underover;
use typst_library::diag::{bail, SourceResult}; use typst_library::World;
use typst_library::diag::{SourceResult, bail};
use typst_library::engine::Engine; use typst_library::engine::Engine;
use typst_library::foundations::{ use typst_library::foundations::{
Content, NativeElement, Packed, Resolve, StyleChain, SymbolElem, Content, NativeElement, Packed, Resolve, StyleChain, SymbolElem,
@ -28,15 +29,14 @@ use typst_library::math::*;
use typst_library::model::ParElem; use typst_library::model::ParElem;
use typst_library::routines::{Arenas, RealizationKind}; use typst_library::routines::{Arenas, RealizationKind};
use typst_library::text::{ use typst_library::text::{
families, variant, Font, LinebreakElem, SpaceElem, TextEdgeBounds, TextElem, Font, LinebreakElem, SpaceElem, TextEdgeBounds, TextElem, families, variant,
}; };
use typst_library::World;
use typst_syntax::Span; use typst_syntax::Span;
use typst_utils::Numeric; use typst_utils::Numeric;
use unicode_math_class::MathClass; use unicode_math_class::MathClass;
use self::fragment::{ use self::fragment::{
has_dtls_feat, stretch_axes, FrameFragment, GlyphFragment, Limits, MathFragment, FrameFragment, GlyphFragment, Limits, MathFragment, has_dtls_feat, stretch_axes,
}; };
use self::run::{LeftRightAlternator, MathRun, MathRunFrameBuilder}; use self::run::{LeftRightAlternator, MathRun, MathRunFrameBuilder};
use self::shared::*; use self::shared::*;
@ -603,13 +603,10 @@ fn layout_h(
ctx: &mut MathContext, ctx: &mut MathContext,
styles: StyleChain, styles: StyleChain,
) -> SourceResult<()> { ) -> SourceResult<()> {
if let Spacing::Rel(rel) = elem.amount { if let Spacing::Rel(rel) = elem.amount
if rel.rel.is_zero() { && rel.rel.is_zero()
ctx.push(MathFragment::Spacing( {
rel.abs.resolve(styles), ctx.push(MathFragment::Spacing(rel.abs.resolve(styles), elem.weak.get(styles)));
elem.weak.get(styles),
));
}
} }
Ok(()) Ok(())
} }

View File

@ -5,7 +5,7 @@ use typst_library::math::{EquationElem, MathSize, RootElem};
use typst_library::text::TextElem; use typst_library::text::TextElem;
use typst_library::visualize::{FixedStroke, Geometry}; use typst_library::visualize::{FixedStroke, Geometry};
use super::{style_cramped, FrameFragment, GlyphFragment, MathContext}; use super::{FrameFragment, GlyphFragment, MathContext, style_cramped};
/// Lays out a [`RootElem`]. /// Lays out a [`RootElem`].
/// ///

View File

@ -2,11 +2,11 @@ use std::iter::once;
use typst_library::foundations::{Resolve, StyleChain}; use typst_library::foundations::{Resolve, StyleChain};
use typst_library::layout::{Abs, AlignElem, Em, Frame, InlineItem, Point, Size}; use typst_library::layout::{Abs, AlignElem, Em, Frame, InlineItem, Point, Size};
use typst_library::math::{EquationElem, MathSize, MEDIUM, THICK, THIN}; use typst_library::math::{EquationElem, MEDIUM, MathSize, THICK, THIN};
use typst_library::model::ParElem; use typst_library::model::ParElem;
use unicode_math_class::MathClass; use unicode_math_class::MathClass;
use super::{alignments, FrameFragment, MathFragment}; use super::{FrameFragment, MathFragment, alignments};
const TIGHT_LEADING: Em = Em::new(0.25); const TIGHT_LEADING: Em = Em::new(0.25);
@ -87,11 +87,11 @@ impl MathRun {
// Insert spacing between the last and this non-ignorant item. // Insert spacing between the last and this non-ignorant item.
if !fragment.is_ignorant() { if !fragment.is_ignorant() {
if let Some(i) = last { if let Some(i) = last
if let Some(s) = spacing(&resolved[i], space.take(), &fragment) { && let Some(s) = spacing(&resolved[i], space.take(), &fragment)
{
resolved.insert(i + 1, s); resolved.insert(i + 1, s);
} }
}
last = Some(resolved.len()); last = Some(resolved.len());
} }
@ -123,11 +123,11 @@ impl MathRun {
1 + self.0.iter().filter(|f| matches!(f, MathFragment::Linebreak)).count(); 1 + self.0.iter().filter(|f| matches!(f, MathFragment::Linebreak)).count();
// A linebreak at the very end does not introduce an extra row. // A linebreak at the very end does not introduce an extra row.
if let Some(f) = self.0.last() { if let Some(f) = self.0.last()
if matches!(f, MathFragment::Linebreak) { && matches!(f, MathFragment::Linebreak)
{
count -= 1 count -= 1
} }
}
count count
} }
@ -344,11 +344,11 @@ impl MathRun {
descent = Abs::zero(); descent = Abs::zero();
space_is_visible = true; space_is_visible = true;
if let Some(f_next) = iter.peek() { if let Some(f_next) = iter.peek()
if !is_space(f_next) { && !is_space(f_next)
{
items.push(InlineItem::Space(Abs::zero(), true)); items.push(InlineItem::Space(Abs::zero(), true));
} }
}
} else { } else {
space_is_visible = false; space_is_visible = false;
} }

View File

@ -1,5 +1,5 @@
use ttf_parser::math::MathValue;
use ttf_parser::Tag; use ttf_parser::Tag;
use ttf_parser::math::MathValue;
use typst_library::foundations::{Style, StyleChain}; use typst_library::foundations::{Style, StyleChain};
use typst_library::layout::{Abs, Em, FixedAlignment, Frame, Point, Size}; use typst_library::layout::{Abs, Em, FixedAlignment, Frame, Point, Size};
use typst_library::math::{EquationElem, MathSize}; use typst_library::math::{EquationElem, MathSize};

View File

@ -1,10 +1,10 @@
use typst_library::diag::{warning, SourceResult}; use typst_library::diag::{SourceResult, warning};
use typst_library::foundations::{Packed, StyleChain}; use typst_library::foundations::{Packed, StyleChain};
use typst_library::layout::{Abs, Axis, Rel}; use typst_library::layout::{Abs, Axis, Rel};
use typst_library::math::StretchElem; use typst_library::math::StretchElem;
use typst_utils::Get; use typst_utils::Get;
use super::{stretch_axes, MathContext, MathFragment}; use super::{MathContext, MathFragment, stretch_axes};
/// Lays out a [`StretchElem`]. /// Lays out a [`StretchElem`].
#[typst_macros::time(name = "math.stretch", span = elem.span())] #[typst_macros::time(name = "math.stretch", span = elem.span())]
@ -37,7 +37,7 @@ pub fn stretch_fragment(
) { ) {
let size = fragment.size(); let size = fragment.size();
let MathFragment::Glyph(ref mut glyph) = fragment else { return }; let MathFragment::Glyph(glyph) = fragment else { return };
// Return if we attempt to stretch along an axis which isn't stretchable, // Return if we attempt to stretch along an axis which isn't stretchable,
// so that the original fragment isn't modified. // so that the original fragment isn't modified.

View File

@ -1,6 +1,6 @@
use std::f64::consts::SQRT_2; use std::f64::consts::SQRT_2;
use codex::styling::{to_style, MathStyle}; use codex::styling::{MathStyle, to_style};
use ecow::EcoString; use ecow::EcoString;
use typst_library::diag::SourceResult; use typst_library::diag::SourceResult;
use typst_library::foundations::{Packed, StyleChain, SymbolElem}; use typst_library::foundations::{Packed, StyleChain, SymbolElem};
@ -9,13 +9,13 @@ use typst_library::math::{EquationElem, MathSize};
use typst_library::text::{ use typst_library::text::{
BottomEdge, BottomEdgeMetric, TextElem, TopEdge, TopEdgeMetric, BottomEdge, BottomEdgeMetric, TextElem, TopEdge, TopEdgeMetric,
}; };
use typst_syntax::{is_newline, Span}; use typst_syntax::{Span, is_newline};
use unicode_math_class::MathClass; use unicode_math_class::MathClass;
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use super::{ use super::{
has_dtls_feat, style_dtls, FrameFragment, GlyphFragment, MathContext, MathFragment, FrameFragment, GlyphFragment, MathContext, MathFragment, MathRun, has_dtls_feat,
MathRun, style_dtls,
}; };
/// Lays out a [`TextElem`]. /// Lays out a [`TextElem`].

View File

@ -10,8 +10,8 @@ use typst_library::visualize::{FixedStroke, Geometry};
use typst_syntax::Span; use typst_syntax::Span;
use super::{ use super::{
stack, style_cramped, style_for_subscript, style_for_superscript, FrameFragment, FrameFragment, GlyphFragment, LeftRightAlternator, MathContext, MathRun, stack,
GlyphFragment, LeftRightAlternator, MathContext, MathRun, style_cramped, style_for_subscript, style_for_superscript,
}; };
const BRACE_GAP: Em = Em::new(0.25); const BRACE_GAP: Em = Em::new(0.25);

View File

@ -7,6 +7,7 @@ mod run;
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use comemo::{Tracked, TrackedMut}; use comemo::{Tracked, TrackedMut};
use typst_library::World;
use typst_library::diag::SourceResult; use typst_library::diag::SourceResult;
use typst_library::engine::{Engine, Route, Sink, Traced}; use typst_library::engine::{Engine, Route, Sink, Traced};
use typst_library::foundations::{Content, StyleChain}; use typst_library::foundations::{Content, StyleChain};
@ -16,11 +17,10 @@ use typst_library::introspection::{
use typst_library::layout::{FrameItem, Page, PagedDocument, Point, Transform}; use typst_library::layout::{FrameItem, Page, PagedDocument, Point, Transform};
use typst_library::model::DocumentInfo; use typst_library::model::DocumentInfo;
use typst_library::routines::{Arenas, Pair, RealizationKind, Routines}; use typst_library::routines::{Arenas, Pair, RealizationKind, Routines};
use typst_library::World;
use self::collect::{collect, Item}; use self::collect::{Item, collect};
use self::finalize::finalize; use self::finalize::finalize;
use self::run::{layout_blank_page, layout_page_run, LayoutedPage}; use self::run::{LayoutedPage, layout_blank_page, layout_page_run};
/// Layout content into a document. /// Layout content into a document.
/// ///

View File

@ -1,4 +1,5 @@
use comemo::{Track, Tracked, TrackedMut}; use comemo::{Track, Tracked, TrackedMut};
use typst_library::World;
use typst_library::diag::SourceResult; use typst_library::diag::SourceResult;
use typst_library::engine::{Engine, Route, Sink, Traced}; use typst_library::engine::{Engine, Route, Sink, Traced};
use typst_library::foundations::{ use typst_library::foundations::{
@ -16,10 +17,9 @@ use typst_library::model::Numbering;
use typst_library::routines::{Pair, Routines}; use typst_library::routines::{Pair, Routines};
use typst_library::text::{LocalName, TextElem}; use typst_library::text::{LocalName, TextElem};
use typst_library::visualize::Paint; use typst_library::visualize::Paint;
use typst_library::World;
use typst_utils::Numeric; use typst_utils::Numeric;
use crate::flow::{layout_flow, FlowMode}; use crate::flow::{FlowMode, layout_flow};
/// A mostly finished layout for one page. Needs only knowledge of its exact /// A mostly finished layout for one page. Needs only knowledge of its exact
/// page number to be finalized into a `Page`. (Because the margins can depend /// page number to be finalized into a `Page`. (Because the margins can depend

View File

@ -1,4 +1,4 @@
use typst_library::diag::{bail, SourceResult}; use typst_library::diag::{SourceResult, bail};
use typst_library::engine::Engine; use typst_library::engine::Engine;
use typst_library::foundations::{Packed, Resolve, StyleChain}; use typst_library::foundations::{Packed, Resolve, StyleChain};
use typst_library::introspection::Locator; use typst_library::introspection::Locator;

View File

@ -1,12 +1,12 @@
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use comemo::Track; use comemo::Track;
use ecow::{eco_format, EcoVec}; use ecow::{EcoVec, eco_format};
use smallvec::smallvec; use smallvec::smallvec;
use typst_library::diag::{bail, At, SourceResult}; use typst_library::diag::{At, SourceResult, bail};
use typst_library::foundations::{ use typst_library::foundations::{
dict, Content, Context, NativeElement, NativeRuleMap, Packed, Resolve, ShowFn, Smart, Content, Context, NativeElement, NativeRuleMap, Packed, Resolve, ShowFn, Smart,
StyleChain, Target, StyleChain, Target, dict,
}; };
use typst_library::introspection::{Counter, Locator, LocatorLink}; use typst_library::introspection::{Counter, Locator, LocatorLink};
use typst_library::layout::{ use typst_library::layout::{
@ -161,11 +161,7 @@ const TERMS_RULE: ShowFn<TermsElem> = |elem, _, styles| {
let indent = elem.indent.get(styles); let indent = elem.indent.get(styles);
let hanging_indent = elem.hanging_indent.get(styles); let hanging_indent = elem.hanging_indent.get(styles);
let gutter = elem.spacing.get(styles).unwrap_or_else(|| { let gutter = elem.spacing.get(styles).unwrap_or_else(|| {
if tight { if tight { styles.get(ParElem::leading) } else { styles.get(ParElem::spacing) }
styles.get(ParElem::leading)
} else {
styles.get(ParElem::spacing)
}
}); });
let pad = hanging_indent + indent; let pad = hanging_indent + indent;

View File

@ -1,7 +1,7 @@
use std::f64::consts::SQRT_2; use std::f64::consts::SQRT_2;
use kurbo::{CubicBez, ParamCurveExtrema}; use kurbo::{CubicBez, ParamCurveExtrema};
use typst_library::diag::{bail, SourceResult}; use typst_library::diag::{SourceResult, bail};
use typst_library::engine::Engine; use typst_library::engine::Engine;
use typst_library::foundations::{Content, Packed, Resolve, Smart, StyleChain}; use typst_library::foundations::{Content, Packed, Resolve, Smart, StyleChain};
use typst_library::introspection::Locator; use typst_library::introspection::Locator;

View File

@ -1,4 +1,4 @@
use typst_library::diag::{bail, SourceResult}; use typst_library::diag::{SourceResult, bail};
use typst_library::engine::Engine; use typst_library::engine::Engine;
use typst_library::foundations::{Content, Packed, Resolve, StyleChain, StyledElem}; use typst_library::foundations::{Content, Packed, Resolve, StyleChain, StyledElem};
use typst_library::introspection::{Locator, SplitLocator}; use typst_library::introspection::{Locator, SplitLocator};

View File

@ -1,6 +1,6 @@
use std::cell::LazyCell; use std::cell::LazyCell;
use typst_library::diag::{bail, SourceResult}; use typst_library::diag::{SourceResult, bail};
use typst_library::engine::Engine; use typst_library::engine::Engine;
use typst_library::foundations::{Content, Packed, Resolve, Smart, StyleChain}; use typst_library::foundations::{Content, Packed, Resolve, Smart, StyleChain};
use typst_library::introspection::Locator; use typst_library::introspection::Locator;

View File

@ -8,7 +8,7 @@ use std::string::FromUtf8Error;
use az::SaturatingAs; use az::SaturatingAs;
use comemo::Tracked; use comemo::Tracked;
use ecow::{eco_vec, EcoVec}; use ecow::{EcoVec, eco_vec};
use typst_syntax::package::{PackageSpec, PackageVersion}; use typst_syntax::package::{PackageSpec, PackageVersion};
use typst_syntax::{Lines, Span, Spanned, SyntaxError}; use typst_syntax::{Lines, Span, Spanned, SyntaxError};
use utf8_iter::ErrorReportingUtf8Chars; use utf8_iter::ErrorReportingUtf8Chars;
@ -296,14 +296,13 @@ impl<T> Trace<T> for SourceResult<T> {
let Some(trace_range) = world.range(span) else { return errors }; let Some(trace_range) = world.range(span) else { return errors };
for error in errors.make_mut().iter_mut() { for error in errors.make_mut().iter_mut() {
// Skip traces that surround the error. // Skip traces that surround the error.
if let Some(error_range) = world.range(error.span) { if let Some(error_range) = world.range(error.span)
if error.span.id() == span.id() && error.span.id() == span.id()
&& trace_range.start <= error_range.start && trace_range.start <= error_range.start
&& trace_range.end >= error_range.end && trace_range.end >= error_range.end
{ {
continue; continue;
} }
}
error.trace.push(Spanned::new(make_point(), span)); error.trace.push(Spanned::new(make_point(), span));
} }
@ -839,7 +838,9 @@ pub fn format_xml_like_error(format: &str, error: roxmltree::Error) -> LoadError
let pos = LineCol::one_based(error.pos().row as usize, error.pos().col as usize); let pos = LineCol::one_based(error.pos().row as usize, error.pos().col as usize);
let message = match error { let message = match error {
roxmltree::Error::UnexpectedCloseTag(expected, actual, _) => { roxmltree::Error::UnexpectedCloseTag(expected, actual, _) => {
eco_format!("failed to parse {format} (found closing tag '{actual}' instead of '{expected}')") eco_format!(
"failed to parse {format} (found closing tag '{actual}' instead of '{expected}')"
)
} }
roxmltree::Error::UnknownEntityReference(entity, _) => { roxmltree::Error::UnknownEntityReference(entity, _) => {
eco_format!("failed to parse {format} (unknown entity '{entity}')") eco_format!("failed to parse {format} (unknown entity '{entity}')")

View File

@ -8,11 +8,11 @@ use ecow::EcoVec;
use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
use typst_syntax::{FileId, Span}; use typst_syntax::{FileId, Span};
use crate::diag::{bail, HintedStrResult, SourceDiagnostic, SourceResult, StrResult}; use crate::World;
use crate::diag::{HintedStrResult, SourceDiagnostic, SourceResult, StrResult, bail};
use crate::foundations::{Styles, Value}; use crate::foundations::{Styles, Value};
use crate::introspection::Introspector; use crate::introspection::Introspector;
use crate::routines::Routines; use crate::routines::Routines;
use crate::World;
/// Holds all data needed during compilation. /// Holds all data needed during compilation.
pub struct Engine<'a> { pub struct Engine<'a> {
@ -47,7 +47,11 @@ impl Engine<'_> {
} }
/// Runs tasks on the engine in parallel. /// Runs tasks on the engine in parallel.
pub fn parallelize<P, I, T, U, F>(&mut self, iter: P, f: F) -> impl Iterator<Item = U> pub fn parallelize<P, I, T, U, F>(
&mut self,
iter: P,
f: F,
) -> impl Iterator<Item = U> + use<P, I, T, U, F>
where where
P: IntoIterator<IntoIter = I>, P: IntoIterator<IntoIter = I>,
I: Iterator<Item = T>, I: Iterator<Item = T>,
@ -111,11 +115,7 @@ impl Traced {
/// We hide the span if it isn't in the given file so that only results for /// We hide the span if it isn't in the given file so that only results for
/// the file with the traced span are invalidated. /// the file with the traced span are invalidated.
pub fn get(&self, id: FileId) -> Option<Span> { pub fn get(&self, id: FileId) -> Option<Span> {
if self.0.and_then(Span::id) == Some(id) { if self.0.and_then(Span::id) == Some(id) { self.0 } else { None }
self.0
} else {
None
}
} }
} }

View File

@ -1,12 +1,12 @@
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::ops::Add; use std::ops::Add;
use ecow::{eco_format, eco_vec, EcoString, EcoVec}; use ecow::{EcoString, EcoVec, eco_format, eco_vec};
use typst_syntax::{Span, Spanned}; use typst_syntax::{Span, Spanned};
use crate::diag::{bail, error, At, SourceDiagnostic, SourceResult, StrResult}; use crate::diag::{At, SourceDiagnostic, SourceResult, StrResult, bail, error};
use crate::foundations::{ use crate::foundations::{
cast, func, repr, scope, ty, Array, Dict, FromValue, IntoValue, Repr, Str, Value, Array, Dict, FromValue, IntoValue, Repr, Str, Value, cast, func, repr, scope, ty,
}; };
/// Captured arguments to a function. /// Captured arguments to a function.

View File

@ -4,16 +4,16 @@ use std::num::{NonZeroI64, NonZeroUsize};
use std::ops::{Add, AddAssign}; use std::ops::{Add, AddAssign};
use comemo::Tracked; use comemo::Tracked;
use ecow::{eco_format, EcoString, EcoVec}; use ecow::{EcoString, EcoVec, eco_format};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use smallvec::SmallVec; use smallvec::SmallVec;
use typst_syntax::{Span, Spanned}; use typst_syntax::{Span, Spanned};
use crate::diag::{bail, At, HintedStrResult, SourceDiagnostic, SourceResult, StrResult}; use crate::diag::{At, HintedStrResult, SourceDiagnostic, SourceResult, StrResult, bail};
use crate::engine::Engine; use crate::engine::Engine;
use crate::foundations::{ use crate::foundations::{
cast, func, ops, repr, scope, ty, Args, Bytes, CastInfo, Context, Dict, FromValue, Args, Bytes, CastInfo, Context, Dict, FromValue, Func, IntoValue, Reflect, Repr, Str,
Func, IntoValue, Reflect, Repr, Str, Value, Version, Value, Version, cast, func, ops, repr, scope, ty,
}; };
/// Create a new [`Array`] from values. /// Create a new [`Array`] from values.

View File

@ -4,8 +4,8 @@ use ecow::EcoString;
use crate::diag::HintedStrResult; use crate::diag::HintedStrResult;
use crate::foundations::{ use crate::foundations::{
ty, CastInfo, Fold, FromValue, IntoValue, Reflect, Repr, Resolve, StyleChain, Type, CastInfo, Fold, FromValue, IntoValue, Reflect, Repr, Resolve, StyleChain, Type,
Value, Value, ty,
}; };
/// A value that indicates a smart default. /// A value that indicates a smart default.

View File

@ -1,6 +1,6 @@
use ecow::EcoString; use ecow::EcoString;
use crate::foundations::{ty, Repr}; use crate::foundations::{Repr, ty};
/// A type with two states. /// A type with two states.
/// ///

View File

@ -5,13 +5,13 @@ use std::ops::{Add, AddAssign, Deref};
use std::str::Utf8Error; use std::str::Utf8Error;
use std::sync::Arc; use std::sync::Arc;
use ecow::{eco_format, EcoString}; use ecow::{EcoString, eco_format};
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use typst_syntax::Lines; use typst_syntax::Lines;
use typst_utils::LazyHash; use typst_utils::LazyHash;
use crate::diag::{bail, StrResult}; use crate::diag::{StrResult, bail};
use crate::foundations::{cast, func, scope, ty, Array, Reflect, Repr, Str, Value}; use crate::foundations::{Array, Reflect, Repr, Str, Value, cast, func, scope, ty};
/// A sequence of bytes. /// A sequence of bytes.
/// ///

View File

@ -7,8 +7,8 @@ use az::SaturatingAs;
use typst_syntax::{Span, Spanned}; use typst_syntax::{Span, Spanned};
use typst_utils::{round_int_with_precision, round_with_precision}; use typst_utils::{round_int_with_precision, round_with_precision};
use crate::diag::{bail, At, HintedString, SourceResult, StrResult}; use crate::diag::{At, HintedString, SourceResult, StrResult, bail};
use crate::foundations::{cast, func, ops, Decimal, IntoValue, Module, Scope, Value}; use crate::foundations::{Decimal, IntoValue, Module, Scope, Value, cast, func, ops};
use crate::layout::{Angle, Fr, Length, Ratio}; use crate::layout::{Angle, Fr, Length, Ratio};
/// A module with calculation definitions. /// A module with calculation definitions.

View File

@ -14,7 +14,7 @@ use unicode_math_class::MathClass;
use crate::diag::{At, HintedStrResult, HintedString, SourceResult, StrResult}; use crate::diag::{At, HintedStrResult, HintedString, SourceResult, StrResult};
use crate::foundations::{ use crate::foundations::{
array, repr, Fold, NativeElement, Packed, Repr, Str, Type, Value, Fold, NativeElement, Packed, Repr, Str, Type, Value, array, repr,
}; };
/// Determine details of a type. /// Determine details of a type.
@ -347,14 +347,15 @@ impl CastInfo {
msg.hint(eco_format!("use `label({})` to create a label", s.repr())); msg.hint(eco_format!("use `label({})` to create a label", s.repr()));
} }
} }
} else if let Value::Decimal(_) = found { } else if let Value::Decimal(_) = found
if !matching_type && parts.iter().any(|p| p == "float") { && !matching_type
&& parts.iter().any(|p| p == "float")
{
msg.hint(eco_format!( msg.hint(eco_format!(
"if loss of precision is acceptable, explicitly cast the \ "if loss of precision is acceptable, explicitly cast the \
decimal to a float with `float(value)`" decimal to a float with `float(value)`"
)); ));
} }
}
msg msg
} }

View File

@ -11,8 +11,8 @@ use typst_utils::Static;
use crate::diag::SourceResult; use crate::diag::SourceResult;
use crate::engine::Engine; use crate::engine::Engine;
use crate::foundations::{ use crate::foundations::{
cast, Args, Content, ContentVtable, FieldAccessError, Func, ParamInfo, Repr, Scope, Args, Content, ContentVtable, FieldAccessError, Func, ParamInfo, Repr, Scope,
Selector, StyleChain, Styles, Value, Selector, StyleChain, Styles, Value, cast,
}; };
use crate::text::{Lang, Region}; use crate::text::{Lang, Region};

View File

@ -3,7 +3,7 @@ use std::hash::Hash;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::sync::OnceLock; use std::sync::OnceLock;
use ecow::{eco_format, EcoString}; use ecow::{EcoString, eco_format};
use crate::foundations::{ use crate::foundations::{
Container, Content, FieldVtable, Fold, FoldFn, IntoValue, NativeElement, Packed, Container, Content, FieldVtable, Fold, FoldFn, IntoValue, NativeElement, Packed,

View File

@ -17,7 +17,7 @@ use std::iter::{self, Sum};
use std::ops::{Add, AddAssign, ControlFlow}; use std::ops::{Add, AddAssign, ControlFlow};
use comemo::Tracked; use comemo::Tracked;
use ecow::{eco_format, EcoString}; use ecow::{EcoString, eco_format};
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use typst_syntax::Span; use typst_syntax::Span;
@ -26,8 +26,8 @@ use typst_utils::singleton;
use crate::diag::{SourceResult, StrResult}; use crate::diag::{SourceResult, StrResult};
use crate::engine::Engine; use crate::engine::Engine;
use crate::foundations::{ use crate::foundations::{
func, repr, scope, ty, Context, Dict, IntoValue, Label, Property, Recipe, Context, Dict, IntoValue, Label, Property, Recipe, RecipeIndex, Repr, Selector, Str,
RecipeIndex, Repr, Selector, Str, Style, StyleChain, Styles, Value, Style, StyleChain, Styles, Value, func, repr, scope, ty,
}; };
use crate::introspection::Location; use crate::introspection::Location;
use crate::layout::{AlignElem, Alignment, Axes, Length, MoveElem, PadElem, Rel, Sides}; use crate::layout::{AlignElem, Alignment, Axes, Length, MoveElem, PadElem, Rel, Sides};
@ -174,11 +174,11 @@ impl Content {
id: u8, id: u8,
styles: Option<StyleChain>, styles: Option<StyleChain>,
) -> Result<Value, FieldAccessError> { ) -> Result<Value, FieldAccessError> {
if id == 255 { if id == 255
if let Some(label) = self.label() { && let Some(label) = self.label()
{
return Ok(label.into_value()); return Ok(label.into_value());
} }
}
match self.0.handle().field(id) { match self.0.handle().field(id) {
Some(handle) => match styles { Some(handle) => match styles {

View File

@ -5,7 +5,7 @@ use std::ptr::NonNull;
use std::sync::atomic::{self, AtomicUsize, Ordering}; use std::sync::atomic::{self, AtomicUsize, Ordering};
use typst_syntax::Span; use typst_syntax::Span;
use typst_utils::{fat, HashLock, SmallBitSet}; use typst_utils::{HashLock, SmallBitSet, fat};
use super::vtable; use super::vtable;
use crate::foundations::{Element, Label, NativeElement, Packed}; use crate::foundations::{Element, Label, NativeElement, Packed};

View File

@ -1,9 +1,9 @@
use comemo::Track; use comemo::Track;
use crate::diag::{bail, Hint, HintedStrResult, SourceResult}; use crate::diag::{Hint, HintedStrResult, SourceResult, bail};
use crate::engine::Engine; use crate::engine::Engine;
use crate::foundations::{ use crate::foundations::{
elem, Args, Construct, Content, Func, ShowFn, StyleChain, Value, Args, Construct, Content, Func, ShowFn, StyleChain, Value, elem,
}; };
use crate::introspection::{Locatable, Location}; use crate::introspection::{Locatable, Location};

View File

@ -2,17 +2,17 @@ use std::cmp::Ordering;
use std::hash::Hash; use std::hash::Hash;
use std::ops::{Add, Sub}; use std::ops::{Add, Sub};
use ecow::{eco_format, EcoString, EcoVec}; use ecow::{EcoString, EcoVec, eco_format};
use time::error::{Format, InvalidFormatDescription}; use time::error::{Format, InvalidFormatDescription};
use time::macros::format_description; use time::macros::format_description;
use time::{format_description, Month, PrimitiveDateTime}; use time::{Month, PrimitiveDateTime, format_description};
use crate::diag::{bail, StrResult}; use crate::World;
use crate::diag::{StrResult, bail};
use crate::engine::Engine; use crate::engine::Engine;
use crate::foundations::{ use crate::foundations::{
cast, func, repr, scope, ty, Dict, Duration, Repr, Smart, Str, Value, Dict, Duration, Repr, Smart, Str, Value, cast, func, repr, scope, ty,
}; };
use crate::World;
/// Represents a date, a time, or a combination of both. /// Represents a date, a time, or a combination of both.
/// ///

View File

@ -3,14 +3,14 @@ use std::hash::{Hash, Hasher};
use std::ops::Neg; use std::ops::Neg;
use std::str::FromStr; use std::str::FromStr;
use ecow::{eco_format, EcoString}; use ecow::{EcoString, eco_format};
use rust_decimal::MathematicalOps; use rust_decimal::MathematicalOps;
use typst_syntax::{ast, Span, Spanned}; use typst_syntax::{Span, Spanned, ast};
use crate::diag::{warning, At, SourceResult};
use crate::engine::Engine;
use crate::foundations::{cast, func, repr, scope, ty, Repr, Str};
use crate::World; use crate::World;
use crate::diag::{At, SourceResult, warning};
use crate::engine::Engine;
use crate::foundations::{Repr, Str, cast, func, repr, scope, ty};
/// A fixed-point decimal number type. /// A fixed-point decimal number type.
/// ///

View File

@ -3,7 +3,7 @@ use std::hash::{Hash, Hasher};
use std::ops::{Add, AddAssign}; use std::ops::{Add, AddAssign};
use std::sync::Arc; use std::sync::Arc;
use ecow::{eco_format, EcoString}; use ecow::{EcoString, eco_format};
use indexmap::IndexMap; use indexmap::IndexMap;
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
use typst_syntax::is_ident; use typst_syntax::is_ident;
@ -11,7 +11,7 @@ use typst_utils::ArcExt;
use crate::diag::{Hint, HintedStrResult, StrResult}; use crate::diag::{Hint, HintedStrResult, StrResult};
use crate::foundations::{ use crate::foundations::{
array, cast, func, repr, scope, ty, Array, Module, Repr, Str, Value, Array, Module, Repr, Str, Value, array, cast, func, repr, scope, ty,
}; };
/// Create a new [`Dict`] from key-value pairs. /// Create a new [`Dict`] from key-value pairs.

Some files were not shown because too many files have changed in this diff Show More