mirror of https://github.com/stelzo/typst.git
Merge remote-tracking branch 'typst/0.13'
This commit is contained in:
commit
9cc920cc62
|
|
@ -3835,7 +3835,7 @@ checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
|
|||
|
||||
[[package]]
|
||||
name = "typst"
|
||||
version = "0.13.0-rc1"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"comemo",
|
||||
"ecow",
|
||||
|
|
@ -3852,12 +3852,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typst-assets"
|
||||
version = "0.13.0-rc1"
|
||||
source = "git+https://github.com/typst/typst-assets?rev=7eb87f5#7eb87f5496aff556ace09cf574d11d90d90543ca"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1051c56bbbf74d31ea6c6b1661e62fa0ebb8104403ee53f6dcd321600426e0b6"
|
||||
|
||||
[[package]]
|
||||
name = "typst-cli"
|
||||
version = "0.13.0-rc1"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
|
|
@ -3902,12 +3903,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typst-dev-assets"
|
||||
version = "0.12.0"
|
||||
source = "git+https://github.com/typst/typst-dev-assets?rev=7f8999d#7f8999d19907cd6e1148b295efbc844921c0761c"
|
||||
version = "0.13.0"
|
||||
source = "git+https://github.com/typst/typst-dev-assets?tag=v0.13.0#61aebe9575a5abff889f76d73c7b01dc8e17e340"
|
||||
|
||||
[[package]]
|
||||
name = "typst-docs"
|
||||
version = "0.13.0-rc1"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"ecow",
|
||||
|
|
@ -3930,7 +3931,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typst-eval"
|
||||
version = "0.13.0-rc1"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"comemo",
|
||||
"ecow",
|
||||
|
|
@ -3948,7 +3949,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typst-fuzz"
|
||||
version = "0.13.0-rc1"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"comemo",
|
||||
"libfuzzer-sys",
|
||||
|
|
@ -3960,7 +3961,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typst-html"
|
||||
version = "0.13.0-rc1"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"comemo",
|
||||
"ecow",
|
||||
|
|
@ -3974,7 +3975,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typst-ide"
|
||||
version = "0.13.0-rc1"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"comemo",
|
||||
"ecow",
|
||||
|
|
@ -3991,7 +3992,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typst-kit"
|
||||
version = "0.13.0-rc1"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"ecow",
|
||||
|
|
@ -4015,7 +4016,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typst-layout"
|
||||
version = "0.13.0-rc1"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"az",
|
||||
"bumpalo",
|
||||
|
|
@ -4045,7 +4046,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typst-library"
|
||||
version = "0.13.0-rc1"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"az",
|
||||
"bitflags 2.8.0",
|
||||
|
|
@ -4067,6 +4068,7 @@ dependencies = [
|
|||
"kamadak-exif",
|
||||
"kurbo",
|
||||
"lipsum",
|
||||
"memchr",
|
||||
"palette",
|
||||
"phf",
|
||||
"png",
|
||||
|
|
@ -4104,7 +4106,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typst-macros"
|
||||
version = "0.13.0-rc1"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
|
|
@ -4114,7 +4116,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typst-pdf"
|
||||
version = "0.13.0-rc1"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"base64",
|
||||
|
|
@ -4140,7 +4142,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typst-realize"
|
||||
version = "0.13.0-rc1"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bumpalo",
|
||||
|
|
@ -4156,7 +4158,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typst-render"
|
||||
version = "0.13.0-rc1"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"comemo",
|
||||
|
|
@ -4172,7 +4174,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typst-svg"
|
||||
version = "0.13.0-rc1"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"comemo",
|
||||
|
|
@ -4190,7 +4192,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typst-syntax"
|
||||
version = "0.13.0-rc1"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"ecow",
|
||||
"serde",
|
||||
|
|
@ -4206,7 +4208,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typst-tests"
|
||||
version = "0.13.0-rc1"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"comemo",
|
||||
|
|
@ -4231,7 +4233,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typst-timing"
|
||||
version = "0.13.0-rc1"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"parking_lot",
|
||||
"serde",
|
||||
|
|
@ -4241,7 +4243,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typst-utils"
|
||||
version = "0.13.0-rc1"
|
||||
version = "0.13.0"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"portable-atomic",
|
||||
|
|
|
|||
39
Cargo.toml
39
Cargo.toml
|
|
@ -4,7 +4,7 @@ default-members = ["crates/typst-cli"]
|
|||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "0.13.0-rc1"
|
||||
version = "0.13.0"
|
||||
rust-version = "1.80" # also change in ci.yml
|
||||
authors = ["The Typst Project Developers"]
|
||||
edition = "2021"
|
||||
|
|
@ -16,24 +16,24 @@ keywords = ["typst"]
|
|||
readme = "README.md"
|
||||
|
||||
[workspace.dependencies]
|
||||
typst = { path = "crates/typst", version = "0.13.0-rc1" }
|
||||
typst-cli = { path = "crates/typst-cli", version = "0.13.0-rc1" }
|
||||
typst-eval = { path = "crates/typst-eval", version = "0.13.0-rc1" }
|
||||
typst-html = { path = "crates/typst-html", version = "0.13.0-rc1" }
|
||||
typst-ide = { path = "crates/typst-ide", version = "0.13.0-rc1" }
|
||||
typst-kit = { path = "crates/typst-kit", version = "0.13.0-rc1" }
|
||||
typst-layout = { path = "crates/typst-layout", version = "0.13.0-rc1" }
|
||||
typst-library = { path = "crates/typst-library", version = "0.13.0-rc1" }
|
||||
typst-macros = { path = "crates/typst-macros", version = "0.13.0-rc1" }
|
||||
typst-pdf = { path = "crates/typst-pdf", version = "0.13.0-rc1" }
|
||||
typst-realize = { path = "crates/typst-realize", version = "0.13.0-rc1" }
|
||||
typst-render = { path = "crates/typst-render", version = "0.13.0-rc1" }
|
||||
typst-svg = { path = "crates/typst-svg", version = "0.13.0-rc1" }
|
||||
typst-syntax = { path = "crates/typst-syntax", version = "0.13.0-rc1" }
|
||||
typst-timing = { path = "crates/typst-timing", version = "0.13.0-rc1" }
|
||||
typst-utils = { path = "crates/typst-utils", version = "0.13.0-rc1" }
|
||||
typst-assets = { git = "https://github.com/typst/typst-assets", rev = "7eb87f5" }
|
||||
typst-dev-assets = { git = "https://github.com/typst/typst-dev-assets", rev = "7f8999d" }
|
||||
typst = { path = "crates/typst", version = "0.13.0" }
|
||||
typst-cli = { path = "crates/typst-cli", version = "0.13.0" }
|
||||
typst-eval = { path = "crates/typst-eval", version = "0.13.0" }
|
||||
typst-html = { path = "crates/typst-html", version = "0.13.0" }
|
||||
typst-ide = { path = "crates/typst-ide", version = "0.13.0" }
|
||||
typst-kit = { path = "crates/typst-kit", version = "0.13.0" }
|
||||
typst-layout = { path = "crates/typst-layout", version = "0.13.0" }
|
||||
typst-library = { path = "crates/typst-library", version = "0.13.0" }
|
||||
typst-macros = { path = "crates/typst-macros", version = "0.13.0" }
|
||||
typst-pdf = { path = "crates/typst-pdf", version = "0.13.0" }
|
||||
typst-realize = { path = "crates/typst-realize", version = "0.13.0" }
|
||||
typst-render = { path = "crates/typst-render", version = "0.13.0" }
|
||||
typst-svg = { path = "crates/typst-svg", version = "0.13.0" }
|
||||
typst-syntax = { path = "crates/typst-syntax", version = "0.13.0" }
|
||||
typst-timing = { path = "crates/typst-timing", version = "0.13.0" }
|
||||
typst-utils = { path = "crates/typst-utils", version = "0.13.0" }
|
||||
typst-assets = "0.13.0"
|
||||
typst-dev-assets = { git = "https://github.com/typst/typst-dev-assets", tag = "v0.13.0" }
|
||||
arrayvec = "0.7.4"
|
||||
az = "1.2"
|
||||
base64 = "0.22"
|
||||
|
|
@ -74,6 +74,7 @@ kamadak-exif = "0.6"
|
|||
kurbo = "0.11"
|
||||
libfuzzer-sys = "0.4"
|
||||
lipsum = "0.9"
|
||||
memchr = "2"
|
||||
miniz_oxide = "0.8"
|
||||
native-tls = "0.2"
|
||||
notify = "8"
|
||||
|
|
|
|||
|
|
@ -6,8 +6,9 @@ use std::path::{Path, PathBuf};
|
|||
use chrono::{DateTime, Datelike, Timelike, Utc};
|
||||
use codespan_reporting::diagnostic::{Diagnostic, Label};
|
||||
use codespan_reporting::term;
|
||||
use ecow::{eco_format, EcoString};
|
||||
use ecow::eco_format;
|
||||
use parking_lot::RwLock;
|
||||
use pathdiff::diff_paths;
|
||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||
use typst::diag::{
|
||||
bail, At, Severity, SourceDiagnostic, SourceResult, StrResult, Warned,
|
||||
|
|
@ -188,7 +189,7 @@ pub fn compile_once(
|
|||
|
||||
match output {
|
||||
// Export the PDF / PNG.
|
||||
Ok(()) => {
|
||||
Ok(outputs) => {
|
||||
let duration = start.elapsed();
|
||||
|
||||
if config.watching {
|
||||
|
|
@ -202,7 +203,7 @@ pub fn compile_once(
|
|||
print_diagnostics(world, &[], &warnings, config.diagnostic_format)
|
||||
.map_err(|err| eco_format!("failed to print diagnostics ({err})"))?;
|
||||
|
||||
write_make_deps(world, config)?;
|
||||
write_make_deps(world, config, outputs)?;
|
||||
open_output(config)?;
|
||||
}
|
||||
|
||||
|
|
@ -226,12 +227,15 @@ pub fn compile_once(
|
|||
fn compile_and_export(
|
||||
world: &mut SystemWorld,
|
||||
config: &mut CompileConfig,
|
||||
) -> Warned<SourceResult<()>> {
|
||||
) -> Warned<SourceResult<Vec<Output>>> {
|
||||
match config.output_format {
|
||||
OutputFormat::Html => {
|
||||
let Warned { output, warnings } = typst::compile::<HtmlDocument>(world);
|
||||
let result = output.and_then(|document| export_html(&document, config));
|
||||
Warned { output: result, warnings }
|
||||
Warned {
|
||||
output: result.map(|()| vec![config.output.clone()]),
|
||||
warnings,
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let Warned { output, warnings } = typst::compile::<PagedDocument>(world);
|
||||
|
|
@ -257,9 +261,14 @@ fn export_html(document: &HtmlDocument, config: &CompileConfig) -> SourceResult<
|
|||
}
|
||||
|
||||
/// Export to a paged target format.
|
||||
fn export_paged(document: &PagedDocument, config: &CompileConfig) -> SourceResult<()> {
|
||||
fn export_paged(
|
||||
document: &PagedDocument,
|
||||
config: &CompileConfig,
|
||||
) -> SourceResult<Vec<Output>> {
|
||||
match config.output_format {
|
||||
OutputFormat::Pdf => export_pdf(document, config),
|
||||
OutputFormat::Pdf => {
|
||||
export_pdf(document, config).map(|()| vec![config.output.clone()])
|
||||
}
|
||||
OutputFormat::Png => {
|
||||
export_image(document, config, ImageExportFormat::Png).at(Span::detached())
|
||||
}
|
||||
|
|
@ -327,7 +336,7 @@ fn export_image(
|
|||
document: &PagedDocument,
|
||||
config: &CompileConfig,
|
||||
fmt: ImageExportFormat,
|
||||
) -> StrResult<()> {
|
||||
) -> StrResult<Vec<Output>> {
|
||||
// Determine whether we have indexable templates in output
|
||||
let can_handle_multiple = match config.output {
|
||||
Output::Stdout => false,
|
||||
|
|
@ -383,7 +392,7 @@ fn export_image(
|
|||
&& config.export_cache.is_cached(*i, &page.frame)
|
||||
&& path.exists()
|
||||
{
|
||||
return Ok(());
|
||||
return Ok(Output::Path(path.to_path_buf()));
|
||||
}
|
||||
|
||||
Output::Path(path.to_owned())
|
||||
|
|
@ -392,11 +401,9 @@ fn export_image(
|
|||
};
|
||||
|
||||
export_image_page(config, page, &output, fmt)?;
|
||||
Ok(())
|
||||
Ok(output)
|
||||
})
|
||||
.collect::<Result<Vec<()>, EcoString>>()?;
|
||||
|
||||
Ok(())
|
||||
.collect::<StrResult<Vec<Output>>>()
|
||||
}
|
||||
|
||||
mod output_template {
|
||||
|
|
@ -501,14 +508,25 @@ impl ExportCache {
|
|||
/// Writes a Makefile rule describing the relationship between the output and
|
||||
/// its dependencies to the path specified by the --make-deps argument, if it
|
||||
/// was provided.
|
||||
fn write_make_deps(world: &mut SystemWorld, config: &CompileConfig) -> StrResult<()> {
|
||||
fn write_make_deps(
|
||||
world: &mut SystemWorld,
|
||||
config: &CompileConfig,
|
||||
outputs: Vec<Output>,
|
||||
) -> StrResult<()> {
|
||||
let Some(ref make_deps_path) = config.make_deps else { return Ok(()) };
|
||||
let Output::Path(output_path) = &config.output else {
|
||||
bail!("failed to create make dependencies file because output was stdout")
|
||||
};
|
||||
let Some(output_path) = output_path.as_os_str().to_str() else {
|
||||
let Ok(output_paths) = outputs
|
||||
.into_iter()
|
||||
.filter_map(|o| match o {
|
||||
Output::Path(path) => Some(path.into_os_string().into_string()),
|
||||
Output::Stdout => None,
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
else {
|
||||
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")
|
||||
}
|
||||
|
||||
// Based on `munge` in libcpp/mkdeps.cc from the GCC source code. This isn't
|
||||
// perfect as some special characters can't be escaped.
|
||||
|
|
@ -522,6 +540,10 @@ fn write_make_deps(world: &mut SystemWorld, config: &CompileConfig) -> StrResult
|
|||
res.push('$');
|
||||
slashes = 0;
|
||||
}
|
||||
':' => {
|
||||
res.push('\\');
|
||||
slashes = 0;
|
||||
}
|
||||
' ' | '\t' => {
|
||||
// `munge`'s source contains a comment here that says: "A
|
||||
// space or tab preceded by 2N+1 backslashes represents N
|
||||
|
|
@ -544,18 +566,29 @@ fn write_make_deps(world: &mut SystemWorld, config: &CompileConfig) -> StrResult
|
|||
|
||||
fn write(
|
||||
make_deps_path: &Path,
|
||||
output_path: &str,
|
||||
output_paths: Vec<String>,
|
||||
root: PathBuf,
|
||||
dependencies: impl Iterator<Item = PathBuf>,
|
||||
) -> io::Result<()> {
|
||||
let mut file = File::create(make_deps_path)?;
|
||||
let current_dir = std::env::current_dir()?;
|
||||
let relative_root = diff_paths(&root, ¤t_dir).unwrap_or(root.clone());
|
||||
|
||||
file.write_all(munge(output_path).as_bytes())?;
|
||||
for (i, output_path) in output_paths.into_iter().enumerate() {
|
||||
if i != 0 {
|
||||
file.write_all(b" ")?;
|
||||
}
|
||||
file.write_all(munge(&output_path).as_bytes())?;
|
||||
}
|
||||
file.write_all(b":")?;
|
||||
for dependency in dependencies {
|
||||
let Some(dependency) =
|
||||
dependency.strip_prefix(&root).unwrap_or(&dependency).to_str()
|
||||
else {
|
||||
let relative_dependency = match dependency.strip_prefix(&root) {
|
||||
Ok(root_relative_dependency) => {
|
||||
relative_root.join(root_relative_dependency)
|
||||
}
|
||||
Err(_) => dependency,
|
||||
};
|
||||
let Some(relative_dependency) = relative_dependency.to_str() else {
|
||||
// Silently skip paths that aren't valid unicode so we still
|
||||
// produce a rule that will work for the other paths that can be
|
||||
// processed.
|
||||
|
|
@ -563,14 +596,14 @@ fn write_make_deps(world: &mut SystemWorld, config: &CompileConfig) -> StrResult
|
|||
};
|
||||
|
||||
file.write_all(b" ")?;
|
||||
file.write_all(munge(dependency).as_bytes())?;
|
||||
file.write_all(munge(relative_dependency).as_bytes())?;
|
||||
}
|
||||
file.write_all(b"\n")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
write(make_deps_path, output_path, world.root().to_owned(), world.dependencies())
|
||||
write(make_deps_path, output_paths, world.root().to_owned(), world.dependencies())
|
||||
.map_err(|err| {
|
||||
eco_format!("failed to create make dependencies file due to IO error ({err})")
|
||||
})
|
||||
|
|
|
|||
|
|
@ -204,6 +204,10 @@ impl Watcher {
|
|||
let event = event
|
||||
.map_err(|err| eco_format!("failed to watch dependencies ({err})"))?;
|
||||
|
||||
if !is_relevant_event_kind(&event.kind) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Workaround for notify-rs' implicit unwatch on remove/rename
|
||||
// (triggered by some editors when saving files) with the
|
||||
// inotify backend. By keeping track of the potentially
|
||||
|
|
@ -224,7 +228,17 @@ impl Watcher {
|
|||
}
|
||||
}
|
||||
|
||||
relevant |= self.is_event_relevant(&event);
|
||||
// Don't recompile because the output file changed.
|
||||
// FIXME: This doesn't work properly for multifile image export.
|
||||
if event
|
||||
.paths
|
||||
.iter()
|
||||
.all(|path| is_same_file(path, &self.output).unwrap_or(false))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
relevant = true;
|
||||
}
|
||||
|
||||
// If we found a relevant event or if any of the missing files now
|
||||
|
|
@ -234,32 +248,23 @@ impl Watcher {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether a watch event is relevant for compilation.
|
||||
fn is_event_relevant(&self, event: ¬ify::Event) -> bool {
|
||||
// Never recompile because the output file changed.
|
||||
if event
|
||||
.paths
|
||||
.iter()
|
||||
.all(|path| is_same_file(path, &self.output).unwrap_or(false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
match &event.kind {
|
||||
notify::EventKind::Any => true,
|
||||
notify::EventKind::Access(_) => false,
|
||||
notify::EventKind::Create(_) => true,
|
||||
notify::EventKind::Modify(kind) => match kind {
|
||||
notify::event::ModifyKind::Any => true,
|
||||
notify::event::ModifyKind::Data(_) => true,
|
||||
notify::event::ModifyKind::Metadata(_) => false,
|
||||
notify::event::ModifyKind::Name(_) => true,
|
||||
notify::event::ModifyKind::Other => false,
|
||||
},
|
||||
notify::EventKind::Remove(_) => true,
|
||||
notify::EventKind::Other => false,
|
||||
}
|
||||
/// Whether a kind of watch event is relevant for compilation.
|
||||
fn is_relevant_event_kind(kind: ¬ify::EventKind) -> bool {
|
||||
match kind {
|
||||
notify::EventKind::Any => true,
|
||||
notify::EventKind::Access(_) => false,
|
||||
notify::EventKind::Create(_) => true,
|
||||
notify::EventKind::Modify(kind) => match kind {
|
||||
notify::event::ModifyKind::Any => true,
|
||||
notify::event::ModifyKind::Data(_) => true,
|
||||
notify::event::ModifyKind::Metadata(_) => false,
|
||||
notify::event::ModifyKind::Name(_) => true,
|
||||
notify::event::ModifyKind::Other => false,
|
||||
},
|
||||
notify::EventKind::Remove(_) => true,
|
||||
notify::EventKind::Other => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ fn eval_code<'a>(
|
|||
_ => expr.eval(vm)?,
|
||||
};
|
||||
|
||||
output = ops::join(output, value).at(span)?;
|
||||
output = ops::join(output, value, &mut (&mut vm.engine, span)).at(span)?;
|
||||
|
||||
if let Some(event) = &vm.flow {
|
||||
warn_for_discarded_content(&mut vm.engine, event, &output);
|
||||
|
|
|
|||
|
|
@ -83,7 +83,8 @@ impl Eval for ast::WhileLoop<'_> {
|
|||
}
|
||||
|
||||
let value = body.eval(vm)?;
|
||||
output = ops::join(output, value).at(body.span())?;
|
||||
let span = body.span();
|
||||
output = ops::join(output, value, &mut (&mut vm.engine, span)).at(span)?;
|
||||
|
||||
match vm.flow {
|
||||
Some(FlowEvent::Break(_)) => {
|
||||
|
|
@ -129,7 +130,9 @@ impl Eval for ast::ForLoop<'_> {
|
|||
|
||||
let body = self.body();
|
||||
let value = body.eval(vm)?;
|
||||
output = ops::join(output, value).at(body.span())?;
|
||||
let span = body.span();
|
||||
output =
|
||||
ops::join(output, value, &mut (&mut vm.engine, span)).at(span)?;
|
||||
|
||||
match vm.flow {
|
||||
Some(FlowEvent::Break(_)) => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use typst_library::diag::{At, HintedStrResult, SourceResult};
|
||||
use typst_library::diag::{At, DeprecationSink, HintedStrResult, SourceResult};
|
||||
use typst_library::foundations::{ops, IntoValue, Value};
|
||||
use typst_syntax::ast::{self, AstNode};
|
||||
|
||||
|
|
@ -23,22 +23,22 @@ impl Eval for ast::Binary<'_> {
|
|||
|
||||
fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||
match self.op() {
|
||||
ast::BinOp::Add => apply_binary(self, vm, ops::add),
|
||||
ast::BinOp::Add => apply_binary_with_sink(self, vm, ops::add),
|
||||
ast::BinOp::Sub => apply_binary(self, vm, ops::sub),
|
||||
ast::BinOp::Mul => apply_binary(self, vm, ops::mul),
|
||||
ast::BinOp::Div => apply_binary(self, vm, ops::div),
|
||||
ast::BinOp::And => apply_binary(self, vm, ops::and),
|
||||
ast::BinOp::Or => apply_binary(self, vm, ops::or),
|
||||
ast::BinOp::Eq => apply_binary(self, vm, ops::eq),
|
||||
ast::BinOp::Neq => apply_binary(self, vm, ops::neq),
|
||||
ast::BinOp::Eq => apply_binary_with_sink(self, vm, ops::eq),
|
||||
ast::BinOp::Neq => apply_binary_with_sink(self, vm, ops::neq),
|
||||
ast::BinOp::Lt => apply_binary(self, vm, ops::lt),
|
||||
ast::BinOp::Leq => apply_binary(self, vm, ops::leq),
|
||||
ast::BinOp::Gt => apply_binary(self, vm, ops::gt),
|
||||
ast::BinOp::Geq => apply_binary(self, vm, ops::geq),
|
||||
ast::BinOp::In => apply_binary(self, vm, ops::in_),
|
||||
ast::BinOp::NotIn => apply_binary(self, vm, ops::not_in),
|
||||
ast::BinOp::In => apply_binary_with_sink(self, vm, ops::in_),
|
||||
ast::BinOp::NotIn => apply_binary_with_sink(self, vm, ops::not_in),
|
||||
ast::BinOp::Assign => apply_assignment(self, vm, |_, b| Ok(b)),
|
||||
ast::BinOp::AddAssign => apply_assignment(self, vm, ops::add),
|
||||
ast::BinOp::AddAssign => apply_assignment_with_sink(self, vm, ops::add),
|
||||
ast::BinOp::SubAssign => apply_assignment(self, vm, ops::sub),
|
||||
ast::BinOp::MulAssign => apply_assignment(self, vm, ops::mul),
|
||||
ast::BinOp::DivAssign => apply_assignment(self, vm, ops::div),
|
||||
|
|
@ -65,6 +65,18 @@ fn apply_binary(
|
|||
op(lhs, rhs).at(binary.span())
|
||||
}
|
||||
|
||||
/// Apply a basic binary operation, with the possiblity of deprecations.
|
||||
fn apply_binary_with_sink(
|
||||
binary: ast::Binary,
|
||||
vm: &mut Vm,
|
||||
op: impl Fn(Value, Value, &mut dyn DeprecationSink) -> HintedStrResult<Value>,
|
||||
) -> SourceResult<Value> {
|
||||
let span = binary.span();
|
||||
let lhs = binary.lhs().eval(vm)?;
|
||||
let rhs = binary.rhs().eval(vm)?;
|
||||
op(lhs, rhs, &mut (&mut vm.engine, span)).at(span)
|
||||
}
|
||||
|
||||
/// Apply an assignment operation.
|
||||
fn apply_assignment(
|
||||
binary: ast::Binary,
|
||||
|
|
@ -89,3 +101,23 @@ fn apply_assignment(
|
|||
*location = op(lhs, rhs).at(binary.span())?;
|
||||
Ok(Value::None)
|
||||
}
|
||||
|
||||
/// Apply an assignment operation, with the possiblity of deprecations.
|
||||
fn apply_assignment_with_sink(
|
||||
binary: ast::Binary,
|
||||
vm: &mut Vm,
|
||||
op: fn(Value, Value, &mut dyn DeprecationSink) -> HintedStrResult<Value>,
|
||||
) -> SourceResult<Value> {
|
||||
let rhs = binary.rhs().eval(vm)?;
|
||||
let location = binary.lhs().access(vm)?;
|
||||
let lhs = std::mem::take(&mut *location);
|
||||
let mut sink = vec![];
|
||||
let span = binary.span();
|
||||
*location = op(lhs, rhs, &mut (&mut sink, span)).at(span)?;
|
||||
if !sink.is_empty() {
|
||||
for warning in sink {
|
||||
vm.engine.sink.warn(warning);
|
||||
}
|
||||
}
|
||||
Ok(Value::None)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,8 +83,8 @@ fn html_document_impl(
|
|||
)?;
|
||||
|
||||
let output = handle_list(&mut engine, &mut locator, children.iter().copied())?;
|
||||
let introspector = Introspector::html(&output);
|
||||
let root = root_element(output, &info)?;
|
||||
let introspector = Introspector::html(&root);
|
||||
|
||||
Ok(HtmlDocument { info, root, introspector })
|
||||
}
|
||||
|
|
@ -307,18 +307,18 @@ fn head_element(info: &DocumentInfo) -> HtmlElement {
|
|||
|
||||
/// Determine which kind of output the user generated.
|
||||
fn classify_output(mut output: Vec<HtmlNode>) -> SourceResult<OutputKind> {
|
||||
let len = output.len();
|
||||
let count = output.iter().filter(|node| !matches!(node, HtmlNode::Tag(_))).count();
|
||||
for node in &mut output {
|
||||
let HtmlNode::Element(elem) = node else { continue };
|
||||
let tag = elem.tag;
|
||||
let mut take = || std::mem::replace(elem, HtmlElement::new(tag::html));
|
||||
match (tag, len) {
|
||||
match (tag, count) {
|
||||
(tag::html, 1) => return Ok(OutputKind::Html(take())),
|
||||
(tag::body, 1) => return Ok(OutputKind::Body(take())),
|
||||
(tag::html | tag::body, _) => bail!(
|
||||
elem.span,
|
||||
"`{}` element must be the only element in the document",
|
||||
elem.tag
|
||||
elem.tag,
|
||||
),
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ indexmap = { workspace = true }
|
|||
kamadak-exif = { workspace = true }
|
||||
kurbo = { workspace = true }
|
||||
lipsum = { workspace = true }
|
||||
memchr = { workspace = true }
|
||||
palette = { workspace = true }
|
||||
phf = { workspace = true }
|
||||
png = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -232,18 +232,42 @@ impl From<SyntaxError> for SourceDiagnostic {
|
|||
/// Destination for a deprecation message when accessing a deprecated value.
|
||||
pub trait DeprecationSink {
|
||||
/// Emits the given deprecation message into this sink.
|
||||
fn emit(self, message: &str);
|
||||
fn emit(&mut self, message: &str);
|
||||
|
||||
/// Emits the given deprecation message into this sink, with the given
|
||||
/// hints.
|
||||
fn emit_with_hints(&mut self, message: &str, hints: &[&str]);
|
||||
}
|
||||
|
||||
impl DeprecationSink for () {
|
||||
fn emit(self, _: &str) {}
|
||||
fn emit(&mut self, _: &str) {}
|
||||
fn emit_with_hints(&mut self, _: &str, _: &[&str]) {}
|
||||
}
|
||||
|
||||
impl DeprecationSink for (&mut Vec<SourceDiagnostic>, Span) {
|
||||
fn emit(&mut self, message: &str) {
|
||||
self.0.push(SourceDiagnostic::warning(self.1, message));
|
||||
}
|
||||
|
||||
fn emit_with_hints(&mut self, message: &str, hints: &[&str]) {
|
||||
self.0.push(
|
||||
SourceDiagnostic::warning(self.1, message)
|
||||
.with_hints(hints.iter().copied().map(Into::into)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl DeprecationSink for (&mut Engine<'_>, Span) {
|
||||
/// Emits the deprecation message as a warning.
|
||||
fn emit(self, message: &str) {
|
||||
fn emit(&mut self, message: &str) {
|
||||
self.0.sink.warn(SourceDiagnostic::warning(self.1, message));
|
||||
}
|
||||
|
||||
fn emit_with_hints(&mut self, message: &str, hints: &[&str]) {
|
||||
self.0.sink.warn(
|
||||
SourceDiagnostic::warning(self.1, message)
|
||||
.with_hints(hints.iter().copied().map(Into::into)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A part of a diagnostic's [trace](SourceDiagnostic::trace).
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@ use serde::{Deserialize, Serialize};
|
|||
use smallvec::SmallVec;
|
||||
use typst_syntax::{Span, Spanned};
|
||||
|
||||
use crate::diag::{bail, At, HintedStrResult, SourceDiagnostic, SourceResult, StrResult};
|
||||
use crate::diag::{
|
||||
bail, At, DeprecationSink, HintedStrResult, SourceDiagnostic, SourceResult, StrResult,
|
||||
};
|
||||
use crate::engine::Engine;
|
||||
use crate::foundations::{
|
||||
cast, func, ops, repr, scope, ty, Args, Bytes, CastInfo, Context, Dict, FromValue,
|
||||
|
|
@ -143,6 +145,11 @@ impl Array {
|
|||
|
||||
Ok(self.iter().cloned().cycle().take(count).collect())
|
||||
}
|
||||
|
||||
/// The internal implementation of [`Array::contains`].
|
||||
pub fn contains_impl(&self, value: &Value, sink: &mut dyn DeprecationSink) -> bool {
|
||||
self.0.iter().any(|v| ops::equal(v, value, sink))
|
||||
}
|
||||
}
|
||||
|
||||
#[scope]
|
||||
|
|
@ -290,10 +297,12 @@ impl Array {
|
|||
#[func]
|
||||
pub fn contains(
|
||||
&self,
|
||||
engine: &mut Engine,
|
||||
span: Span,
|
||||
/// The value to search for.
|
||||
value: Value,
|
||||
) -> bool {
|
||||
self.0.contains(&value)
|
||||
self.contains_impl(&value, &mut (engine, span))
|
||||
}
|
||||
|
||||
/// Searches for an item for which the given function returns `{true}` and
|
||||
|
|
@ -576,6 +585,8 @@ impl Array {
|
|||
#[func]
|
||||
pub fn sum(
|
||||
self,
|
||||
engine: &mut Engine,
|
||||
span: Span,
|
||||
/// What to return if the array is empty. Must be set if the array can
|
||||
/// be empty.
|
||||
#[named]
|
||||
|
|
@ -587,7 +598,7 @@ impl Array {
|
|||
.or(default)
|
||||
.ok_or("cannot calculate sum of empty array with no default")?;
|
||||
for item in iter {
|
||||
acc = ops::add(acc, item)?;
|
||||
acc = ops::add(acc, item, &mut (&mut *engine, span))?;
|
||||
}
|
||||
Ok(acc)
|
||||
}
|
||||
|
|
@ -686,6 +697,8 @@ impl Array {
|
|||
#[func]
|
||||
pub fn join(
|
||||
self,
|
||||
engine: &mut Engine,
|
||||
span: Span,
|
||||
/// A value to insert between each item of the array.
|
||||
#[default]
|
||||
separator: Option<Value>,
|
||||
|
|
@ -701,13 +714,18 @@ impl Array {
|
|||
for (i, value) in self.into_iter().enumerate() {
|
||||
if i > 0 {
|
||||
if i + 1 == len && last.is_some() {
|
||||
result = ops::join(result, last.take().unwrap())?;
|
||||
result = ops::join(
|
||||
result,
|
||||
last.take().unwrap(),
|
||||
&mut (&mut *engine, span),
|
||||
)?;
|
||||
} else {
|
||||
result = ops::join(result, separator.clone())?;
|
||||
result =
|
||||
ops::join(result, separator.clone(), &mut (&mut *engine, span))?;
|
||||
}
|
||||
}
|
||||
|
||||
result = ops::join(result, value)?;
|
||||
result = ops::join(result, value, &mut (&mut *engine, span))?;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
|
|
@ -862,13 +880,14 @@ impl Array {
|
|||
self,
|
||||
engine: &mut Engine,
|
||||
context: Tracked<Context>,
|
||||
span: Span,
|
||||
/// If given, applies this function to the elements in the array to
|
||||
/// determine the keys to deduplicate by.
|
||||
#[named]
|
||||
key: Option<Func>,
|
||||
) -> SourceResult<Array> {
|
||||
let mut out = EcoVec::with_capacity(self.0.len());
|
||||
let mut key_of = |x: Value| match &key {
|
||||
let key_of = |engine: &mut Engine, x: Value| match &key {
|
||||
// NOTE: We are relying on `comemo`'s memoization of function
|
||||
// evaluation to not excessively reevaluate the `key`.
|
||||
Some(f) => f.call(engine, context, [x]),
|
||||
|
|
@ -879,14 +898,18 @@ impl Array {
|
|||
// 1. We would like to preserve the order of the elements.
|
||||
// 2. We cannot hash arbitrary `Value`.
|
||||
'outer: for value in self {
|
||||
let key = key_of(value.clone())?;
|
||||
let key = key_of(&mut *engine, value.clone())?;
|
||||
if out.is_empty() {
|
||||
out.push(value);
|
||||
continue;
|
||||
}
|
||||
|
||||
for second in out.iter() {
|
||||
if ops::equal(&key, &key_of(second.clone())?) {
|
||||
if ops::equal(
|
||||
&key,
|
||||
&key_of(&mut *engine, second.clone())?,
|
||||
&mut (&mut *engine, span),
|
||||
) {
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -437,10 +437,10 @@ impl PartialEq for Func {
|
|||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&NativeFuncData> for Func {
|
||||
fn eq(&self, other: &&NativeFuncData) -> bool {
|
||||
impl PartialEq<&'static NativeFuncData> for Func {
|
||||
fn eq(&self, other: &&'static NativeFuncData) -> bool {
|
||||
match &self.repr {
|
||||
Repr::Native(native) => native.function == other.function,
|
||||
Repr::Native(native) => *native == Static(*other),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use std::cmp::Ordering;
|
|||
use ecow::eco_format;
|
||||
use typst_utils::Numeric;
|
||||
|
||||
use crate::diag::{bail, HintedStrResult, StrResult};
|
||||
use crate::diag::{bail, DeprecationSink, HintedStrResult, StrResult};
|
||||
use crate::foundations::{
|
||||
format_str, Datetime, IntoValue, Regex, Repr, SymbolElem, Value,
|
||||
};
|
||||
|
|
@ -21,7 +21,7 @@ macro_rules! mismatch {
|
|||
}
|
||||
|
||||
/// Join a value with another value.
|
||||
pub fn join(lhs: Value, rhs: Value) -> StrResult<Value> {
|
||||
pub fn join(lhs: Value, rhs: Value, sink: &mut dyn DeprecationSink) -> StrResult<Value> {
|
||||
use Value::*;
|
||||
Ok(match (lhs, rhs) {
|
||||
(a, None) => a,
|
||||
|
|
@ -39,6 +39,17 @@ pub fn join(lhs: Value, rhs: Value) -> StrResult<Value> {
|
|||
(Array(a), Array(b)) => Array(a + b),
|
||||
(Dict(a), Dict(b)) => Dict(a + b),
|
||||
(Args(a), Args(b)) => Args(a + b),
|
||||
|
||||
// Type compatibility.
|
||||
(Type(a), Str(b)) => {
|
||||
warn_type_str_join(sink);
|
||||
Str(format_str!("{a}{b}"))
|
||||
}
|
||||
(Str(a), Type(b)) => {
|
||||
warn_type_str_join(sink);
|
||||
Str(format_str!("{a}{b}"))
|
||||
}
|
||||
|
||||
(a, b) => mismatch!("cannot join {} with {}", a, b),
|
||||
})
|
||||
}
|
||||
|
|
@ -88,7 +99,11 @@ pub fn neg(value: Value) -> HintedStrResult<Value> {
|
|||
}
|
||||
|
||||
/// Compute the sum of two values.
|
||||
pub fn add(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
|
||||
pub fn add(
|
||||
lhs: Value,
|
||||
rhs: Value,
|
||||
sink: &mut dyn DeprecationSink,
|
||||
) -> HintedStrResult<Value> {
|
||||
use Value::*;
|
||||
Ok(match (lhs, rhs) {
|
||||
(a, None) => a,
|
||||
|
|
@ -156,6 +171,16 @@ pub fn add(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
|
|||
(Datetime(a), Duration(b)) => Datetime(a + b),
|
||||
(Duration(a), Datetime(b)) => Datetime(b + a),
|
||||
|
||||
// Type compatibility.
|
||||
(Type(a), Str(b)) => {
|
||||
warn_type_str_add(sink);
|
||||
Str(format_str!("{a}{b}"))
|
||||
}
|
||||
(Str(a), Type(b)) => {
|
||||
warn_type_str_add(sink);
|
||||
Str(format_str!("{a}{b}"))
|
||||
}
|
||||
|
||||
(Dyn(a), Dyn(b)) => {
|
||||
// Alignments can be summed.
|
||||
if let (Some(&a), Some(&b)) =
|
||||
|
|
@ -394,13 +419,21 @@ pub fn or(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
|
|||
}
|
||||
|
||||
/// Compute whether two values are equal.
|
||||
pub fn eq(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
|
||||
Ok(Value::Bool(equal(&lhs, &rhs)))
|
||||
pub fn eq(
|
||||
lhs: Value,
|
||||
rhs: Value,
|
||||
sink: &mut dyn DeprecationSink,
|
||||
) -> HintedStrResult<Value> {
|
||||
Ok(Value::Bool(equal(&lhs, &rhs, sink)))
|
||||
}
|
||||
|
||||
/// Compute whether two values are unequal.
|
||||
pub fn neq(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
|
||||
Ok(Value::Bool(!equal(&lhs, &rhs)))
|
||||
pub fn neq(
|
||||
lhs: Value,
|
||||
rhs: Value,
|
||||
sink: &mut dyn DeprecationSink,
|
||||
) -> HintedStrResult<Value> {
|
||||
Ok(Value::Bool(!equal(&lhs, &rhs, sink)))
|
||||
}
|
||||
|
||||
macro_rules! comparison {
|
||||
|
|
@ -419,7 +452,7 @@ comparison!(gt, ">", Ordering::Greater);
|
|||
comparison!(geq, ">=", Ordering::Greater | Ordering::Equal);
|
||||
|
||||
/// Determine whether two values are equal.
|
||||
pub fn equal(lhs: &Value, rhs: &Value) -> bool {
|
||||
pub fn equal(lhs: &Value, rhs: &Value, sink: &mut dyn DeprecationSink) -> bool {
|
||||
use Value::*;
|
||||
match (lhs, rhs) {
|
||||
// Compare reflexively.
|
||||
|
|
@ -463,6 +496,12 @@ pub fn equal(lhs: &Value, rhs: &Value) -> bool {
|
|||
rat == rel.rel && rel.abs.is_zero()
|
||||
}
|
||||
|
||||
// Type compatibility.
|
||||
(Type(ty), Str(str)) | (Str(str), Type(ty)) => {
|
||||
warn_type_str_equal(sink, str);
|
||||
ty.compat_name() == str.as_str()
|
||||
}
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -534,8 +573,12 @@ fn try_cmp_arrays(a: &[Value], b: &[Value]) -> StrResult<Ordering> {
|
|||
}
|
||||
|
||||
/// Test whether one value is "in" another one.
|
||||
pub fn in_(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
|
||||
if let Some(b) = contains(&lhs, &rhs) {
|
||||
pub fn in_(
|
||||
lhs: Value,
|
||||
rhs: Value,
|
||||
sink: &mut dyn DeprecationSink,
|
||||
) -> HintedStrResult<Value> {
|
||||
if let Some(b) = contains(&lhs, &rhs, sink) {
|
||||
Ok(Value::Bool(b))
|
||||
} else {
|
||||
mismatch!("cannot apply 'in' to {} and {}", lhs, rhs)
|
||||
|
|
@ -543,8 +586,12 @@ pub fn in_(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
|
|||
}
|
||||
|
||||
/// Test whether one value is "not in" another one.
|
||||
pub fn not_in(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
|
||||
if let Some(b) = contains(&lhs, &rhs) {
|
||||
pub fn not_in(
|
||||
lhs: Value,
|
||||
rhs: Value,
|
||||
sink: &mut dyn DeprecationSink,
|
||||
) -> HintedStrResult<Value> {
|
||||
if let Some(b) = contains(&lhs, &rhs, sink) {
|
||||
Ok(Value::Bool(!b))
|
||||
} else {
|
||||
mismatch!("cannot apply 'not in' to {} and {}", lhs, rhs)
|
||||
|
|
@ -552,13 +599,27 @@ pub fn not_in(lhs: Value, rhs: Value) -> HintedStrResult<Value> {
|
|||
}
|
||||
|
||||
/// Test for containment.
|
||||
pub fn contains(lhs: &Value, rhs: &Value) -> Option<bool> {
|
||||
pub fn contains(
|
||||
lhs: &Value,
|
||||
rhs: &Value,
|
||||
sink: &mut dyn DeprecationSink,
|
||||
) -> Option<bool> {
|
||||
use Value::*;
|
||||
match (lhs, rhs) {
|
||||
(Str(a), Str(b)) => Some(b.as_str().contains(a.as_str())),
|
||||
(Dyn(a), Str(b)) => a.downcast::<Regex>().map(|regex| regex.is_match(b)),
|
||||
(Str(a), Dict(b)) => Some(b.contains(a)),
|
||||
(a, Array(b)) => Some(b.contains(a.clone())),
|
||||
(a, Array(b)) => Some(b.contains_impl(a, sink)),
|
||||
|
||||
// Type compatibility.
|
||||
(Type(a), Str(b)) => {
|
||||
warn_type_in_str(sink);
|
||||
Some(b.as_str().contains(a.compat_name()))
|
||||
}
|
||||
(Type(a), Dict(b)) => {
|
||||
warn_type_in_dict(sink);
|
||||
Some(b.contains(a.compat_name()))
|
||||
}
|
||||
|
||||
_ => Option::None,
|
||||
}
|
||||
|
|
@ -568,3 +629,90 @@ pub fn contains(lhs: &Value, rhs: &Value) -> Option<bool> {
|
|||
fn too_large() -> &'static str {
|
||||
"value is too large"
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn warn_type_str_add(sink: &mut dyn DeprecationSink) {
|
||||
sink.emit_with_hints(
|
||||
"adding strings and types is deprecated",
|
||||
&["convert the type to a string with `str` first"],
|
||||
);
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn warn_type_str_join(sink: &mut dyn DeprecationSink) {
|
||||
sink.emit_with_hints(
|
||||
"joining strings and types is deprecated",
|
||||
&["convert the type to a string with `str` first"],
|
||||
);
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn warn_type_str_equal(sink: &mut dyn DeprecationSink, s: &str) {
|
||||
// Only warn if `s` looks like a type name to prevent false positives.
|
||||
if is_compat_type_name(s) {
|
||||
sink.emit_with_hints(
|
||||
"comparing strings with types is deprecated",
|
||||
&[
|
||||
"compare with the literal type instead",
|
||||
"this comparison will always return `false` in future Typst releases",
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn warn_type_in_str(sink: &mut dyn DeprecationSink) {
|
||||
sink.emit_with_hints(
|
||||
"checking whether a type is contained in a string is deprecated",
|
||||
&["this compatibility behavior only exists because `type` used to return a string"],
|
||||
);
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn warn_type_in_dict(sink: &mut dyn DeprecationSink) {
|
||||
sink.emit_with_hints(
|
||||
"checking whether a type is contained in a dictionary is deprecated",
|
||||
&["this compatibility behavior only exists because `type` used to return a string"],
|
||||
);
|
||||
}
|
||||
|
||||
fn is_compat_type_name(s: &str) -> bool {
|
||||
matches!(
|
||||
s,
|
||||
"boolean"
|
||||
| "alignment"
|
||||
| "angle"
|
||||
| "arguments"
|
||||
| "array"
|
||||
| "bytes"
|
||||
| "color"
|
||||
| "content"
|
||||
| "counter"
|
||||
| "datetime"
|
||||
| "decimal"
|
||||
| "dictionary"
|
||||
| "direction"
|
||||
| "duration"
|
||||
| "float"
|
||||
| "fraction"
|
||||
| "function"
|
||||
| "gradient"
|
||||
| "integer"
|
||||
| "label"
|
||||
| "length"
|
||||
| "location"
|
||||
| "module"
|
||||
| "pattern"
|
||||
| "ratio"
|
||||
| "regex"
|
||||
| "relative length"
|
||||
| "selector"
|
||||
| "state"
|
||||
| "string"
|
||||
| "stroke"
|
||||
| "symbol"
|
||||
| "tiling"
|
||||
| "type"
|
||||
| "version"
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -300,7 +300,7 @@ impl Binding {
|
|||
/// As the `sink`
|
||||
/// - pass `()` to ignore the message.
|
||||
/// - pass `(&mut engine, span)` to emit a warning into the engine.
|
||||
pub fn read_checked(&self, sink: impl DeprecationSink) -> &Value {
|
||||
pub fn read_checked(&self, mut sink: impl DeprecationSink) -> &Value {
|
||||
if let Some(message) = self.deprecation {
|
||||
sink.emit(message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,16 @@ use crate::foundations::{
|
|||
/// #type(int) \
|
||||
/// #type(type)
|
||||
/// ```
|
||||
///
|
||||
/// # Compatibility
|
||||
/// In Typst 0.7 and lower, the `type` function returned a string instead of a
|
||||
/// type. Compatibility with the old way will remain until Typst 0.14 to give
|
||||
/// package authors time to upgrade.
|
||||
///
|
||||
/// - Checks like `{int == "integer"}` evaluate to `{true}`
|
||||
/// - Adding/joining a type and string will yield a string
|
||||
/// - The `{in}` operator on a type and a dictionary will evaluate to `{true}`
|
||||
/// if the dictionary has a string key matching the type's name
|
||||
#[ty(scope, cast)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Type(Static<NativeTypeData>);
|
||||
|
|
@ -106,6 +116,14 @@ impl Type {
|
|||
}
|
||||
}
|
||||
|
||||
// Type compatibility.
|
||||
impl Type {
|
||||
/// The type's backward-compatible name.
|
||||
pub fn compat_name(&self) -> &str {
|
||||
self.long_name()
|
||||
}
|
||||
}
|
||||
|
||||
#[scope]
|
||||
impl Type {
|
||||
/// Determines a value's type.
|
||||
|
|
|
|||
|
|
@ -292,7 +292,8 @@ impl Repr for Value {
|
|||
|
||||
impl PartialEq for Value {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
ops::equal(self, other)
|
||||
// No way to emit deprecation warnings here :(
|
||||
ops::equal(self, other, &mut ())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use typst_utils::NonZeroExt;
|
|||
|
||||
use crate::diag::{bail, StrResult};
|
||||
use crate::foundations::{Content, Label, Repr, Selector};
|
||||
use crate::html::{HtmlElement, HtmlNode};
|
||||
use crate::html::HtmlNode;
|
||||
use crate::introspection::{Location, Tag};
|
||||
use crate::layout::{Frame, FrameItem, Page, Point, Position, Transform};
|
||||
use crate::model::Numbering;
|
||||
|
|
@ -55,8 +55,8 @@ impl Introspector {
|
|||
|
||||
/// Creates an introspector for HTML.
|
||||
#[typst_macros::time(name = "introspect html")]
|
||||
pub fn html(root: &HtmlElement) -> Self {
|
||||
IntrospectorBuilder::new().build_html(root)
|
||||
pub fn html(output: &[HtmlNode]) -> Self {
|
||||
IntrospectorBuilder::new().build_html(output)
|
||||
}
|
||||
|
||||
/// Iterates over all locatable elements.
|
||||
|
|
@ -392,9 +392,9 @@ impl IntrospectorBuilder {
|
|||
}
|
||||
|
||||
/// Build an introspector for an HTML document.
|
||||
fn build_html(mut self, root: &HtmlElement) -> Introspector {
|
||||
fn build_html(mut self, output: &[HtmlNode]) -> Introspector {
|
||||
let mut elems = Vec::new();
|
||||
self.discover_in_html(&mut elems, root);
|
||||
self.discover_in_html(&mut elems, output);
|
||||
self.finalize(elems)
|
||||
}
|
||||
|
||||
|
|
@ -434,16 +434,16 @@ impl IntrospectorBuilder {
|
|||
}
|
||||
|
||||
/// Processes the tags in the HTML element.
|
||||
fn discover_in_html(&mut self, sink: &mut Vec<Pair>, elem: &HtmlElement) {
|
||||
for child in &elem.children {
|
||||
match child {
|
||||
fn discover_in_html(&mut self, sink: &mut Vec<Pair>, nodes: &[HtmlNode]) {
|
||||
for node in nodes {
|
||||
match node {
|
||||
HtmlNode::Tag(tag) => self.discover_in_tag(
|
||||
sink,
|
||||
tag,
|
||||
Position { page: NonZeroUsize::ONE, point: Point::zero() },
|
||||
),
|
||||
HtmlNode::Text(_, _) => {}
|
||||
HtmlNode::Element(elem) => self.discover_in_html(sink, elem),
|
||||
HtmlNode::Element(elem) => self.discover_in_html(sink, &elem.children),
|
||||
HtmlNode::Frame(frame) => self.discover_in_frame(
|
||||
sink,
|
||||
frame,
|
||||
|
|
|
|||
|
|
@ -1526,11 +1526,7 @@ impl<'a> CellGrid<'a> {
|
|||
self.entry(x, y).map(|entry| match entry {
|
||||
Entry::Cell(_) => Axes::new(x, y),
|
||||
Entry::Merged { parent } => {
|
||||
let c = if self.has_gutter {
|
||||
1 + self.cols.len() / 2
|
||||
} else {
|
||||
self.cols.len()
|
||||
};
|
||||
let c = self.non_gutter_column_count();
|
||||
let factor = if self.has_gutter { 2 } else { 1 };
|
||||
Axes::new(factor * (*parent % c), factor * (*parent / c))
|
||||
}
|
||||
|
|
@ -1602,6 +1598,21 @@ impl<'a> CellGrid<'a> {
|
|||
cell.rowspan.get()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn non_gutter_column_count(&self) -> usize {
|
||||
if self.has_gutter {
|
||||
// Calculation: With gutters, we have
|
||||
// 'cols = 2 * (non-gutter cols) - 1', since there is a gutter
|
||||
// column between each regular column. Therefore,
|
||||
// 'floor(cols / 2)' will be equal to
|
||||
// 'floor(non-gutter cols - 1/2) = non-gutter-cols - 1',
|
||||
// so 'non-gutter cols = 1 + floor(cols / 2)'.
|
||||
1 + self.cols.len() / 2
|
||||
} else {
|
||||
self.cols.len()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a cell's requested x and y, the vector with the resolved cell
|
||||
|
|
|
|||
|
|
@ -282,7 +282,7 @@ fn show_cell_html(tag: HtmlTag, cell: &Cell, styles: StyleChain) -> Content {
|
|||
|
||||
fn show_cellgrid_html(grid: CellGrid, styles: StyleChain) -> Content {
|
||||
let elem = |tag, body| HtmlElem::new(tag).with_body(Some(body)).pack();
|
||||
let mut rows: Vec<_> = grid.entries.chunks(grid.cols.len()).collect();
|
||||
let mut rows: Vec<_> = grid.entries.chunks(grid.non_gutter_column_count()).collect();
|
||||
|
||||
let tr = |tag, row: &[Entry]| {
|
||||
let row = row
|
||||
|
|
|
|||
|
|
@ -1380,24 +1380,7 @@ pub fn is_default_ignorable(c: char) -> bool {
|
|||
fn check_font_list(engine: &mut Engine, list: &Spanned<FontList>) {
|
||||
let book = engine.world.book();
|
||||
for family in &list.v {
|
||||
let found = book.contains_family(family.as_str());
|
||||
if family.as_str() == "linux libertine" {
|
||||
let mut warning = warning!(
|
||||
list.span,
|
||||
"Typst's default font has changed from Linux Libertine to its successor Libertinus Serif";
|
||||
hint: "please set the font to `\"Libertinus Serif\"` instead"
|
||||
);
|
||||
|
||||
if found {
|
||||
warning.hint(
|
||||
"Linux Libertine is available on your system - \
|
||||
you can ignore this warning if you are sure you want to use it",
|
||||
);
|
||||
warning.hint("this warning will be removed in Typst 0.13");
|
||||
}
|
||||
|
||||
engine.sink.warn(warning);
|
||||
} else if !found {
|
||||
if !book.contains_family(family.as_str()) {
|
||||
engine.sink.warn(warning!(
|
||||
list.span,
|
||||
"unknown font family: {}",
|
||||
|
|
|
|||
|
|
@ -446,10 +446,14 @@ impl Show for Packed<RawElem> {
|
|||
let mut realized = Content::sequence(seq);
|
||||
|
||||
if TargetElem::target_in(styles).is_html() {
|
||||
return Ok(HtmlElem::new(tag::pre)
|
||||
.with_body(Some(realized))
|
||||
.pack()
|
||||
.spanned(self.span()));
|
||||
return Ok(HtmlElem::new(if self.block(styles) {
|
||||
tag::pre
|
||||
} else {
|
||||
tag::code
|
||||
})
|
||||
.with_body(Some(realized))
|
||||
.pack()
|
||||
.spanned(self.span()));
|
||||
}
|
||||
|
||||
if self.block(styles) {
|
||||
|
|
|
|||
|
|
@ -398,8 +398,7 @@ impl ImageFormat {
|
|||
return Some(Self::Raster(RasterFormat::Exchange(format)));
|
||||
}
|
||||
|
||||
// SVG or compressed SVG.
|
||||
if data.starts_with(b"<svg") || data.starts_with(&[0x1f, 0x8b]) {
|
||||
if is_svg(data) {
|
||||
return Some(Self::Vector(VectorFormat::Svg));
|
||||
}
|
||||
|
||||
|
|
@ -407,6 +406,21 @@ impl ImageFormat {
|
|||
}
|
||||
}
|
||||
|
||||
/// Checks whether the data looks like an SVG or a compressed SVG.
|
||||
fn is_svg(data: &[u8]) -> bool {
|
||||
// Check for the gzip magic bytes. This check is perhaps a bit too
|
||||
// permissive as other formats than SVGZ could use gzip.
|
||||
if data.starts_with(&[0x1f, 0x8b]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the first 2048 bytes contain the SVG namespace declaration, we assume
|
||||
// that it's an SVG. Note that, if the SVG does not contain a namespace
|
||||
// declaration, usvg will reject it.
|
||||
let head = &data[..data.len().min(2048)];
|
||||
memchr::memmem::find(head, b"http://www.w3.org/2000/svg").is_some()
|
||||
}
|
||||
|
||||
/// A vector graphics format.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
|
||||
pub enum VectorFormat {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
---
|
||||
title: 0.13.0
|
||||
description: Changes slated to appear in Typst 0.13.0
|
||||
description: Changes in Typst 0.13.0
|
||||
---
|
||||
|
||||
# Version 0.13.0, Release Candidate 1 (February 5, 2025) { #v0.13.0-rc1 }
|
||||
# Version 0.13.0 (February 19, 2025)
|
||||
|
||||
## Highlights
|
||||
- There is now a distinction between [proper paragraphs]($par) and just
|
||||
|
|
@ -99,8 +99,9 @@ description: Changes slated to appear in Typst 0.13.0
|
|||
- Fixed interaction of clipping and outset on [`box`] and [`block`]
|
||||
- Fixed panic with [`path`] of infinite length
|
||||
- Fixed non-solid (e.g. tiling) text fills in clipped blocks
|
||||
- Auto-detection of image formats from a raw buffer now has basic support for
|
||||
SVGs
|
||||
- Fixed a crash for images with a DPI value of zero
|
||||
- Fixed floating-point error in [`gradient.repeat`]
|
||||
- Auto-detection of image formats from a raw buffer now has support for SVGs
|
||||
|
||||
## Scripting
|
||||
- Functions that accept [file paths]($syntax/#paths) now also accept raw
|
||||
|
|
@ -187,12 +188,12 @@ description: Changes slated to appear in Typst 0.13.0
|
|||
- [CJK-Latin-spacing]($text.cjk-latin-spacing) does not affect [raw] text
|
||||
anymore
|
||||
- Fixed wrong language codes being used for Greek and Ukrainian
|
||||
- Fixed default quotes for Croatian
|
||||
- Fixed default quotes for Croatian and Bulgarian
|
||||
- Fixed crash in RTL text handling
|
||||
- Added support for [`raw`] syntax highlighting for a few new languages: CFML,
|
||||
NSIS, and WGSL
|
||||
- New font metadata exception for New Computer Modern Sans Math
|
||||
- Updated bundled New Computer Modern fonts to version 7.0
|
||||
- Updated bundled New Computer Modern fonts to version 7.0.1
|
||||
|
||||
## Layout
|
||||
- Fixed various bugs with footnotes
|
||||
|
|
@ -271,6 +272,9 @@ feature flag.
|
|||
- Added a live reloading HTTP server to `typst watch` when targeting HTML
|
||||
- Fixed self-update not being aware about certain target architectures
|
||||
- Fixed crash when piping `typst fonts` output to another command
|
||||
- Fixed handling of relative paths in `--make-deps` output
|
||||
- Fixed handling of multipage SVG and PNG export in `--make-deps` output
|
||||
- Colons in filenames are now correctly escaped in `--make-deps` output
|
||||
|
||||
## Symbols
|
||||
- New
|
||||
|
|
@ -313,6 +317,9 @@ feature flag.
|
|||
functions directly accepting both paths and bytes
|
||||
- The `sect` and its variants in favor of `inter`, and `integral.sect` in favor
|
||||
of `integral.inter`
|
||||
- The compatibility behavior of type/str comparisons (e.g. `{int == "integer"}`)
|
||||
which was temporarily introduced in Typst 0.8 now emits warnings. It will be
|
||||
removed in Typst 0.14.
|
||||
|
||||
## Removals
|
||||
- Removed `style` function and `styles` argument of [`measure`], use a [context]
|
||||
|
|
@ -324,9 +331,6 @@ feature flag.
|
|||
- Removed compatibility behavior where [`counter.display`] worked without
|
||||
[context] **(Breaking change)**
|
||||
- Removed compatibility behavior of [`locate`] **(Breaking change)**
|
||||
- Removed compatibility behavior of type/str comparisons
|
||||
(e.g. `{int == "integer"}`) which was temporarily introduced in Typst 0.8
|
||||
**(Breaking change)**
|
||||
|
||||
## Development
|
||||
- The `typst::compile` function is now generic and can return either a
|
||||
|
|
@ -337,4 +341,4 @@ feature flag.
|
|||
- Fixed linux/arm64 Docker image
|
||||
|
||||
## Contributors
|
||||
<contributors from="v0.12.0" to="v0.13.0-rc1" />
|
||||
<contributors from="v0.12.0" to="v0.13.0" />
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ forward. This section documents all changes to Typst since its initial public
|
|||
release.
|
||||
|
||||
## Versions
|
||||
- [Typst 0.13.0 (Release Candidate 1)]($changelog/0.13.0)
|
||||
- [Typst 0.13.0]($changelog/0.13.0)
|
||||
- [Typst 0.12.0]($changelog/0.12.0)
|
||||
- [Typst 0.11.1]($changelog/0.11.1)
|
||||
- [Typst 0.11.0]($changelog/0.11.0)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td>a</td>
|
||||
<td>b</td>
|
||||
<td>c</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>d</td>
|
||||
<td>e</td>
|
||||
<td>f</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>g</td>
|
||||
<td>h</td>
|
||||
<td>i</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td>a</td>
|
||||
<td>b</td>
|
||||
<td>c</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>d</td>
|
||||
<td>e</td>
|
||||
<td>f</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>g</td>
|
||||
<td>h</td>
|
||||
<td>i</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<!DOCTYPE html>
|
||||
<html></html>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<!DOCTYPE html>
|
||||
<html>Hi</html>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td>a</td>
|
||||
<td>b</td>
|
||||
<td>c</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>d</td>
|
||||
<td>e</td>
|
||||
<td>f</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>g</td>
|
||||
<td>h</td>
|
||||
<td>i</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 129 B |
|
|
@ -251,6 +251,6 @@ fn lines(
|
|||
(1..=count)
|
||||
.map(|n| numbering.apply(engine, context, &[n]))
|
||||
.collect::<SourceResult<Array>>()?
|
||||
.join(Some('\n'.into_value()), None)
|
||||
.join(engine, span, Some('\n'.into_value()), None)
|
||||
.at(span)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,62 @@
|
|||
#test(type(1), int)
|
||||
#test(type(ltr), direction)
|
||||
#test(type(10 / 3), float)
|
||||
#test(type(10) == int, true)
|
||||
#test(type(10) != int, false)
|
||||
|
||||
--- type-string-compatibility-add ---
|
||||
// Warning: 7-23 adding strings and types is deprecated
|
||||
// Hint: 7-23 convert the type to a string with `str` first
|
||||
#test("is " + type(10), "is integer")
|
||||
// Warning: 7-23 adding strings and types is deprecated
|
||||
// Hint: 7-23 convert the type to a string with `str` first
|
||||
#test(type(10) + " is", "integer is")
|
||||
|
||||
--- type-string-compatibility-join ---
|
||||
// Warning: 16-24 joining strings and types is deprecated
|
||||
// Hint: 16-24 convert the type to a string with `str` first
|
||||
#test({ "is "; type(10) }, "is integer")
|
||||
// Warning: 19-24 joining strings and types is deprecated
|
||||
// Hint: 19-24 convert the type to a string with `str` first
|
||||
#test({ type(10); " is" }, "integer is")
|
||||
|
||||
--- type-string-compatibility-equal ---
|
||||
// Warning: 7-28 comparing strings with types is deprecated
|
||||
// Hint: 7-28 compare with the literal type instead
|
||||
// Hint: 7-28 this comparison will always return `false` in future Typst releases
|
||||
#test(type(10) == "integer", true)
|
||||
// Warning: 7-26 comparing strings with types is deprecated
|
||||
// Hint: 7-26 compare with the literal type instead
|
||||
// Hint: 7-26 this comparison will always return `false` in future Typst releases
|
||||
#test(type(10) != "float", true)
|
||||
// This is not a warning.
|
||||
#test(type(10) in ("any", str, int), true)
|
||||
|
||||
--- type-string-compatibility-in-array ---
|
||||
// Warning: 7-35 comparing strings with types is deprecated
|
||||
// Hint: 7-35 compare with the literal type instead
|
||||
// Hint: 7-35 this comparison will always return `false` in future Typst releases
|
||||
#test(int in ("integer", "string"), true)
|
||||
// Warning: 7-37 comparing strings with types is deprecated
|
||||
// Hint: 7-37 compare with the literal type instead
|
||||
// Hint: 7-37 this comparison will always return `false` in future Typst releases
|
||||
#test(float in ("integer", "string"), false)
|
||||
|
||||
--- type-string-compatibility-in-str ---
|
||||
// Warning: 7-35 checking whether a type is contained in a string is deprecated
|
||||
// Hint: 7-35 this compatibility behavior only exists because `type` used to return a string
|
||||
#test(int in "integers or strings", true)
|
||||
// Warning: 7-35 checking whether a type is contained in a string is deprecated
|
||||
// Hint: 7-35 this compatibility behavior only exists because `type` used to return a string
|
||||
#test(str in "integers or strings", true)
|
||||
// Warning: 7-37 checking whether a type is contained in a string is deprecated
|
||||
// Hint: 7-37 this compatibility behavior only exists because `type` used to return a string
|
||||
#test(float in "integers or strings", false)
|
||||
|
||||
--- type-string-compatibility-in-dict ---
|
||||
// Warning: 7-37 checking whether a type is contained in a dictionary is deprecated
|
||||
// Hint: 7-37 this compatibility behavior only exists because `type` used to return a string
|
||||
#test(int in (integer: 1, string: 2), true)
|
||||
|
||||
--- issue-3110-type-constructor ---
|
||||
// Let the error message report the type name.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
--- html-elem-alone-context html ---
|
||||
#context html.elem("html")
|
||||
|
||||
--- html-elem-not-alone html ---
|
||||
// Error: 2-19 `<html>` element must be the only element in the document
|
||||
#html.elem("html")
|
||||
Text
|
||||
|
||||
--- html-elem-metadata html ---
|
||||
#html.elem("html", context {
|
||||
let val = query(<l>).first().value
|
||||
test(val, "Hi")
|
||||
val
|
||||
})
|
||||
#metadata("Hi") <l>
|
||||
|
|
@ -30,3 +30,30 @@
|
|||
[row],
|
||||
),
|
||||
)
|
||||
|
||||
--- col-gutter-table html ---
|
||||
#table(
|
||||
columns: 3,
|
||||
column-gutter: 3pt,
|
||||
[a], [b], [c],
|
||||
[d], [e], [f],
|
||||
[g], [h], [i]
|
||||
)
|
||||
|
||||
--- row-gutter-table html ---
|
||||
#table(
|
||||
columns: 3,
|
||||
row-gutter: 3pt,
|
||||
[a], [b], [c],
|
||||
[d], [e], [f],
|
||||
[g], [h], [i]
|
||||
)
|
||||
|
||||
--- col-row-gutter-table html ---
|
||||
#table(
|
||||
columns: 3,
|
||||
gutter: 3pt,
|
||||
[a], [b], [c],
|
||||
[d], [e], [f],
|
||||
[g], [h], [i]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -77,11 +77,6 @@ I
|
|||
#let var = text(font: ("list-of", "nonexistent-fonts"))[don't]
|
||||
#var
|
||||
|
||||
--- text-font-linux-libertine ---
|
||||
// Warning: 17-34 Typst's default font has changed from Linux Libertine to its successor Libertinus Serif
|
||||
// Hint: 17-34 please set the font to `"Libertinus Serif"` instead
|
||||
#set text(font: "Linux Libertine")
|
||||
|
||||
--- issue-5499-text-fill-in-clip-block ---
|
||||
|
||||
#let t = tiling(
|
||||
|
|
|
|||
|
|
@ -65,6 +65,17 @@ A #box(image("/assets/images/tiger.jpg", height: 1cm, width: 80%)) B
|
|||
caption: [Bilingual text]
|
||||
)
|
||||
|
||||
--- image-svg-auto-detection ---
|
||||
#image(bytes(
|
||||
```
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- An SVG -->
|
||||
<svg width="200" height="150" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="red" stroke="black" x="25" y="25" width="150" height="100"/>
|
||||
</svg>
|
||||
```.text
|
||||
))
|
||||
|
||||
--- image-pixmap-rgb8 ---
|
||||
#image(
|
||||
bytes((
|
||||
|
|
@ -152,8 +163,8 @@ A #box(image("/assets/images/tiger.jpg", height: 1cm, width: 80%)) B
|
|||
#image("path/does/not/exist")
|
||||
|
||||
--- image-bad-format ---
|
||||
// Error: 2-22 unknown image format
|
||||
#image("./image.typ")
|
||||
// Error: 2-37 unknown image format
|
||||
#image("/assets/plugins/hello.wasm")
|
||||
|
||||
--- image-bad-svg ---
|
||||
// Error: 2-33 failed to parse SVG (found closing tag 'g' instead of 'style' in line 4)
|
||||
|
|
|
|||
Loading…
Reference in New Issue