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

View File

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

8
Cargo.lock generated
View File

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

View File

@ -5,9 +5,9 @@ resolver = "2"
[workspace.package]
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"]
edition = "2021"
edition = "2024"
homepage = "https://typst.app"
repository = "https://github.com/typst/typst"
license = "Apache-2.0"
@ -68,7 +68,6 @@ icu_provider = { version = "1.4", features = ["sync"] }
icu_provider_adapters = "1.4"
icu_provider_blob = "1.4"
icu_segmenter = { version = "1.4", features = ["serde"] }
if_chain = "1"
image = { version = "0.25.5", default-features = false, features = ["png", "jpeg", "gif", "webp"] }
indexmap = { version = "2", features = ["serde"] }
infer = { version = "0.19.0", default-features = false }

View File

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

View File

@ -10,13 +10,13 @@ use ecow::eco_format;
use parking_lot::RwLock;
use pathdiff::diff_paths;
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use typst::WorldExt;
use typst::diag::{
bail, At, Severity, SourceDiagnostic, SourceResult, StrResult, Warned,
At, Severity, SourceDiagnostic, SourceResult, StrResult, Warned, bail,
};
use typst::foundations::{Datetime, Smart};
use typst::layout::{Frame, Page, PageRanges, PagedDocument};
use typst::syntax::{FileId, Lines, Span};
use typst::WorldExt;
use typst_html::HtmlDocument;
use typst_pdf::{PdfOptions, PdfStandards, Timestamp};
@ -513,7 +513,9 @@ fn write_make_deps(
})
.collect::<Result<Vec<_>, _>>()
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() {
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_kit::download::{DownloadState, Downloader, Progress};
use crate::terminal::{self, TermOut};
use crate::ARGS;
use crate::terminal::{self, TermOut};
/// Prints download progress by writing `downloading {0}` followed by repeatedly
/// updating the last terminal line.

View File

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

View File

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

View File

@ -1,12 +1,12 @@
use comemo::Track;
use ecow::{eco_format, EcoString};
use ecow::{EcoString, eco_format};
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::foundations::{Content, IntoValue, LocatableSelector, Scope};
use typst::layout::PagedDocument;
use typst::syntax::{Span, SyntaxMode};
use typst::World;
use typst_eval::eval_string;
use crate::args::{QueryCommand, SerializationFormat};

View File

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

View File

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

View File

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

View File

@ -10,12 +10,12 @@ use codespan_reporting::term::{self, termcolor};
use ecow::eco_format;
use notify::{Event, RecommendedWatcher, RecursiveMode, Watcher as _};
use same_file::is_same_file;
use typst::diag::{bail, warning, StrResult};
use typst::diag::{StrResult, bail, warning};
use typst::syntax::Span;
use typst::utils::format_duration;
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::world::{SystemWorld, WorldCreationError};
use crate::{print_error, terminal};

View File

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

View File

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

View File

@ -1,9 +1,9 @@
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_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.
pub(crate) trait Access {
@ -29,11 +29,11 @@ impl Access for ast::Expr<'_> {
impl Access for ast::Ident<'_> {
fn access<'a>(self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
let span = self.span();
if vm.inspected == Some(span) {
if let Ok(binding) = vm.scopes.get(&self) {
if vm.inspected == Some(span)
&& let Ok(binding) = vm.scopes.get(&self)
{
vm.trace(binding.read().clone());
}
}
vm.scopes
.get_mut(&self)
.and_then(|b| b.write().map_err(Into::into))

View File

@ -1,7 +1,7 @@
use std::collections::HashSet;
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_syntax::ast::{self, AstNode};

View File

@ -1,8 +1,9 @@
use comemo::{Tracked, TrackedMut};
use ecow::{eco_format, EcoString, EcoVec};
use ecow::{EcoString, EcoVec, eco_format};
use typst_library::World;
use typst_library::diag::{
bail, error, At, HintedStrResult, HintedString, SourceDiagnostic, SourceResult,
Trace, Tracepoint,
At, HintedStrResult, HintedString, SourceDiagnostic, SourceResult, Trace, Tracepoint,
bail, error,
};
use typst_library::engine::{Engine, Sink, Traced};
use typst_library::foundations::{
@ -12,12 +13,11 @@ use typst_library::foundations::{
use typst_library::introspection::Introspector;
use typst_library::math::LrElem;
use typst_library::routines::Routines;
use typst_library::World;
use typst_syntax::ast::{self, AstNode, Ident};
use typst_syntax::{Span, Spanned, SyntaxNode};
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<'_> {
type Output = Value;

View File

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

View File

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

View File

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

View File

@ -14,7 +14,7 @@ mod methods;
mod rules;
mod vm;
pub use self::call::{eval_closure, CapturesVisitor};
pub use self::call::{CapturesVisitor, eval_closure};
pub use self::flow::FlowEvent;
pub use self::import::import;
pub use self::vm::Vm;
@ -24,14 +24,14 @@ use self::binding::*;
use self::methods::*;
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::foundations::{Context, Module, NativeElement, Scope, Scopes, Value};
use typst_library::introspection::Introspector;
use typst_library::math::EquationElem;
use typst_library::routines::Routines;
use typst_library::World;
use typst_syntax::{ast, parse, parse_code, parse_math, Source, Span, SyntaxMode};
use typst_syntax::{Source, Span, SyntaxMode, ast, parse, parse_code, parse_math};
/// Evaluate a source file and return the resulting module.
#[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::{
Content, Label, NativeElement, Repr, Smart, Symbol, Unlabellable, Value,
};

View File

@ -1,8 +1,8 @@
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 crate::{access_dict, Access, Eval, Vm};
use crate::{Access, Eval, Vm, access_dict};
impl Eval for ast::Unary<'_> {
type Output = Value;
@ -76,13 +76,13 @@ fn apply_assignment(
// An assignment to a dictionary field is different from a normal access
// since it can create the field instead of just modifying it.
if binary.op() == ast::BinOp::Assign {
if let ast::Expr::FieldAccess(access) = lhs {
if binary.op() == ast::BinOp::Assign
&& let ast::Expr::FieldAccess(access) = lhs
{
let dict = access_dict(vm, access)?;
dict.insert(access.field().get().clone().into(), rhs);
return Ok(Value::None);
}
}
let location = binary.lhs().access(vm)?;
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::{
Element, Func, Recipe, Selector, ShowableSelector, Styles, Transformation,
};
@ -12,11 +12,11 @@ impl Eval for ast::SetRule<'_> {
type Output = Styles;
fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
if let Some(condition) = self.condition() {
if !condition.eval(vm)?.cast::<bool>().at(condition.span())? {
if let Some(condition) = self.condition()
&& !condition.eval(vm)?.cast::<bool>().at(condition.span())?
{
return Ok(Styles::new());
}
}
let target = self.target();
let target = target
@ -58,12 +58,11 @@ impl Eval for ast::ShowRule<'_> {
/// Migration hint for `show par: set block(spacing: ..)`.
fn check_show_par_set_block(vm: &mut Vm, recipe: &Recipe) {
if_chain::if_chain! {
if let Some(Selector::Elem(elem, _)) = recipe.selector();
if *elem == Element::of::<ParElem>();
if let Transformation::Style(styles) = recipe.transform();
if styles.has(BlockElem::above) || styles.has(BlockElem::below);
then {
if let Some(Selector::Elem(elem, _)) = recipe.selector()
&& *elem == Element::of::<ParElem>()
&& let Transformation::Style(styles) = recipe.transform()
&& (styles.has(BlockElem::above) || styles.has(BlockElem::below))
{
vm.engine.sink.warn(warning!(
recipe.span(),
"`show par: set block(spacing: ..)` has no effect anymore";
@ -71,5 +70,4 @@ fn check_show_par_set_block(vm: &mut Vm, recipe: &Recipe) {
hint: "this is specific to paragraphs as they are not considered blocks anymore"
))
}
}
}

View File

@ -1,10 +1,10 @@
use comemo::Tracked;
use typst_library::World;
use typst_library::diag::warning;
use typst_library::engine::Engine;
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::ast::{self, AstNode};
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::foundations::{Content, StyleChain, Target, TargetElem};
use typst_library::introspection::{SplitLocator, TagElem};
@ -8,7 +8,7 @@ use typst_library::routines::Pair;
use typst_library::text::{LinebreakElem, SmartQuoteElem, SpaceElem, TextElem};
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.
pub fn convert_to_nodes<'a>(

View File

@ -2,7 +2,8 @@ use std::collections::HashSet;
use std::num::NonZeroUsize;
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::foundations::{Content, StyleChain};
use typst_library::introspection::{
@ -11,11 +12,10 @@ use typst_library::introspection::{
use typst_library::layout::{Point, Position, Transform};
use typst_library::model::DocumentInfo;
use typst_library::routines::{Arenas, RealizationKind, Routines};
use typst_library::World;
use typst_syntax::Span;
use typst_utils::NonZeroExt;
use crate::{attr, tag, HtmlDocument, HtmlElement, HtmlNode};
use crate::{HtmlDocument, HtmlElement, HtmlNode, attr, tag};
/// Produce an HTML document from content.
///

View File

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

View File

@ -1,12 +1,12 @@
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::introspection::Introspector;
use typst_syntax::Span;
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.
@ -262,11 +262,7 @@ impl RawMode {
{
// Template literals can be multi-line, so indent may change
// the semantics of the JavaScript.
if text.contains('`') {
Self::Wrap
} else {
Self::Indent
}
if text.contains('`') { Self::Wrap } else { Self::Indent }
}
tag::style => Self::Indent,
_ => 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::introspection::{Introspector, Locator, LocatorLink};
use typst_library::routines::{Arenas, FragmentKind, RealizationKind, Routines};
use typst_library::World;
use typst_library::routines::{Arenas, FragmentKind, RealizationKind, Routines};
use crate::HtmlNode;

View File

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

View File

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

View File

@ -1,12 +1,12 @@
use std::num::NonZeroUsize;
use ecow::{eco_format, EcoVec};
use typst_library::diag::{warning, At};
use ecow::{EcoVec, eco_format};
use typst_library::diag::{At, warning};
use typst_library::foundations::{
Content, NativeElement, NativeRuleMap, ShowFn, Smart, StyleChain, Target,
};
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::model::{
Attribution, CiteElem, CiteGroup, Destination, EmphElem, EnumElem, FigureCaption,
@ -19,7 +19,7 @@ use typst_library::text::{
};
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).
pub fn register(rules: &mut NativeRuleMap) {
@ -238,13 +238,11 @@ const QUOTE_RULE: ShowFn<QuoteElem> = |elem, _, styles| {
if block {
let mut blockquote = HtmlElem::new(tag::blockquote).with_body(Some(realized));
if let Some(Attribution::Content(attribution)) = attribution {
if let Some(link) = attribution.to_packed::<LinkElem>() {
if let LinkTarget::Dest(Destination::Url(url)) = &link.dest {
blockquote =
blockquote.with_attr(attr::cite, url.clone().into_inner());
}
}
if let Some(Attribution::Content(attribution)) = attribution
&& let Some(link) = attribution.to_packed::<LinkElem>()
&& let LinkTarget::Dest(Destination::Url(url)) = &link.dest
{
blockquote = blockquote.with_attr(attr::cite, url.clone().into_inner());
}
realized = blockquote.pack().spanned(span);

View File

@ -9,9 +9,9 @@ use std::sync::LazyLock;
use bumpalo::Bump;
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_library::diag::{bail, At, Hint, HintedStrResult, SourceResult};
use typst_library::diag::{At, Hint, HintedStrResult, SourceResult, bail};
use typst_library::engine::Engine;
use typst_library::foundations::{
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_macros::cast;
use crate::{css, tag, HtmlAttr, HtmlAttrs, HtmlElem, HtmlTag};
use crate::{HtmlAttr, HtmlAttrs, HtmlElem, HtmlTag, css, tag};
/// Hook up all typed HTML definitions.
pub(super) fn define(html: &mut Scope) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,9 @@
use ecow::EcoString;
use typst::foundations::{Module, Value};
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.
pub fn named_items<T>(
@ -59,11 +59,11 @@ pub fn named_items<T>(
};
// Seeing the module itself.
if let Some((name, span)) = name_and_span {
if let Some(res) = recv(NamedItem::Module(&name, span, module)) {
if let Some((name, span)) = name_and_span
&& let Some(res) = recv(NamedItem::Module(&name, span, module))
{
return Some(res);
}
}
// Seeing the imported items.
match imports {
@ -124,8 +124,9 @@ pub fn named_items<T>(
}
if let Some(parent) = node.parent() {
if let Some(v) = parent.cast::<ast::ForLoop>() {
if node.prev_sibling_kind() != Some(SyntaxKind::In) {
if let Some(v) = parent.cast::<ast::ForLoop>()
&& node.prev_sibling_kind() != Some(SyntaxKind::In)
{
let pattern = v.pattern();
for ident in pattern.bindings() {
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| {
// Check if the node is in the body of the closure.
@ -155,15 +155,15 @@ pub fn named_items<T>(
}
}
ast::Param::Spread(s) => {
if let Some(sink_ident) = s.sink_ident() {
if let Some(t) = recv(NamedItem::Var(sink_ident)) {
if let Some(sink_ident) = s.sink_ident()
&& let Some(t) = recv(NamedItem::Var(sink_ident))
{
return Some(t);
}
}
}
}
}
}
ancestor = Some(parent.clone());
continue;
@ -216,7 +216,7 @@ impl<'a> NamedItem<'a> {
/// Categorize an expression into common classes IDE functionality can operate
/// 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.
let mut ancestor = node;
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::{FileId, Source, VirtualPath};
use typst::text::{Font, FontBook, TextElem, TextSize};
use typst::utils::{singleton, LazyHash};
use typst::utils::{LazyHash, singleton};
use typst::{Feature, Library, LibraryExt, World};
use crate::IdeWorld;

View File

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

View File

@ -2,7 +2,7 @@ use std::fmt::Write;
use std::ops::ControlFlow;
use comemo::Track;
use ecow::{eco_format, EcoString};
use ecow::{EcoString, eco_format};
use typst::engine::{Engine, Route, Sink, Traced};
use typst::foundations::{Scope, Value};
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));
let library = world.library();
if in_math {
library.math.scope()
} else {
library.global.scope()
}
if in_math { library.math.scope() } else { library.global.scope() }
}
/// 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 once_cell::sync::OnceCell;
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 crate::download::{Downloader, Progress};
@ -189,7 +189,7 @@ impl PackageStorage {
}
}
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 is_explicit = matches!(body, None | Some(BlockBody::Content(_)));
// Skip filling/stroking the first frame if it is empty and a non-empty
// one follows.
// Skip filling, stroking and labeling the first frame if it is empty and
// a non-empty one follows.
let mut skip_first = false;
if let [first, rest @ ..] = fragment.as_slice() {
skip_first = has_fill_or_stroke
&& first.is_empty()
&& rest.iter().any(|frame| !frame.is_empty());
skip_first = first.is_empty() && rest.iter().any(|frame| !frame.is_empty());
}
// 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.
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);
}
}
@ -407,11 +406,11 @@ fn distribute<'a>(
// If there is still something remaining, apply it to the
// last region (it will overflow, but there's nothing else
// we can do).
if !remaining.approx_empty() {
if let Some(last) = buf.last_mut() {
if !remaining.approx_empty()
&& let Some(last) = buf.last_mut()
{
*last += remaining;
}
}
// Distribute the heights to the first region and the
// 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::hash::Hash;
use bumpalo::boxed::Box as BumpBox;
use bumpalo::Bump;
use bumpalo::boxed::Box as BumpBox;
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::foundations::{Packed, Resolve, Smart, StyleChain};
use typst_library::introspection::{
@ -19,10 +20,9 @@ use typst_library::layout::{
use typst_library::model::ParElem;
use typst_library::routines::{Pair, Routines};
use typst_library::text::TextElem;
use typst_library::World;
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::modifiers::layout_and_modify;
@ -459,6 +459,7 @@ impl<'a> MultiChild<'a> {
regions: Regions,
) -> SourceResult<(Frame, Option<MultiSpill<'a, 'b>>)> {
let fragment = self.layout_full(engine, regions)?;
let exist_non_empty_frame = fragment.iter().any(|f| !f.is_empty());
// Extract the first frame.
let mut frames = fragment.into_iter();
@ -468,6 +469,7 @@ impl<'a> MultiChild<'a> {
let mut spill = None;
if frames.next().is_some() {
spill = Some(MultiSpill {
exist_non_empty_frame,
multi: self,
full: regions.full,
first: regions.size.y,
@ -539,6 +541,7 @@ fn layout_multi_impl(
/// The spilled remains of a `MultiChild` that broke across two regions.
#[derive(Debug, Clone)]
pub struct MultiSpill<'a, 'b> {
pub(super) exist_non_empty_frame: bool,
multi: &'b MultiChild<'a>,
first: Abs,
full: Abs,
@ -684,11 +687,11 @@ impl<T> CachedCell<T> {
let input_hash = typst_utils::hash128(&input);
let mut slot = self.0.borrow_mut();
if let Some((hash, output)) = &*slot {
if *hash == input_hash {
if let Some((hash, output)) = &*slot
&& *hash == input_hash
{
return output.clone();
}
}
let output = f(input);
*slot = Some((input_hash, output.clone()));

View File

@ -18,7 +18,7 @@ use typst_syntax::Span;
use typst_utils::{NonZeroExt, Numeric};
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
@ -319,11 +319,7 @@ impl<'a, 'b> Composer<'a, 'b, '_, '_> {
let used = base.y - remaining;
let half = need / 2.0;
let ratio = (used + half) / base.y;
if ratio <= 0.5 {
FixedAlignment::Start
} else {
FixedAlignment::End
}
if ratio <= 0.5 { FixedAlignment::Start } else { FixedAlignment::End }
});
// 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.
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)?;
// 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 comemo::{Track, Tracked, TrackedMut};
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::foundations::{Content, Packed, Resolve, StyleChain};
use typst_library::introspection::{
@ -27,14 +28,13 @@ use typst_library::layout::{
use typst_library::model::{FootnoteElem, FootnoteEntry, LineNumberingScope, ParLine};
use typst_library::routines::{Arenas, FragmentKind, Pair, RealizationKind, Routines};
use typst_library::text::TextElem;
use typst_library::World;
use typst_utils::{NonZeroExt, Numeric};
use self::block::{layout_multi_block, layout_single_block};
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;
/// Lays out content into a single region, producing a single frame.

View File

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

View File

@ -1,8 +1,8 @@
use std::sync::Arc;
use typst_library::foundations::{AlternativeFold, Fold};
use typst_library::layout::grid::resolve::{CellGrid, Line, Repeatable};
use typst_library::layout::Abs;
use typst_library::layout::grid::resolve::{CellGrid, Line, Repeatable};
use typst_library::visualize::Stroke;
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
// 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.
if let Some(parent) = grid.effective_parent_cell_position(x, y) {
if parent.x < x {
if let Some(parent) = grid.effective_parent_cell_position(x, y)
&& parent.x < x
{
// There is a colspan cell going through this vline's position,
// so don't draw it here.
return None;
}
}
}
let (left_cell_stroke, left_cell_prioritized) = x
.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
// 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.
if let Some(parent) = grid.effective_parent_cell_position(x, y) {
if parent.y < y {
if let Some(parent) = grid.effective_parent_cell_position(x, y)
&& parent.y < y
{
// 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
// 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
// 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::foundations::{Packed, StyleChain};
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::model::TableElem;
use self::layouter::RowPiece;
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};

View File

@ -240,8 +240,10 @@ impl<'a> GridLayouter<'a> {
self.current.initial_after_repeats = self.regions.size.y;
}
if let Some(footer) = &self.grid.footer {
if footer.repeated && skipped_region {
if let Some(footer) = &self.grid.footer
&& footer.repeated
&& skipped_region
{
// Simulate the footer again; the region's 'full' might have
// changed.
self.regions.size.y += self.current.footer_height;
@ -250,7 +252,6 @@ impl<'a> GridLayouter<'a> {
.height;
self.regions.size.y -= self.current.footer_height;
}
}
let repeating_header_rows =
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::{Abs, Axes, Frame, Point, Region, Regions, Size, Sizing};
use super::layouter::{points, Row};
use super::{layout_cell, Cell, GridLayouter};
use super::layouter::{Row, points};
use super::{Cell, GridLayouter, layout_cell};
/// All information needed to layout a single rowspan.
pub struct Rowspan {
@ -238,8 +238,10 @@ impl GridLayouter<'_> {
// current row is dynamic and depends on the amount of upcoming
// unbreakable cells (with or without a rowspan setting).
let mut amount_unbreakable_rows = None;
if let Some(footer) = &self.grid.footer {
if !footer.repeated && current_row >= footer.start {
if let Some(footer) = &self.grid.footer
&& !footer.repeated
&& current_row >= footer.start
{
// Non-repeated footer, so keep it unbreakable.
//
// TODO(subfooters): This will become unnecessary
@ -247,7 +249,6 @@ impl GridLayouter<'_> {
// have widow prevention.
amount_unbreakable_rows = Some(self.grid.rows.len() - footer.start);
}
}
let row_group = self.simulate_unbreakable_row_group(
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) {
subtract -= sizes.pop().unwrap();
}
if subtract > Abs::zero() {
if let Some(last_size) = sizes.last_mut() {
if subtract > Abs::zero()
&& let Some(last_size) = sizes.last_mut()
{
*last_size -= subtract;
}
}
}

View File

@ -6,14 +6,14 @@ use typst_library::layout::{
};
use typst_library::routines::Pair;
use typst_library::text::{
is_default_ignorable, LinebreakElem, SmartQuoteElem, SmartQuoter, SmartQuotes,
SpaceElem, TextElem,
LinebreakElem, SmartQuoteElem, SmartQuoter, SmartQuotes, SpaceElem, TextElem,
is_default_ignorable,
};
use typst_syntax::Span;
use typst_utils::Numeric;
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
// full text.
@ -274,12 +274,12 @@ impl<'a> Collector<'a> {
let segment_len = self.full.len() - prev;
// Merge adjacent text segments with the same styles.
if let Some(Segment::Text(last_len, last_styles)) = self.segments.last_mut() {
if *last_styles == styles {
if let Some(Segment::Text(last_len, last_styles)) = self.segments.last_mut()
&& *last_styles == styles
{
*last_len += segment_len;
return;
}
}
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::layout::{Abs, Dir, Em, Fr, Frame, FrameItem, Point};
use typst_library::model::ParLineMarker;
use typst_library::text::{variant, Lang, TextElem};
use typst_library::text::{Lang, TextElem, variant};
use typst_utils::Numeric;
use super::*;
@ -155,18 +155,18 @@ pub fn line<'a>(
let mut items = collect_items(engine, p, range, trim);
// 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 let Some(shaped) = items.first_text_mut() {
if pred.is_some_and(|pred| should_repeat_hyphen(pred, full))
&& let Some(shaped) = items.first_text_mut()
{
shaped.prepend_hyphen(engine, p.config.fallback);
}
}
// Add a hyphen at the line end, if we ended on a soft hyphen.
if dash == Some(Dash::Soft) {
if let Some(shaped) = items.last_text_mut() {
if dash == Some(Dash::Soft)
&& let Some(shaped) = items.last_text_mut()
{
shaped.push_hyphen(engine, p.config.fallback);
}
}
// Deal with CJ characters at line boundaries.
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.
if !items.iter().any(|item| matches!(item, Item::Text(_))) {
if let Some(fallback) = fallback {
if !items.iter().any(|item| matches!(item, Item::Text(_)))
&& let Some(fallback) = fallback
{
items.push(fallback, usize::MAX);
}
}
items
}
@ -461,9 +461,9 @@ pub fn commit(
}
// Handle hanging punctuation to the left.
if let Some(Item::Text(text)) = line.items.first() {
if let Some(glyph) = text.glyphs.first() {
if !text.dir.is_positive()
if let Some(Item::Text(text)) = line.items.first()
&& let Some(glyph) = text.glyphs.first()
&& !text.dir.is_positive()
&& text.styles.get(TextElem::overhang)
&& (line.items.len() > 1 || text.glyphs.len() > 1)
{
@ -471,21 +471,17 @@ pub fn commit(
offset -= amount;
remaining += amount;
}
}
}
// Handle hanging punctuation to the right.
if let Some(Item::Text(text)) = line.items.last() {
if let Some(glyph) = text.glyphs.last() {
if text.dir.is_positive()
if let Some(Item::Text(text)) = line.items.last()
&& let Some(glyph) = text.glyphs.last()
&& text.dir.is_positive()
&& text.styles.get(TextElem::overhang)
&& (line.items.len() > 1 || text.glyphs.len() > 1)
{
let amount = overhang(glyph.c) * glyph.x_advance.at(glyph.size);
remaining += amount;
}
}
}
// Determine how much additional space is needed. The justification_ratio is
// 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 az::SaturatingAs;
use icu_properties::maps::{CodePointMapData, CodePointMapDataBorrowed};
use icu_properties::LineBreak;
use icu_properties::maps::{CodePointMapData, CodePointMapDataBorrowed};
use icu_provider::AsDeserializingBufferProvider;
use icu_provider_adapters::fork::ForkByKeyProvider;
use icu_provider_blob::BlobDataProvider;
@ -11,7 +11,7 @@ use icu_segmenter::LineSegmenter;
use typst_library::engine::Engine;
use typst_library::layout::{Abs, Em};
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 unicode_segmentation::UnicodeSegmentation;
@ -136,13 +136,13 @@ fn linebreak_simple<'a>(
// 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
// resulting line cannot be broken up further.
if !width.fits(attempt.width) {
if let Some((last_attempt, last_end)) = last.take() {
if !width.fits(attempt.width)
&& let Some((last_attempt, last_end)) = last.take()
{
lines.push(last_attempt);
start = last_end;
attempt = line(engine, p, start..end, breakpoint, lines.last());
}
}
// 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
@ -894,11 +894,7 @@ impl CostMetrics {
/// we allow less because otherwise we get an invalid layout fairly often,
/// which makes our bound useless.
fn min_ratio(&self, approx: bool) -> f64 {
if approx {
self.min_approx_ratio
} else {
self.min_ratio
}
if approx { 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;
use comemo::{Track, Tracked, TrackedMut};
use typst_library::World;
use typst_library::diag::SourceResult;
use typst_library::engine::{Engine, Route, Sink, Traced};
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::text::{Costs, Lang, TextElem};
use typst_library::World;
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::finalize::finalize;
use self::line::{apply_shift, commit, line, Line};
use self::linebreak::{linebreak, Breakpoint};
use self::prepare::{prepare, Preparation};
use self::line::{Line, apply_shift, commit, line};
use self::linebreak::{Breakpoint, linebreak};
use self::prepare::{Preparation, prepare};
use self::shaping::{
cjk_punct_style, is_of_cj_script, shape_range, ShapedGlyph, ShapedText,
BEGIN_PUNCT_PAT, END_PUNCT_PAT,
BEGIN_PUNCT_PAT, END_PUNCT_PAT, ShapedGlyph, ShapedText, cjk_punct_style,
is_of_cj_script, shape_range,
};
/// Range of a substring of text.
@ -190,11 +190,7 @@ fn configuration(
Config {
justify,
linebreaks: base.linebreaks.unwrap_or_else(|| {
if justify {
Linebreaks::Optimized
} else {
Linebreaks::Simple
}
if justify { Linebreaks::Optimized } else { Linebreaks::Simple }
}),
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 rustybuzz::{BufferFlags, Feature, ShapePlan, UnicodeBuffer};
use ttf_parser::gsub::SubstitutionSubtable;
use ttf_parser::Tag;
use ttf_parser::gsub::SubstitutionSubtable;
use typst_library::World;
use typst_library::engine::Engine;
use typst_library::foundations::{Smart, StyleChain};
use typst_library::layout::{Abs, Dir, Em, Frame, FrameItem, Point, Size};
use typst_library::text::{
families, features, is_default_ignorable, language, variant, Font, FontFamily,
FontVariant, Glyph, Lang, Region, ShiftSettings, TextEdgeBounds, TextElem, TextItem,
Font, FontFamily, FontVariant, Glyph, Lang, Region, ShiftSettings, TextEdgeBounds,
TextElem, TextItem, families, features, is_default_ignorable, language, variant,
};
use typst_library::World;
use typst_utils::SliceExt;
use unicode_bidi::{BidiInfo, Level as BidiLevel};
use unicode_script::{Script, UnicodeScript};
use super::{decorate, Item, Range, SpanMapper};
use super::{Item, Range, SpanMapper, decorate};
use crate::modifiers::FrameModifyText;
/// The result of shaping text.
@ -539,11 +539,7 @@ impl<'a> ShapedText<'a> {
// Find any glyph with the text index.
let found = self.glyphs.binary_search_by(|g: &ShapedGlyph| {
let ordering = g.range.start.cmp(&text_index);
if ltr {
ordering
} else {
ordering.reverse()
}
if ltr { ordering } else { ordering.reverse() }
});
let mut idx = match found {

View File

@ -24,11 +24,7 @@ pub fn layout_list(
let body_indent = elem.body_indent.get(styles);
let tight = elem.tight.get(styles);
let gutter = elem.spacing.get(styles).unwrap_or_else(|| {
if tight {
styles.get(ParElem::leading)
} else {
styles.get(ParElem::spacing)
}
if tight { styles.get(ParElem::leading) } else { styles.get(ParElem::spacing) }
});
let Depth(depth) = styles.get(ListElem::depth);
@ -88,22 +84,15 @@ pub fn layout_enum(
let body_indent = elem.body_indent.get(styles);
let tight = elem.tight.get(styles);
let gutter = elem.spacing.get(styles).unwrap_or_else(|| {
if tight {
styles.get(ParElem::leading)
} else {
styles.get(ParElem::spacing)
}
if tight { styles.get(ParElem::leading) } else { styles.get(ParElem::spacing) }
});
let mut cells = vec![];
let mut locator = locator.split();
let mut number = elem.start.get(styles).unwrap_or_else(|| {
if reversed {
elem.children.len() as u64
} else {
1
}
});
let mut number = elem
.start
.get(styles)
.unwrap_or_else(|| if reversed { elem.children.len() as u64 } else { 1 });
let mut parents = styles.get_cloned(EnumElem::parents);
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 super::{
style_cramped, style_dtls, style_flac, FrameFragment, GlyphFragment, MathContext,
MathFragment,
FrameFragment, GlyphFragment, MathContext, MathFragment, style_cramped, style_dtls,
style_flac,
};
/// 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 super::{
stretch_fragment, style_for_subscript, style_for_superscript, FrameFragment, Limits,
MathContext, MathFragment,
FrameFragment, Limits, MathContext, MathFragment, stretch_fragment,
style_for_subscript, style_for_superscript,
};
macro_rules! measure {

View File

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

View File

@ -2,18 +2,18 @@ use std::fmt::{self, Debug, Formatter};
use az::SaturatingAs;
use rustybuzz::{BufferFlags, UnicodeBuffer};
use ttf_parser::math::{GlyphAssembly, GlyphConstruction, GlyphPart};
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::introspection::Tag;
use typst_library::layout::{
Abs, Axes, Axis, Corner, Em, Frame, FrameItem, Point, Size, VAlignment,
};
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_utils::{default_math_class, Get};
use typst_utils::{Get, default_math_class};
use unicode_math_class::MathClass;
use super::MathContext;
@ -681,7 +681,11 @@ fn min_connector_overlap(font: &Font) -> Option<Em> {
.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()
.tables()
.math?
@ -810,7 +814,10 @@ fn assemble(
/// Return an iterator over the assembly's parts with extenders repeated the
/// 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| {
let count = if part.part_flags.extender() { repeat } else { 1 };
std::iter::repeat_n(part, count)

View File

@ -5,7 +5,7 @@ use typst_library::math::{EquationElem, LrElem, MidElem};
use typst_utils::SliceExt;
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`].
#[typst_macros::time(name = "math.lr", span = elem.span())]
@ -21,11 +21,11 @@ pub fn layout_lr(
}
// Extract implicit LrElem.
if let Some(lr) = body.to_packed::<LrElem>() {
if lr.size.get(styles).is_one() {
if let Some(lr) = body.to_packed::<LrElem>()
&& lr.size.get(styles).is_one()
{
body = &lr.body;
}
}
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.
for fragment in inner_fragments.iter_mut() {
if let MathFragment::Glyph(ref mut glyph) = fragment {
if glyph.mid_stretched == Some(false) {
if let MathFragment::Glyph(glyph) = fragment
&& glyph.mid_stretched == Some(false)
{
glyph.mid_stretched = Some(true);
scale(ctx, fragment, relative_to, height);
}
}
}
// Remove weak SpacingFragment immediately after the opening or immediately
// before the closing.
@ -95,7 +95,7 @@ pub fn layout_mid(
let mut fragments = ctx.layout_into_fragments(&elem.body, styles)?;
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.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::layout::{
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 super::{
alignments, style_for_denominator, AlignmentResult, FrameFragment, GlyphFragment,
LeftRightAlternator, MathContext, DELIM_SHORT_FALL,
AlignmentResult, DELIM_SHORT_FALL, FrameFragment, GlyphFragment, LeftRightAlternator,
MathContext, alignments, style_for_denominator,
};
const VERTICAL_PADDING: Ratio = Ratio::new(0.1);

View File

@ -13,7 +13,8 @@ mod stretch;
mod text;
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::foundations::{
Content, NativeElement, Packed, Resolve, StyleChain, SymbolElem,
@ -28,15 +29,14 @@ use typst_library::math::*;
use typst_library::model::ParElem;
use typst_library::routines::{Arenas, RealizationKind};
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_utils::Numeric;
use unicode_math_class::MathClass;
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::shared::*;
@ -603,13 +603,10 @@ fn layout_h(
ctx: &mut MathContext,
styles: StyleChain,
) -> SourceResult<()> {
if let Spacing::Rel(rel) = elem.amount {
if rel.rel.is_zero() {
ctx.push(MathFragment::Spacing(
rel.abs.resolve(styles),
elem.weak.get(styles),
));
}
if let Spacing::Rel(rel) = elem.amount
&& rel.rel.is_zero()
{
ctx.push(MathFragment::Spacing(rel.abs.resolve(styles), elem.weak.get(styles)));
}
Ok(())
}

View File

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

View File

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

View File

@ -1,5 +1,5 @@
use ttf_parser::math::MathValue;
use ttf_parser::Tag;
use ttf_parser::math::MathValue;
use typst_library::foundations::{Style, StyleChain};
use typst_library::layout::{Abs, Em, FixedAlignment, Frame, Point, Size};
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::layout::{Abs, Axis, Rel};
use typst_library::math::StretchElem;
use typst_utils::Get;
use super::{stretch_axes, MathContext, MathFragment};
use super::{MathContext, MathFragment, stretch_axes};
/// Lays out a [`StretchElem`].
#[typst_macros::time(name = "math.stretch", span = elem.span())]
@ -37,7 +37,7 @@ pub fn stretch_fragment(
) {
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,
// so that the original fragment isn't modified.

View File

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

View File

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

View File

@ -7,6 +7,7 @@ mod run;
use std::num::NonZeroUsize;
use comemo::{Tracked, TrackedMut};
use typst_library::World;
use typst_library::diag::SourceResult;
use typst_library::engine::{Engine, Route, Sink, Traced};
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::model::DocumentInfo;
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::run::{layout_blank_page, layout_page_run, LayoutedPage};
use self::run::{LayoutedPage, layout_blank_page, layout_page_run};
/// Layout content into a document.
///

View File

@ -1,4 +1,5 @@
use comemo::{Track, Tracked, TrackedMut};
use typst_library::World;
use typst_library::diag::SourceResult;
use typst_library::engine::{Engine, Route, Sink, Traced};
use typst_library::foundations::{
@ -16,10 +17,9 @@ use typst_library::model::Numbering;
use typst_library::routines::{Pair, Routines};
use typst_library::text::{LocalName, TextElem};
use typst_library::visualize::Paint;
use typst_library::World;
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
/// 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::foundations::{Packed, Resolve, StyleChain};
use typst_library::introspection::Locator;

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@ use std::string::FromUtf8Error;
use az::SaturatingAs;
use comemo::Tracked;
use ecow::{eco_vec, EcoVec};
use ecow::{EcoVec, eco_vec};
use typst_syntax::package::{PackageSpec, PackageVersion};
use typst_syntax::{Lines, Span, Spanned, SyntaxError};
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 };
for error in errors.make_mut().iter_mut() {
// Skip traces that surround the error.
if let Some(error_range) = world.range(error.span) {
if error.span.id() == span.id()
if let Some(error_range) = world.range(error.span)
&& error.span.id() == span.id()
&& trace_range.start <= error_range.start
&& trace_range.end >= error_range.end
{
continue;
}
}
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 message = match error {
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, _) => {
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 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::introspection::Introspector;
use crate::routines::Routines;
use crate::World;
/// Holds all data needed during compilation.
pub struct Engine<'a> {
@ -47,7 +47,11 @@ impl Engine<'_> {
}
/// 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
P: IntoIterator<IntoIter = I>,
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
/// the file with the traced span are invalidated.
pub fn get(&self, id: FileId) -> Option<Span> {
if self.0.and_then(Span::id) == Some(id) {
self.0
} else {
None
}
if self.0.and_then(Span::id) == Some(id) { self.0 } else { None }
}
}

View File

@ -1,12 +1,12 @@
use std::fmt::{self, Debug, Formatter};
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 crate::diag::{bail, error, At, SourceDiagnostic, SourceResult, StrResult};
use crate::diag::{At, SourceDiagnostic, SourceResult, StrResult, bail, error};
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.

View File

@ -4,16 +4,16 @@ use std::num::{NonZeroI64, NonZeroUsize};
use std::ops::{Add, AddAssign};
use comemo::Tracked;
use ecow::{eco_format, EcoString, EcoVec};
use ecow::{EcoString, EcoVec, eco_format};
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
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::foundations::{
cast, func, ops, repr, scope, ty, Args, Bytes, CastInfo, Context, Dict, FromValue,
Func, IntoValue, Reflect, Repr, Str, Value, Version,
Args, Bytes, CastInfo, Context, Dict, FromValue, Func, IntoValue, Reflect, Repr, Str,
Value, Version, cast, func, ops, repr, scope, ty,
};
/// Create a new [`Array`] from values.

View File

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

View File

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

View File

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

View File

@ -7,8 +7,8 @@ use az::SaturatingAs;
use typst_syntax::{Span, Spanned};
use typst_utils::{round_int_with_precision, round_with_precision};
use crate::diag::{bail, At, HintedString, SourceResult, StrResult};
use crate::foundations::{cast, func, ops, Decimal, IntoValue, Module, Scope, Value};
use crate::diag::{At, HintedString, SourceResult, StrResult, bail};
use crate::foundations::{Decimal, IntoValue, Module, Scope, Value, cast, func, ops};
use crate::layout::{Angle, Fr, Length, Ratio};
/// 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::foundations::{
array, repr, Fold, NativeElement, Packed, Repr, Str, Type, Value,
Fold, NativeElement, Packed, Repr, Str, Type, Value, array, repr,
};
/// Determine details of a type.
@ -347,14 +347,15 @@ impl CastInfo {
msg.hint(eco_format!("use `label({})` to create a label", s.repr()));
}
}
} else if let Value::Decimal(_) = found {
if !matching_type && parts.iter().any(|p| p == "float") {
} else if let Value::Decimal(_) = found
&& !matching_type
&& parts.iter().any(|p| p == "float")
{
msg.hint(eco_format!(
"if loss of precision is acceptable, explicitly cast the \
decimal to a float with `float(value)`"
));
}
}
msg
}

View File

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

View File

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

View File

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

View File

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

View File

@ -2,17 +2,17 @@ use std::cmp::Ordering;
use std::hash::Hash;
use std::ops::{Add, Sub};
use ecow::{eco_format, EcoString, EcoVec};
use ecow::{EcoString, EcoVec, eco_format};
use time::error::{Format, InvalidFormatDescription};
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::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.
///

View File

@ -3,14 +3,14 @@ use std::hash::{Hash, Hasher};
use std::ops::Neg;
use std::str::FromStr;
use ecow::{eco_format, EcoString};
use ecow::{EcoString, eco_format};
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::diag::{At, SourceResult, warning};
use crate::engine::Engine;
use crate::foundations::{Repr, Str, cast, func, repr, scope, ty};
/// A fixed-point decimal number type.
///

View File

@ -3,7 +3,7 @@ use std::hash::{Hash, Hasher};
use std::ops::{Add, AddAssign};
use std::sync::Arc;
use ecow::{eco_format, EcoString};
use ecow::{EcoString, eco_format};
use indexmap::IndexMap;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use typst_syntax::is_ident;
@ -11,7 +11,7 @@ use typst_utils::ArcExt;
use crate::diag::{Hint, HintedStrResult, StrResult};
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.

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