mirror of
https://github.com/typst/typst
synced 2025-08-03 01:37:54 +08:00
Merge branch 'main' into pdf-accessibility
This commit is contained in:
commit
7c8a4e4243
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -3286,7 +3286,6 @@ dependencies = [
|
|||||||
name = "typst-syntax"
|
name = "typst-syntax"
|
||||||
version = "0.13.1"
|
version = "0.13.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"comemo",
|
|
||||||
"ecow",
|
"ecow",
|
||||||
"serde",
|
"serde",
|
||||||
"toml",
|
"toml",
|
||||||
|
@ -160,6 +160,7 @@ strip = true
|
|||||||
[workspace.lints.clippy]
|
[workspace.lints.clippy]
|
||||||
blocks_in_conditions = "allow"
|
blocks_in_conditions = "allow"
|
||||||
comparison_chain = "allow"
|
comparison_chain = "allow"
|
||||||
|
iter_over_hash_type = "warn"
|
||||||
manual_range_contains = "allow"
|
manual_range_contains = "allow"
|
||||||
mutable_key_type = "allow"
|
mutable_key_type = "allow"
|
||||||
uninlined_format_args = "warn"
|
uninlined_format_args = "warn"
|
||||||
|
@ -139,6 +139,7 @@ impl Watcher {
|
|||||||
fn update(&mut self, iter: impl IntoIterator<Item = PathBuf>) -> StrResult<()> {
|
fn update(&mut self, iter: impl IntoIterator<Item = PathBuf>) -> StrResult<()> {
|
||||||
// Mark all files as not "seen" so that we may unwatch them if they
|
// Mark all files as not "seen" so that we may unwatch them if they
|
||||||
// aren't in the dependency list.
|
// aren't in the dependency list.
|
||||||
|
#[allow(clippy::iter_over_hash_type, reason = "order does not matter")]
|
||||||
for seen in self.watched.values_mut() {
|
for seen in self.watched.values_mut() {
|
||||||
*seen = false;
|
*seen = false;
|
||||||
}
|
}
|
||||||
|
@ -173,6 +173,7 @@ impl SystemWorld {
|
|||||||
|
|
||||||
/// Reset the compilation state in preparation of a new compilation.
|
/// Reset the compilation state in preparation of a new compilation.
|
||||||
pub fn reset(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
|
#[allow(clippy::iter_over_hash_type, reason = "order does not matter")]
|
||||||
for slot in self.slots.get_mut().values_mut() {
|
for slot in self.slots.get_mut().values_mut() {
|
||||||
slot.reset();
|
slot.reset();
|
||||||
}
|
}
|
||||||
|
@ -709,9 +709,11 @@ fn complete_params(ctx: &mut CompletionContext) -> bool {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parameters: "func(|)", "func(hi|)", "func(12,|)".
|
// Parameters: "func(|)", "func(hi|)", "func(12, |)", "func(12,|)" [explicit mode only]
|
||||||
if let SyntaxKind::LeftParen | SyntaxKind::Comma = deciding.kind()
|
if let SyntaxKind::LeftParen | SyntaxKind::Comma = deciding.kind()
|
||||||
&& (deciding.kind() != SyntaxKind::Comma || deciding.range().end < ctx.cursor)
|
&& (deciding.kind() != SyntaxKind::Comma
|
||||||
|
|| deciding.range().end < ctx.cursor
|
||||||
|
|| ctx.explicit)
|
||||||
{
|
{
|
||||||
if let Some(next) = deciding.next_leaf() {
|
if let Some(next) = deciding.next_leaf() {
|
||||||
ctx.from = ctx.cursor.min(next.offset());
|
ctx.from = ctx.cursor.min(next.offset());
|
||||||
@ -891,7 +893,10 @@ fn complete_code(ctx: &mut CompletionContext) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// An existing identifier: "{ pa| }".
|
// An existing identifier: "{ pa| }".
|
||||||
if ctx.leaf.kind() == SyntaxKind::Ident {
|
// Ignores named pair keys as they are not variables (as in "(pa|: 23)").
|
||||||
|
if ctx.leaf.kind() == SyntaxKind::Ident
|
||||||
|
&& (ctx.leaf.index() > 0 || ctx.leaf.parent_kind() != Some(SyntaxKind::Named))
|
||||||
|
{
|
||||||
ctx.from = ctx.leaf.offset();
|
ctx.from = ctx.leaf.offset();
|
||||||
code_completions(ctx, false);
|
code_completions(ctx, false);
|
||||||
return true;
|
return true;
|
||||||
@ -904,11 +909,19 @@ fn complete_code(ctx: &mut CompletionContext) -> bool {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Anywhere: "{ | }".
|
// Anywhere: "{ | }", "(|)", "(1,|)", "(a:|)".
|
||||||
// But not within or after an expression.
|
// But not within or after an expression, and also not part of a dictionary
|
||||||
|
// key (as in "(pa: |,)")
|
||||||
if ctx.explicit
|
if ctx.explicit
|
||||||
|
&& ctx.leaf.parent_kind() != Some(SyntaxKind::Dict)
|
||||||
&& (ctx.leaf.kind().is_trivia()
|
&& (ctx.leaf.kind().is_trivia()
|
||||||
|| matches!(ctx.leaf.kind(), SyntaxKind::LeftParen | SyntaxKind::LeftBrace))
|
|| matches!(
|
||||||
|
ctx.leaf.kind(),
|
||||||
|
SyntaxKind::LeftParen
|
||||||
|
| SyntaxKind::LeftBrace
|
||||||
|
| SyntaxKind::Comma
|
||||||
|
| SyntaxKind::Colon
|
||||||
|
))
|
||||||
{
|
{
|
||||||
ctx.from = ctx.cursor;
|
ctx.from = ctx.cursor;
|
||||||
code_completions(ctx, false);
|
code_completions(ctx, false);
|
||||||
@ -1560,6 +1573,7 @@ mod tests {
|
|||||||
trait ResponseExt {
|
trait ResponseExt {
|
||||||
fn completions(&self) -> &[Completion];
|
fn completions(&self) -> &[Completion];
|
||||||
fn labels(&self) -> BTreeSet<&str>;
|
fn labels(&self) -> BTreeSet<&str>;
|
||||||
|
fn must_be_empty(&self) -> &Self;
|
||||||
fn must_include<'a>(&self, includes: impl IntoIterator<Item = &'a str>) -> &Self;
|
fn must_include<'a>(&self, includes: impl IntoIterator<Item = &'a str>) -> &Self;
|
||||||
fn must_exclude<'a>(&self, excludes: impl IntoIterator<Item = &'a str>) -> &Self;
|
fn must_exclude<'a>(&self, excludes: impl IntoIterator<Item = &'a str>) -> &Self;
|
||||||
fn must_apply<'a>(&self, label: &str, apply: impl Into<Option<&'a str>>)
|
fn must_apply<'a>(&self, label: &str, apply: impl Into<Option<&'a str>>)
|
||||||
@ -1578,6 +1592,16 @@ mod tests {
|
|||||||
self.completions().iter().map(|c| c.label.as_str()).collect()
|
self.completions().iter().map(|c| c.label.as_str()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn must_be_empty(&self) -> &Self {
|
||||||
|
let labels = self.labels();
|
||||||
|
assert!(
|
||||||
|
labels.is_empty(),
|
||||||
|
"expected no suggestions (got {labels:?} instead)"
|
||||||
|
);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn must_include<'a>(&self, includes: impl IntoIterator<Item = &'a str>) -> &Self {
|
fn must_include<'a>(&self, includes: impl IntoIterator<Item = &'a str>) -> &Self {
|
||||||
let labels = self.labels();
|
let labels = self.labels();
|
||||||
@ -1622,7 +1646,15 @@ mod tests {
|
|||||||
let world = world.acquire();
|
let world = world.acquire();
|
||||||
let world = world.borrow();
|
let world = world.borrow();
|
||||||
let doc = typst::compile(world).output.ok();
|
let doc = typst::compile(world).output.ok();
|
||||||
test_with_doc(world, pos, doc.as_ref())
|
test_with_doc(world, pos, doc.as_ref(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn test_implicit(world: impl WorldLike, pos: impl FilePos) -> Response {
|
||||||
|
let world = world.acquire();
|
||||||
|
let world = world.borrow();
|
||||||
|
let doc = typst::compile(world).output.ok();
|
||||||
|
test_with_doc(world, pos, doc.as_ref(), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
@ -1635,7 +1667,7 @@ mod tests {
|
|||||||
let doc = typst::compile(&world).output.ok();
|
let doc = typst::compile(&world).output.ok();
|
||||||
let end = world.main.text().len();
|
let end = world.main.text().len();
|
||||||
world.main.edit(end..end, addition);
|
world.main.edit(end..end, addition);
|
||||||
test_with_doc(&world, pos, doc.as_ref())
|
test_with_doc(&world, pos, doc.as_ref(), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
@ -1643,11 +1675,12 @@ mod tests {
|
|||||||
world: impl WorldLike,
|
world: impl WorldLike,
|
||||||
pos: impl FilePos,
|
pos: impl FilePos,
|
||||||
doc: Option<&PagedDocument>,
|
doc: Option<&PagedDocument>,
|
||||||
|
explicit: bool,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
let world = world.acquire();
|
let world = world.acquire();
|
||||||
let world = world.borrow();
|
let world = world.borrow();
|
||||||
let (source, cursor) = pos.resolve(world);
|
let (source, cursor) = pos.resolve(world);
|
||||||
autocomplete(world, doc, &source, cursor, true)
|
autocomplete(world, doc, &source, cursor, explicit)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1698,7 +1731,7 @@ mod tests {
|
|||||||
let end = world.main.text().len();
|
let end = world.main.text().len();
|
||||||
world.main.edit(end..end, " #cite()");
|
world.main.edit(end..end, " #cite()");
|
||||||
|
|
||||||
test_with_doc(&world, -2, doc.as_ref())
|
test_with_doc(&world, -2, doc.as_ref(), true)
|
||||||
.must_include(["netwok", "glacier-melt", "supplement"])
|
.must_include(["netwok", "glacier-melt", "supplement"])
|
||||||
.must_exclude(["bib"]);
|
.must_exclude(["bib"]);
|
||||||
}
|
}
|
||||||
@ -1853,26 +1886,105 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_autocomplete_fonts() {
|
fn test_autocomplete_fonts() {
|
||||||
test("#text(font:)", -2)
|
test("#text(font:)", -2)
|
||||||
.must_include(["\"Libertinus Serif\"", "\"New Computer Modern Math\""]);
|
.must_include([q!("Libertinus Serif"), q!("New Computer Modern Math")]);
|
||||||
|
|
||||||
test("#show link: set text(font: )", -2)
|
test("#show link: set text(font: )", -2)
|
||||||
.must_include(["\"Libertinus Serif\"", "\"New Computer Modern Math\""]);
|
.must_include([q!("Libertinus Serif"), q!("New Computer Modern Math")]);
|
||||||
|
|
||||||
test("#show math.equation: set text(font: )", -2)
|
test("#show math.equation: set text(font: )", -2)
|
||||||
.must_include(["\"New Computer Modern Math\""])
|
.must_include([q!("New Computer Modern Math")])
|
||||||
.must_exclude(["\"Libertinus Serif\""]);
|
.must_exclude([q!("Libertinus Serif")]);
|
||||||
|
|
||||||
test("#show math.equation: it => { set text(font: )\nit }", -7)
|
test("#show math.equation: it => { set text(font: )\nit }", -7)
|
||||||
.must_include(["\"New Computer Modern Math\""])
|
.must_include([q!("New Computer Modern Math")])
|
||||||
.must_exclude(["\"Libertinus Serif\""]);
|
.must_exclude([q!("Libertinus Serif")]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_autocomplete_typed_html() {
|
fn test_autocomplete_typed_html() {
|
||||||
test("#html.div(translate: )", -2)
|
test("#html.div(translate: )", -2)
|
||||||
.must_include(["true", "false"])
|
.must_include(["true", "false"])
|
||||||
.must_exclude(["\"yes\"", "\"no\""]);
|
.must_exclude([q!("yes"), q!("no")]);
|
||||||
test("#html.input(value: )", -2).must_include(["float", "string", "red", "blue"]);
|
test("#html.input(value: )", -2).must_include(["float", "string", "red", "blue"]);
|
||||||
test("#html.div(role: )", -2).must_include(["\"alertdialog\""]);
|
test("#html.div(role: )", -2).must_include([q!("alertdialog")]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_autocomplete_in_function_params_after_comma_and_colon() {
|
||||||
|
let document = "#text(size: 12pt, [])";
|
||||||
|
|
||||||
|
// After colon
|
||||||
|
test(document, 11).must_include(["length"]);
|
||||||
|
test_implicit(document, 11).must_include(["length"]);
|
||||||
|
|
||||||
|
test(document, 12).must_include(["length"]);
|
||||||
|
test_implicit(document, 12).must_include(["length"]);
|
||||||
|
|
||||||
|
// After comma
|
||||||
|
test(document, 17).must_include(["font"]);
|
||||||
|
test_implicit(document, 17).must_be_empty();
|
||||||
|
|
||||||
|
test(document, 18).must_include(["font"]);
|
||||||
|
test_implicit(document, 18).must_include(["font"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_autocomplete_in_list_literal() {
|
||||||
|
let document = "#let val = 0\n#(1, \"one\")";
|
||||||
|
|
||||||
|
// After opening paren
|
||||||
|
test(document, 15).must_include(["color", "val"]);
|
||||||
|
test_implicit(document, 15).must_be_empty();
|
||||||
|
|
||||||
|
// After first element
|
||||||
|
test(document, 16).must_be_empty();
|
||||||
|
test_implicit(document, 16).must_be_empty();
|
||||||
|
|
||||||
|
// After comma
|
||||||
|
test(document, 17).must_include(["color", "val"]);
|
||||||
|
test_implicit(document, 17).must_be_empty();
|
||||||
|
|
||||||
|
test(document, 18).must_include(["color", "val"]);
|
||||||
|
test_implicit(document, 18).must_be_empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_autocomplete_in_dict_literal() {
|
||||||
|
let document = "#let first = 0\n#(first: 1, second: one)";
|
||||||
|
|
||||||
|
// After opening paren
|
||||||
|
test(document, 17).must_be_empty();
|
||||||
|
test_implicit(document, 17).must_be_empty();
|
||||||
|
|
||||||
|
// After first key
|
||||||
|
test(document, 22).must_be_empty();
|
||||||
|
test_implicit(document, 22).must_be_empty();
|
||||||
|
|
||||||
|
// After colon
|
||||||
|
test(document, 23).must_include(["align", "first"]);
|
||||||
|
test_implicit(document, 23).must_be_empty();
|
||||||
|
|
||||||
|
test(document, 24).must_include(["align", "first"]);
|
||||||
|
test_implicit(document, 24).must_be_empty();
|
||||||
|
|
||||||
|
// After first value
|
||||||
|
test(document, 25).must_be_empty();
|
||||||
|
test_implicit(document, 25).must_be_empty();
|
||||||
|
|
||||||
|
// After comma
|
||||||
|
test(document, 26).must_be_empty();
|
||||||
|
test_implicit(document, 26).must_be_empty();
|
||||||
|
|
||||||
|
test(document, 27).must_be_empty();
|
||||||
|
test_implicit(document, 27).must_be_empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_autocomplete_in_destructuring() {
|
||||||
|
let document = "#let value = 20\n#let (va: value) = (va: 10)";
|
||||||
|
|
||||||
|
// At destructuring rename pattern source
|
||||||
|
test(document, 24).must_be_empty();
|
||||||
|
test_implicit(document, 24).must_be_empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,18 +234,23 @@ impl From<SyntaxError> for SourceDiagnostic {
|
|||||||
|
|
||||||
/// Destination for a deprecation message when accessing a deprecated value.
|
/// Destination for a deprecation message when accessing a deprecated value.
|
||||||
pub trait DeprecationSink {
|
pub trait DeprecationSink {
|
||||||
/// Emits the given deprecation message into this sink.
|
/// Emits the given deprecation message into this sink alongside a version
|
||||||
fn emit(self, message: &str);
|
/// in which the deprecated item is planned to be removed.
|
||||||
|
fn emit(self, message: &str, until: Option<&str>);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeprecationSink for () {
|
impl DeprecationSink for () {
|
||||||
fn emit(self, _: &str) {}
|
fn emit(self, _: &str, _: Option<&str>) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeprecationSink for (&mut Engine<'_>, Span) {
|
impl DeprecationSink for (&mut Engine<'_>, Span) {
|
||||||
/// Emits the deprecation message as a warning.
|
/// Emits the deprecation message as a warning.
|
||||||
fn emit(self, message: &str) {
|
fn emit(self, message: &str, version: Option<&str>) {
|
||||||
self.0.sink.warn(SourceDiagnostic::warning(self.1, message));
|
self.0
|
||||||
|
.sink
|
||||||
|
.warn(SourceDiagnostic::warning(self.1, message).with_hints(
|
||||||
|
version.map(|v| eco_format!("it will be removed in Typst {}", v)),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,8 +253,8 @@ pub struct Binding {
|
|||||||
span: Span,
|
span: Span,
|
||||||
/// The category of the binding.
|
/// The category of the binding.
|
||||||
category: Option<Category>,
|
category: Option<Category>,
|
||||||
/// A deprecation message for the definition.
|
/// The deprecation information if this item is deprecated.
|
||||||
deprecation: Option<&'static str>,
|
deprecation: Option<Box<Deprecation>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The different kinds of slots.
|
/// The different kinds of slots.
|
||||||
@ -284,8 +284,8 @@ impl Binding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Marks this binding as deprecated, with the given `message`.
|
/// Marks this binding as deprecated, with the given `message`.
|
||||||
pub fn deprecated(&mut self, message: &'static str) -> &mut Self {
|
pub fn deprecated(&mut self, deprecation: Deprecation) -> &mut Self {
|
||||||
self.deprecation = Some(message);
|
self.deprecation = Some(Box::new(deprecation));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,8 +300,8 @@ impl Binding {
|
|||||||
/// - pass `()` to ignore the message.
|
/// - pass `()` to ignore the message.
|
||||||
/// - pass `(&mut engine, span)` to emit a warning into the engine.
|
/// - 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, sink: impl DeprecationSink) -> &Value {
|
||||||
if let Some(message) = self.deprecation {
|
if let Some(info) = &self.deprecation {
|
||||||
sink.emit(message);
|
sink.emit(info.message, info.until);
|
||||||
}
|
}
|
||||||
&self.value
|
&self.value
|
||||||
}
|
}
|
||||||
@ -337,8 +337,8 @@ impl Binding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A deprecation message for the value, if any.
|
/// A deprecation message for the value, if any.
|
||||||
pub fn deprecation(&self) -> Option<&'static str> {
|
pub fn deprecation(&self) -> Option<&Deprecation> {
|
||||||
self.deprecation
|
self.deprecation.as_deref()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The category of the value, if any.
|
/// The category of the value, if any.
|
||||||
@ -356,6 +356,51 @@ pub enum Capturer {
|
|||||||
Context,
|
Context,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Information about a deprecated binding.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub struct Deprecation {
|
||||||
|
/// A deprecation message for the definition.
|
||||||
|
message: &'static str,
|
||||||
|
/// A version in which the deprecated binding is planned to be removed.
|
||||||
|
until: Option<&'static str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deprecation {
|
||||||
|
/// Creates new deprecation info with a default message to display when
|
||||||
|
/// emitting the deprecation warning.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { message: "item is deprecated", until: None }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the message to display when emitting the deprecation warning.
|
||||||
|
pub fn with_message(mut self, message: &'static str) -> Self {
|
||||||
|
self.message = message;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the version in which the binding is planned to be removed.
|
||||||
|
pub fn with_until(mut self, version: &'static str) -> Self {
|
||||||
|
self.until = Some(version);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The message to display when emitting the deprecation warning.
|
||||||
|
pub fn message(&self) -> &'static str {
|
||||||
|
self.message
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The version in which the binding is planned to be removed.
|
||||||
|
pub fn until(&self) -> Option<&'static str> {
|
||||||
|
self.until
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Deprecation {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The error message when trying to mutate a variable from the standard
|
/// The error message when trying to mutate a variable from the standard
|
||||||
/// library.
|
/// library.
|
||||||
#[cold]
|
#[cold]
|
||||||
|
@ -151,7 +151,7 @@ impl Symbol {
|
|||||||
modifiers.best_match_in(list.variants().map(|(m, _, d)| (m, d)))
|
modifiers.best_match_in(list.variants().map(|(m, _, d)| (m, d)))
|
||||||
{
|
{
|
||||||
if let Some(message) = deprecation {
|
if let Some(message) = deprecation {
|
||||||
sink.emit(message)
|
sink.emit(message, None)
|
||||||
}
|
}
|
||||||
return Ok(self);
|
return Ok(self);
|
||||||
}
|
}
|
||||||
|
@ -412,9 +412,11 @@ impl Counter {
|
|||||||
/// - If it is a string, creates a custom counter that is only affected
|
/// - If it is a string, creates a custom counter that is only affected
|
||||||
/// by manual updates,
|
/// by manual updates,
|
||||||
/// - If it is the [`page`] function, counts through pages,
|
/// - If it is the [`page`] function, counts through pages,
|
||||||
/// - If it is a [selector], counts through elements that matches with the
|
/// - If it is a [selector], counts through elements that match the
|
||||||
/// selector. For example,
|
/// selector. For example,
|
||||||
/// - provide an element function: counts elements of that type,
|
/// - provide an element function: counts elements of that type,
|
||||||
|
/// - provide a [`where`]($function.where) selector:
|
||||||
|
/// counts a type of element with specific fields,
|
||||||
/// - provide a [`{<label>}`]($label): counts elements with that label.
|
/// - provide a [`{<label>}`]($label): counts elements with that label.
|
||||||
key: CounterKey,
|
key: CounterKey,
|
||||||
) -> Counter {
|
) -> Counter {
|
||||||
|
@ -6,7 +6,7 @@ use crate::foundations::{Array, Context, LocatableSelector, Value, func};
|
|||||||
|
|
||||||
/// Finds elements in the document.
|
/// Finds elements in the document.
|
||||||
///
|
///
|
||||||
/// The `query` functions lets you search your document for elements of a
|
/// The `query` function lets you search your document for elements of a
|
||||||
/// particular type or with a particular label. To use it, you first need to
|
/// particular type or with a particular label. To use it, you first need to
|
||||||
/// ensure that [context] is available.
|
/// ensure that [context] is available.
|
||||||
///
|
///
|
||||||
|
@ -549,7 +549,7 @@ pub struct GridHLine {
|
|||||||
/// the grid's `row-gutter` option.
|
/// the grid's `row-gutter` option.
|
||||||
#[elem(name = "vline", title = "Grid Vertical Line")]
|
#[elem(name = "vline", title = "Grid Vertical Line")]
|
||||||
pub struct GridVLine {
|
pub struct GridVLine {
|
||||||
/// The column before which the horizontal line is placed (zero-indexed).
|
/// The column before which the vertical line is placed (zero-indexed).
|
||||||
/// If the `position` field is set to `{end}`, the line is placed after the
|
/// If the `position` field is set to `{end}`, the line is placed after the
|
||||||
/// column with the given index instead (see that field's docs for
|
/// column with the given index instead (see that field's docs for
|
||||||
/// details).
|
/// details).
|
||||||
|
@ -340,7 +340,7 @@ pub struct PageElem {
|
|||||||
/// This content will overlay the page's body.
|
/// This content will overlay the page's body.
|
||||||
///
|
///
|
||||||
/// ```example
|
/// ```example
|
||||||
/// #set page(foreground: text(24pt)[🥸])
|
/// #set page(foreground: text(24pt)[🤓])
|
||||||
///
|
///
|
||||||
/// Reviewer 2 has marked our paper
|
/// Reviewer 2 has marked our paper
|
||||||
/// "Weak Reject" because they did
|
/// "Weak Reject" because they did
|
||||||
@ -397,6 +397,15 @@ impl LocalName for PageElem {
|
|||||||
/// == Compound Theory
|
/// == Compound Theory
|
||||||
/// In 1984, the first ...
|
/// In 1984, the first ...
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// Even without manual page breaks, content will be automatically paginated
|
||||||
|
/// based on the configured page size. You can set [the page height]($page.height)
|
||||||
|
/// to `{auto}` to let the page grow dynamically until a manual page break
|
||||||
|
/// occurs.
|
||||||
|
///
|
||||||
|
/// Pagination tries to avoid single lines of text at the top or bottom of a
|
||||||
|
/// page (these are called _widows_ and _orphans_). You can adjust the
|
||||||
|
/// [`text.costs`]($text.costs) parameter to disable this behavior.
|
||||||
#[elem(title = "Page Break")]
|
#[elem(title = "Page Break")]
|
||||||
pub struct PagebreakElem {
|
pub struct PagebreakElem {
|
||||||
/// If `{true}`, the page break is skipped if the current page is already
|
/// If `{true}`, the page break is skipped if the current page is already
|
||||||
|
@ -33,7 +33,10 @@ pub fn cbor(
|
|||||||
impl cbor {
|
impl cbor {
|
||||||
/// Reads structured data from CBOR bytes.
|
/// Reads structured data from CBOR bytes.
|
||||||
#[func(title = "Decode CBOR")]
|
#[func(title = "Decode CBOR")]
|
||||||
#[deprecated = "`cbor.decode` is deprecated, directly pass bytes to `cbor` instead"]
|
#[deprecated(
|
||||||
|
message = "`cbor.decode` is deprecated, directly pass bytes to `cbor` instead",
|
||||||
|
until = "0.15.0"
|
||||||
|
)]
|
||||||
pub fn decode(
|
pub fn decode(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
/// CBOR data.
|
/// CBOR data.
|
||||||
|
@ -95,7 +95,10 @@ pub fn csv(
|
|||||||
impl csv {
|
impl csv {
|
||||||
/// Reads structured data from a CSV string/bytes.
|
/// Reads structured data from a CSV string/bytes.
|
||||||
#[func(title = "Decode CSV")]
|
#[func(title = "Decode CSV")]
|
||||||
#[deprecated = "`csv.decode` is deprecated, directly pass bytes to `csv` instead"]
|
#[deprecated(
|
||||||
|
message = "`csv.decode` is deprecated, directly pass bytes to `csv` instead",
|
||||||
|
until = "0.15.0"
|
||||||
|
)]
|
||||||
pub fn decode(
|
pub fn decode(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
/// CSV data.
|
/// CSV data.
|
||||||
|
@ -67,7 +67,10 @@ pub fn json(
|
|||||||
impl json {
|
impl json {
|
||||||
/// Reads structured data from a JSON string/bytes.
|
/// Reads structured data from a JSON string/bytes.
|
||||||
#[func(title = "Decode JSON")]
|
#[func(title = "Decode JSON")]
|
||||||
#[deprecated = "`json.decode` is deprecated, directly pass bytes to `json` instead"]
|
#[deprecated(
|
||||||
|
message = "`json.decode` is deprecated, directly pass bytes to `json` instead",
|
||||||
|
until = "0.15.0"
|
||||||
|
)]
|
||||||
pub fn decode(
|
pub fn decode(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
/// JSON data.
|
/// JSON data.
|
||||||
|
@ -41,7 +41,10 @@ pub fn toml(
|
|||||||
impl toml {
|
impl toml {
|
||||||
/// Reads structured data from a TOML string/bytes.
|
/// Reads structured data from a TOML string/bytes.
|
||||||
#[func(title = "Decode TOML")]
|
#[func(title = "Decode TOML")]
|
||||||
#[deprecated = "`toml.decode` is deprecated, directly pass bytes to `toml` instead"]
|
#[deprecated(
|
||||||
|
message = "`toml.decode` is deprecated, directly pass bytes to `toml` instead",
|
||||||
|
until = "0.15.0"
|
||||||
|
)]
|
||||||
pub fn decode(
|
pub fn decode(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
/// TOML data.
|
/// TOML data.
|
||||||
|
@ -75,7 +75,10 @@ pub fn xml(
|
|||||||
impl xml {
|
impl xml {
|
||||||
/// Reads structured data from an XML string/bytes.
|
/// Reads structured data from an XML string/bytes.
|
||||||
#[func(title = "Decode XML")]
|
#[func(title = "Decode XML")]
|
||||||
#[deprecated = "`xml.decode` is deprecated, directly pass bytes to `xml` instead"]
|
#[deprecated(
|
||||||
|
message = "`xml.decode` is deprecated, directly pass bytes to `xml` instead",
|
||||||
|
until = "0.15.0"
|
||||||
|
)]
|
||||||
pub fn decode(
|
pub fn decode(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
/// XML data.
|
/// XML data.
|
||||||
|
@ -54,7 +54,10 @@ pub fn yaml(
|
|||||||
impl yaml {
|
impl yaml {
|
||||||
/// Reads structured data from a YAML string/bytes.
|
/// Reads structured data from a YAML string/bytes.
|
||||||
#[func(title = "Decode YAML")]
|
#[func(title = "Decode YAML")]
|
||||||
#[deprecated = "`yaml.decode` is deprecated, directly pass bytes to `yaml` instead"]
|
#[deprecated(
|
||||||
|
message = "`yaml.decode` is deprecated, directly pass bytes to `yaml` instead",
|
||||||
|
until = "0.15.0"
|
||||||
|
)]
|
||||||
pub fn decode(
|
pub fn decode(
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
/// YAML data.
|
/// YAML data.
|
||||||
|
@ -172,7 +172,6 @@ impl ShowSet for Packed<EquationElem> {
|
|||||||
fn show_set(&self, styles: StyleChain) -> Styles {
|
fn show_set(&self, styles: StyleChain) -> Styles {
|
||||||
let mut out = Styles::new();
|
let mut out = Styles::new();
|
||||||
if self.block.get(styles) {
|
if self.block.get(styles) {
|
||||||
out.set(AlignElem::alignment, Alignment::CENTER);
|
|
||||||
out.set(AlignElem::alignment, Alignment::CENTER);
|
out.set(AlignElem::alignment, Alignment::CENTER);
|
||||||
out.set(BlockElem::breakable, false);
|
out.set(BlockElem::breakable, false);
|
||||||
out.set(ParLine::numbering, None);
|
out.set(ParLine::numbering, None);
|
||||||
|
@ -192,6 +192,39 @@ pub struct FigureElem {
|
|||||||
/// supplement: [Atom],
|
/// supplement: [Atom],
|
||||||
/// )
|
/// )
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// If you want to modify a counter to skip a number or reset the counter,
|
||||||
|
/// you can access the [counter] of each kind of figure with a
|
||||||
|
/// [`where`]($function.where) selector:
|
||||||
|
///
|
||||||
|
/// - For [tables]($table): `{counter(figure.where(kind: table))}`
|
||||||
|
/// - For [images]($image): `{counter(figure.where(kind: image))}`
|
||||||
|
/// - For a custom kind: `{counter(figure.where(kind: kind))}`
|
||||||
|
///
|
||||||
|
/// ```example
|
||||||
|
/// #figure(
|
||||||
|
/// table(columns: 2, $n$, $1$),
|
||||||
|
/// caption: [The first table.],
|
||||||
|
/// )
|
||||||
|
///
|
||||||
|
/// #counter(
|
||||||
|
/// figure.where(kind: table)
|
||||||
|
/// ).update(41)
|
||||||
|
///
|
||||||
|
/// #figure(
|
||||||
|
/// table(columns: 2, $n$, $42$),
|
||||||
|
/// caption: [The 42nd table],
|
||||||
|
/// )
|
||||||
|
///
|
||||||
|
/// #figure(
|
||||||
|
/// rect[Image],
|
||||||
|
/// caption: [Does not affect images],
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// To conveniently use the correct counter in a show rule, you can access
|
||||||
|
/// the `counter` field. There is an example of this in the documentation
|
||||||
|
/// [of the `figure.caption` element's `body` field]($figure.caption.body).
|
||||||
pub kind: Smart<FigureKind>,
|
pub kind: Smart<FigureKind>,
|
||||||
|
|
||||||
/// The figure's supplement.
|
/// The figure's supplement.
|
||||||
@ -231,8 +264,8 @@ pub struct FigureElem {
|
|||||||
/// Convenience field to get access to the counter for this figure.
|
/// Convenience field to get access to the counter for this figure.
|
||||||
///
|
///
|
||||||
/// The counter only depends on the `kind`:
|
/// The counter only depends on the `kind`:
|
||||||
/// - For (tables)[@table]: `{counter(figure.where(kind: table))}`
|
/// - For [tables]($table): `{counter(figure.where(kind: table))}`
|
||||||
/// - For (images)[@image]: `{counter(figure.where(kind: image))}`
|
/// - For [images]($image): `{counter(figure.where(kind: image))}`
|
||||||
/// - For a custom kind: `{counter(figure.where(kind: kind))}`
|
/// - For a custom kind: `{counter(figure.where(kind: kind))}`
|
||||||
///
|
///
|
||||||
/// These are the counters you'll need to modify if you want to skip a
|
/// These are the counters you'll need to modify if you want to skip a
|
||||||
|
@ -534,7 +534,7 @@ pub struct TableHLine {
|
|||||||
/// part of all your tables' designs.
|
/// part of all your tables' designs.
|
||||||
#[elem(name = "vline", title = "Table Vertical Line")]
|
#[elem(name = "vline", title = "Table Vertical Line")]
|
||||||
pub struct TableVLine {
|
pub struct TableVLine {
|
||||||
/// The column before which the horizontal line is placed (zero-indexed).
|
/// The column before which the vertical line is placed (zero-indexed).
|
||||||
/// Functions identically to the `x` field in [`grid.vline`]($grid.vline).
|
/// Functions identically to the `x` field in [`grid.vline`]($grid.vline).
|
||||||
pub x: Smart<usize>,
|
pub x: Smart<usize>,
|
||||||
|
|
||||||
@ -631,7 +631,7 @@ pub struct TableVLine {
|
|||||||
/// cell(align: left)[🌴🚗],
|
/// cell(align: left)[🌴🚗],
|
||||||
/// cell(
|
/// cell(
|
||||||
/// inset: 0.06em,
|
/// inset: 0.06em,
|
||||||
/// text(1.62em)[🛖🌅🌊],
|
/// text(1.62em)[🏝️🌅🌊],
|
||||||
/// ),
|
/// ),
|
||||||
/// )
|
/// )
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Modifiable symbols.
|
//! Modifiable symbols.
|
||||||
|
|
||||||
use crate::foundations::{Module, Scope, Symbol, Value};
|
use crate::foundations::{Deprecation, Module, Scope, Symbol, Value};
|
||||||
|
|
||||||
/// Hook up all `symbol` definitions.
|
/// Hook up all `symbol` definitions.
|
||||||
pub(super) fn define(global: &mut Scope) {
|
pub(super) fn define(global: &mut Scope) {
|
||||||
@ -23,7 +23,7 @@ fn extend_scope_from_codex_module(scope: &mut Scope, module: codex::Module) {
|
|||||||
|
|
||||||
let scope_binding = scope.define(name, value);
|
let scope_binding = scope.define(name, value);
|
||||||
if let Some(message) = binding.deprecation {
|
if let Some(message) = binding.deprecation {
|
||||||
scope_binding.deprecated(message);
|
scope_binding.deprecated(Deprecation::new().with_message(message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -411,6 +411,9 @@ pub struct TextElem {
|
|||||||
/// = Einleitung
|
/// = Einleitung
|
||||||
/// In diesem Dokument, ...
|
/// In diesem Dokument, ...
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// The language code is case-insensitive, and will be lowercased when
|
||||||
|
/// accessed through [context]($context).
|
||||||
#[default(Lang::ENGLISH)]
|
#[default(Lang::ENGLISH)]
|
||||||
#[ghost]
|
#[ghost]
|
||||||
pub lang: Lang,
|
pub lang: Lang,
|
||||||
@ -418,6 +421,9 @@ pub struct TextElem {
|
|||||||
/// An [ISO 3166-1 alpha-2 region code.](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)
|
/// An [ISO 3166-1 alpha-2 region code.](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)
|
||||||
///
|
///
|
||||||
/// This lets the text processing pipeline make more informed choices.
|
/// This lets the text processing pipeline make more informed choices.
|
||||||
|
///
|
||||||
|
/// The region code is case-insensitive, and will be uppercased when
|
||||||
|
/// accessed through [context]($context).
|
||||||
#[ghost]
|
#[ghost]
|
||||||
pub region: Option<Region>,
|
pub region: Option<Region>,
|
||||||
|
|
||||||
|
@ -264,6 +264,7 @@ impl<'s> SmartQuotes<'s> {
|
|||||||
"he" => ("’", "’", "”", "”"),
|
"he" => ("’", "’", "”", "”"),
|
||||||
"hr" => ("‘", "’", "„", "”"),
|
"hr" => ("‘", "’", "„", "”"),
|
||||||
"bg" => ("’", "’", "„", "“"),
|
"bg" => ("’", "’", "„", "“"),
|
||||||
|
"ar" if !alternative => ("’", "‘", "«", "»"),
|
||||||
_ if lang.dir() == Dir::RTL => ("’", "‘", "”", "“"),
|
_ if lang.dir() == Dir::RTL => ("’", "‘", "”", "“"),
|
||||||
_ => default,
|
_ => default,
|
||||||
};
|
};
|
||||||
|
@ -179,7 +179,10 @@ pub struct ImageElem {
|
|||||||
impl ImageElem {
|
impl ImageElem {
|
||||||
/// Decode a raster or vector graphic from bytes or a string.
|
/// Decode a raster or vector graphic from bytes or a string.
|
||||||
#[func(title = "Decode Image")]
|
#[func(title = "Decode Image")]
|
||||||
#[deprecated = "`image.decode` is deprecated, directly pass bytes to `image` instead"]
|
#[deprecated(
|
||||||
|
message = "`image.decode` is deprecated, directly pass bytes to `image` instead",
|
||||||
|
until = "0.15.0"
|
||||||
|
)]
|
||||||
pub fn decode(
|
pub fn decode(
|
||||||
span: Span,
|
span: Span,
|
||||||
/// The data to decode as an image. Can be a string for SVGs.
|
/// The data to decode as an image. Can be a string for SVGs.
|
||||||
|
@ -24,6 +24,7 @@ pub use self::shape::*;
|
|||||||
pub use self::stroke::*;
|
pub use self::stroke::*;
|
||||||
pub use self::tiling::*;
|
pub use self::tiling::*;
|
||||||
|
|
||||||
|
use crate::foundations::Deprecation;
|
||||||
use crate::foundations::{Element, Scope, Type};
|
use crate::foundations::{Element, Scope, Type};
|
||||||
|
|
||||||
/// Hook up all visualize definitions.
|
/// Hook up all visualize definitions.
|
||||||
@ -41,11 +42,14 @@ pub(super) fn define(global: &mut Scope) {
|
|||||||
global.define_elem::<CircleElem>();
|
global.define_elem::<CircleElem>();
|
||||||
global.define_elem::<PolygonElem>();
|
global.define_elem::<PolygonElem>();
|
||||||
global.define_elem::<CurveElem>();
|
global.define_elem::<CurveElem>();
|
||||||
global
|
global.define("path", Element::of::<PathElem>()).deprecated(
|
||||||
.define("path", Element::of::<PathElem>())
|
Deprecation::new()
|
||||||
.deprecated("the `path` function is deprecated, use `curve` instead");
|
.with_message("the `path` function is deprecated, use `curve` instead"),
|
||||||
global
|
);
|
||||||
.define("pattern", Type::of::<Tiling>())
|
global.define("pattern", Type::of::<Tiling>()).deprecated(
|
||||||
.deprecated("the name `pattern` is deprecated, use `tiling` instead");
|
Deprecation::new()
|
||||||
|
.with_message("the name `pattern` is deprecated, use `tiling` instead")
|
||||||
|
.with_until("0.15.0"),
|
||||||
|
);
|
||||||
global.reset_category();
|
global.reset_category();
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use heck::ToKebabCase;
|
use heck::ToKebabCase;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{Result, parse_quote};
|
use syn::punctuated::Punctuated;
|
||||||
|
use syn::{MetaNameValue, Result, Token, parse_quote};
|
||||||
|
|
||||||
use crate::util::{BareType, foundations};
|
use crate::util::{BareType, foundations};
|
||||||
|
|
||||||
@ -52,13 +53,36 @@ pub fn scope(_: TokenStream, item: syn::Item) -> Result<TokenStream> {
|
|||||||
_ => bail!(child, "unexpected item in scope"),
|
_ => bail!(child, "unexpected item in scope"),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(message) = attrs.iter().find_map(|attr| match &attr.meta {
|
if let Some(attr) = attrs.iter().find(|attr| attr.path().is_ident("deprecated")) {
|
||||||
syn::Meta::NameValue(pair) if pair.path.is_ident("deprecated") => {
|
match &attr.meta {
|
||||||
Some(&pair.value)
|
syn::Meta::NameValue(pair) if pair.path.is_ident("deprecated") => {
|
||||||
|
let message = &pair.value;
|
||||||
|
def = quote! { #def.deprecated(#message) }
|
||||||
|
}
|
||||||
|
syn::Meta::List(list) if list.path.is_ident("deprecated") => {
|
||||||
|
let args = list.parse_args_with(
|
||||||
|
Punctuated::<MetaNameValue, Token![,]>::parse_separated_nonempty,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut deprecation =
|
||||||
|
quote! { crate::foundations::Deprecation::new() };
|
||||||
|
|
||||||
|
if let Some(message) = args.iter().find_map(|pair| {
|
||||||
|
pair.path.is_ident("message").then_some(&pair.value)
|
||||||
|
}) {
|
||||||
|
deprecation = quote! { #deprecation.with_message(#message) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(version) = args.iter().find_map(|pair| {
|
||||||
|
pair.path.is_ident("until").then_some(&pair.value)
|
||||||
|
}) {
|
||||||
|
deprecation = quote! { #deprecation.with_until(#version) }
|
||||||
|
}
|
||||||
|
|
||||||
|
def = quote! { #def.deprecated(#deprecation) }
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
_ => None,
|
|
||||||
}) {
|
|
||||||
def = quote! { #def.deprecated(#message) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
definitions.push(def);
|
definitions.push(def);
|
||||||
|
@ -15,7 +15,6 @@ readme = { workspace = true }
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
typst-timing = { workspace = true }
|
typst-timing = { workspace = true }
|
||||||
typst-utils = { workspace = true }
|
typst-utils = { workspace = true }
|
||||||
comemo = { workspace = true }
|
|
||||||
ecow = { workspace = true }
|
ecow = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
toml = { workspace = true }
|
toml = { workspace = true }
|
||||||
|
@ -143,6 +143,16 @@ pub struct PackageInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PackageManifest {
|
impl PackageManifest {
|
||||||
|
/// Create a new package manifest with the given package info.
|
||||||
|
pub fn new(package: PackageInfo) -> Self {
|
||||||
|
PackageManifest {
|
||||||
|
package,
|
||||||
|
template: None,
|
||||||
|
tool: ToolInfo::default(),
|
||||||
|
unknown_fields: UnknownFields::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Ensure that this manifest is indeed for the specified package.
|
/// Ensure that this manifest is indeed for the specified package.
|
||||||
pub fn validate(&self, spec: &PackageSpec) -> Result<(), EcoString> {
|
pub fn validate(&self, spec: &PackageSpec) -> Result<(), EcoString> {
|
||||||
if self.package.name != spec.name {
|
if self.package.name != spec.name {
|
||||||
@ -173,6 +183,44 @@ impl PackageManifest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TemplateInfo {
|
||||||
|
/// Create a new template info with only required fields.
|
||||||
|
pub fn new(path: impl Into<EcoString>, entrypoint: impl Into<EcoString>) -> Self {
|
||||||
|
TemplateInfo {
|
||||||
|
path: path.into(),
|
||||||
|
entrypoint: entrypoint.into(),
|
||||||
|
thumbnail: None,
|
||||||
|
unknown_fields: UnknownFields::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PackageInfo {
|
||||||
|
/// Create a new package info with only required fields.
|
||||||
|
pub fn new(
|
||||||
|
name: impl Into<EcoString>,
|
||||||
|
version: PackageVersion,
|
||||||
|
entrypoint: impl Into<EcoString>,
|
||||||
|
) -> Self {
|
||||||
|
PackageInfo {
|
||||||
|
name: name.into(),
|
||||||
|
version,
|
||||||
|
entrypoint: entrypoint.into(),
|
||||||
|
authors: vec![],
|
||||||
|
categories: vec![],
|
||||||
|
compiler: None,
|
||||||
|
description: None,
|
||||||
|
disciplines: vec![],
|
||||||
|
exclude: vec![],
|
||||||
|
homepage: None,
|
||||||
|
keywords: vec![],
|
||||||
|
license: None,
|
||||||
|
repository: None,
|
||||||
|
unknown_fields: BTreeMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Identifies a package.
|
/// Identifies a package.
|
||||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct PackageSpec {
|
pub struct PackageSpec {
|
||||||
@ -535,22 +583,11 @@ mod tests {
|
|||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
Ok(PackageManifest {
|
Ok(PackageManifest {
|
||||||
package: PackageInfo {
|
package: PackageInfo::new(
|
||||||
name: "package".into(),
|
"package",
|
||||||
version: PackageVersion { major: 0, minor: 1, patch: 0 },
|
PackageVersion { major: 0, minor: 1, patch: 0 },
|
||||||
entrypoint: "src/lib.typ".into(),
|
"src/lib.typ"
|
||||||
authors: vec![],
|
),
|
||||||
license: None,
|
|
||||||
description: None,
|
|
||||||
homepage: None,
|
|
||||||
repository: None,
|
|
||||||
keywords: vec![],
|
|
||||||
categories: vec![],
|
|
||||||
disciplines: vec![],
|
|
||||||
compiler: None,
|
|
||||||
exclude: vec![],
|
|
||||||
unknown_fields: BTreeMap::new(),
|
|
||||||
},
|
|
||||||
template: None,
|
template: None,
|
||||||
tool: ToolInfo { sections: BTreeMap::new() },
|
tool: ToolInfo { sections: BTreeMap::new() },
|
||||||
unknown_fields: BTreeMap::new(),
|
unknown_fields: BTreeMap::new(),
|
||||||
|
@ -33,9 +33,10 @@ collaborative editor and run Typst in your browser, no installation required.
|
|||||||
|
|
||||||
If you choose to use Typst on your computer instead, you can download the
|
If you choose to use Typst on your computer instead, you can download the
|
||||||
compiler as a single, small binary which any user can run, no root privileges
|
compiler as a single, small binary which any user can run, no root privileges
|
||||||
required. Unlike LaTeX, packages are downloaded when you first use them and
|
required. Unlike popular LaTeX distributions such as TeX Live, packages are
|
||||||
then cached locally, keeping your Typst installation lean. You can use your own
|
downloaded when you first use them and then cached locally, keeping your Typst
|
||||||
editor and decide where to store your files with the local compiler.
|
installation lean. You can use your own editor and decide where to store your
|
||||||
|
files with the local compiler.
|
||||||
|
|
||||||
## How do I create a new, empty document? { #getting-started }
|
## How do I create a new, empty document? { #getting-started }
|
||||||
That's easy. You just create a new, empty text file (the file extension is
|
That's easy. You just create a new, empty text file (the file extension is
|
||||||
@ -459,7 +460,7 @@ and their corresponding Typst functions.
|
|||||||
| LaTeX Package | Typst Alternative |
|
| LaTeX Package | Typst Alternative |
|
||||||
|:--------------------------------|:-------------------------------------------|
|
|:--------------------------------|:-------------------------------------------|
|
||||||
| graphicx, svg | [`image`] function |
|
| graphicx, svg | [`image`] function |
|
||||||
| tabularx | [`table`], [`grid`] functions |
|
| tabularx, tabularray | [`table`], [`grid`] functions |
|
||||||
| fontenc, inputenc, unicode-math | Just start writing! |
|
| fontenc, inputenc, unicode-math | Just start writing! |
|
||||||
| babel, polyglossia | [`text`]($text.lang) function: `[#set text(lang: "zh")]` |
|
| babel, polyglossia | [`text`]($text.lang) function: `[#set text(lang: "zh")]` |
|
||||||
| amsmath | [Math mode]($category/math) |
|
| amsmath | [Math mode]($category/math) |
|
||||||
@ -550,8 +551,8 @@ $ f(x) = (x + 1) / x $
|
|||||||
to include more than one value in a sub- or superscript, enclose their contents
|
to include more than one value in a sub- or superscript, enclose their contents
|
||||||
in parentheses: `{$x_(a -> epsilon)$}`.
|
in parentheses: `{$x_(a -> epsilon)$}`.
|
||||||
|
|
||||||
Since variables in math mode do not need to be prepended with a `#` or a `/`,
|
Since variables in math mode do not need to be prepended with a `#` (or a `\`
|
||||||
you can also call functions without these special characters:
|
like in LaTeX), you can also call functions without these special characters:
|
||||||
|
|
||||||
```example
|
```example
|
||||||
$ f(x, y) := cases(
|
$ f(x, y) := cases(
|
||||||
@ -580,8 +581,8 @@ their call with a `#`. Nobody can stop you from using rectangles or emoji as
|
|||||||
your variables anymore:
|
your variables anymore:
|
||||||
|
|
||||||
```example
|
```example
|
||||||
$ sum^10_(🥸=1)
|
$ sum^10_(🤓=1)
|
||||||
#rect(width: 4mm, height: 2mm)/🥸
|
#rect(width: 4mm, height: 2mm)/🤓
|
||||||
= 🧠 maltese $
|
= 🧠 maltese $
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -3,3 +3,6 @@ with a normal keyboard. Alternatively, you can also always directly enter
|
|||||||
Unicode symbols into your text and formulas. In addition to the symbols listed
|
Unicode symbols into your text and formulas. In addition to the symbols listed
|
||||||
below, math mode defines `dif` and `Dif`. These are not normal symbol values
|
below, math mode defines `dif` and `Dif`. These are not normal symbol values
|
||||||
because they also affect spacing and font style.
|
because they also affect spacing and font style.
|
||||||
|
|
||||||
|
You can define custom symbols with the constructor function of the
|
||||||
|
[symbol]($symbol) type.
|
||||||
|
@ -17,6 +17,7 @@ use serde::Deserialize;
|
|||||||
use serde_yaml as yaml;
|
use serde_yaml as yaml;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
use typst::diag::{StrResult, bail};
|
use typst::diag::{StrResult, bail};
|
||||||
|
use typst::foundations::Deprecation;
|
||||||
use typst::foundations::{
|
use typst::foundations::{
|
||||||
AutoValue, Binding, Bytes, CastInfo, Func, Module, NoneValue, ParamInfo, Repr, Scope,
|
AutoValue, Binding, Bytes, CastInfo, Func, Module, NoneValue, ParamInfo, Repr, Scope,
|
||||||
Smart, Type, Value,
|
Smart, Type, Value,
|
||||||
@ -381,7 +382,7 @@ fn func_page(
|
|||||||
parent: &str,
|
parent: &str,
|
||||||
func: &Func,
|
func: &Func,
|
||||||
path: &[&str],
|
path: &[&str],
|
||||||
deprecation: Option<&'static str>,
|
deprecation: Option<&Deprecation>,
|
||||||
) -> PageModel {
|
) -> PageModel {
|
||||||
let model = func_model(resolver, func, path, false, deprecation);
|
let model = func_model(resolver, func, path, false, deprecation);
|
||||||
let name = func.name().unwrap();
|
let name = func.name().unwrap();
|
||||||
@ -402,7 +403,7 @@ fn func_model(
|
|||||||
func: &Func,
|
func: &Func,
|
||||||
path: &[&str],
|
path: &[&str],
|
||||||
nested: bool,
|
nested: bool,
|
||||||
deprecation: Option<&'static str>,
|
deprecation: Option<&Deprecation>,
|
||||||
) -> FuncModel {
|
) -> FuncModel {
|
||||||
let name = func.name().unwrap();
|
let name = func.name().unwrap();
|
||||||
let scope = func.scope().unwrap();
|
let scope = func.scope().unwrap();
|
||||||
@ -438,7 +439,8 @@ fn func_model(
|
|||||||
oneliner: oneliner(details),
|
oneliner: oneliner(details),
|
||||||
element: func.element().is_some(),
|
element: func.element().is_some(),
|
||||||
contextual: func.contextual().unwrap_or(false),
|
contextual: func.contextual().unwrap_or(false),
|
||||||
deprecation,
|
deprecation_message: deprecation.map(Deprecation::message),
|
||||||
|
deprecation_until: deprecation.and_then(Deprecation::until),
|
||||||
details: Html::markdown(resolver, details, nesting),
|
details: Html::markdown(resolver, details, nesting),
|
||||||
example: example.map(|md| Html::markdown(resolver, md, None)),
|
example: example.map(|md| Html::markdown(resolver, md, None)),
|
||||||
self_,
|
self_,
|
||||||
@ -718,7 +720,7 @@ fn symbols_model(resolver: &dyn Resolver, group: &GroupData) -> SymbolsModel {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (variant, c, deprecation) in symbol.variants() {
|
for (variant, c, deprecation_message) in symbol.variants() {
|
||||||
let shorthand = |list: &[(&'static str, char)]| {
|
let shorthand = |list: &[(&'static str, char)]| {
|
||||||
list.iter().copied().find(|&(_, x)| x == c).map(|(s, _)| s)
|
list.iter().copied().find(|&(_, x)| x == c).map(|(s, _)| s)
|
||||||
};
|
};
|
||||||
@ -737,7 +739,9 @@ fn symbols_model(resolver: &dyn Resolver, group: &GroupData) -> SymbolsModel {
|
|||||||
.filter(|(other, _, _)| other != &variant)
|
.filter(|(other, _, _)| other != &variant)
|
||||||
.map(|(other, _, _)| complete(other))
|
.map(|(other, _, _)| complete(other))
|
||||||
.collect(),
|
.collect(),
|
||||||
deprecation: deprecation.or_else(|| binding.deprecation()),
|
deprecation_message: deprecation_message
|
||||||
|
.or_else(|| binding.deprecation().map(Deprecation::message)),
|
||||||
|
deprecation_until: binding.deprecation().and_then(Deprecation::until),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,8 @@ pub struct FuncModel {
|
|||||||
pub oneliner: EcoString,
|
pub oneliner: EcoString,
|
||||||
pub element: bool,
|
pub element: bool,
|
||||||
pub contextual: bool,
|
pub contextual: bool,
|
||||||
pub deprecation: Option<&'static str>,
|
pub deprecation_message: Option<&'static str>,
|
||||||
|
pub deprecation_until: Option<&'static str>,
|
||||||
pub details: Html,
|
pub details: Html,
|
||||||
/// This example is only for nested function models. Others can have
|
/// This example is only for nested function models. Others can have
|
||||||
/// their example directly in their details.
|
/// their example directly in their details.
|
||||||
@ -165,7 +166,8 @@ pub struct SymbolModel {
|
|||||||
pub markup_shorthand: Option<&'static str>,
|
pub markup_shorthand: Option<&'static str>,
|
||||||
pub math_shorthand: Option<&'static str>,
|
pub math_shorthand: Option<&'static str>,
|
||||||
pub math_class: Option<&'static str>,
|
pub math_class: Option<&'static str>,
|
||||||
pub deprecation: Option<&'static str>,
|
pub deprecation_message: Option<&'static str>,
|
||||||
|
pub deprecation_until: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shorthands listed on a category page.
|
/// Shorthands listed on a category page.
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 959 B After Width: | Height: | Size: 969 B |
@ -1,3 +1,4 @@
|
|||||||
--- cbor-decode-deprecated ---
|
--- cbor-decode-deprecated ---
|
||||||
// Warning: 15-21 `cbor.decode` is deprecated, directly pass bytes to `cbor` instead
|
// Warning: 15-21 `cbor.decode` is deprecated, directly pass bytes to `cbor` instead
|
||||||
|
// Hint: 15-21 it will be removed in Typst 0.15.0
|
||||||
#let _ = cbor.decode
|
#let _ = cbor.decode
|
||||||
|
@ -32,4 +32,5 @@
|
|||||||
|
|
||||||
--- csv-decode-deprecated ---
|
--- csv-decode-deprecated ---
|
||||||
// Warning: 14-20 `csv.decode` is deprecated, directly pass bytes to `csv` instead
|
// Warning: 14-20 `csv.decode` is deprecated, directly pass bytes to `csv` instead
|
||||||
|
// Hint: 14-20 it will be removed in Typst 0.15.0
|
||||||
#let _ = csv.decode
|
#let _ = csv.decode
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
--- json-decode-deprecated ---
|
--- json-decode-deprecated ---
|
||||||
// Warning: 15-21 `json.decode` is deprecated, directly pass bytes to `json` instead
|
// Warning: 15-21 `json.decode` is deprecated, directly pass bytes to `json` instead
|
||||||
|
// Hint: 15-21 it will be removed in Typst 0.15.0
|
||||||
#let _ = json.decode
|
#let _ = json.decode
|
||||||
|
|
||||||
--- issue-3363-json-large-number ---
|
--- issue-3363-json-large-number ---
|
||||||
|
@ -42,4 +42,5 @@
|
|||||||
|
|
||||||
--- toml-decode-deprecated ---
|
--- toml-decode-deprecated ---
|
||||||
// Warning: 15-21 `toml.decode` is deprecated, directly pass bytes to `toml` instead
|
// Warning: 15-21 `toml.decode` is deprecated, directly pass bytes to `toml` instead
|
||||||
|
// Hint: 15-21 it will be removed in Typst 0.15.0
|
||||||
#let _ = toml.decode
|
#let _ = toml.decode
|
||||||
|
@ -29,4 +29,5 @@
|
|||||||
|
|
||||||
--- xml-decode-deprecated ---
|
--- xml-decode-deprecated ---
|
||||||
// Warning: 14-20 `xml.decode` is deprecated, directly pass bytes to `xml` instead
|
// Warning: 14-20 `xml.decode` is deprecated, directly pass bytes to `xml` instead
|
||||||
|
// Hint: 14-20 it will be removed in Typst 0.15.0
|
||||||
#let _ = xml.decode
|
#let _ = xml.decode
|
||||||
|
@ -18,4 +18,5 @@
|
|||||||
|
|
||||||
--- yaml-decode-deprecated ---
|
--- yaml-decode-deprecated ---
|
||||||
// Warning: 15-21 `yaml.decode` is deprecated, directly pass bytes to `yaml` instead
|
// Warning: 15-21 `yaml.decode` is deprecated, directly pass bytes to `yaml` instead
|
||||||
|
// Hint: 15-21 it will be removed in Typst 0.15.0
|
||||||
#let _ = yaml.decode
|
#let _ = yaml.decode
|
||||||
|
@ -188,26 +188,31 @@ A #box(image("/assets/images/tiger.jpg", height: 1cm, width: 80%)) B
|
|||||||
--- image-decode-svg ---
|
--- image-decode-svg ---
|
||||||
// Test parsing from svg data
|
// Test parsing from svg data
|
||||||
// Warning: 8-14 `image.decode` is deprecated, directly pass bytes to `image` instead
|
// Warning: 8-14 `image.decode` is deprecated, directly pass bytes to `image` instead
|
||||||
|
// Hint: 8-14 it will be removed in Typst 0.15.0
|
||||||
#image.decode(`<svg xmlns="http://www.w3.org/2000/svg" height="140" width="500"><ellipse cx="200" cy="80" rx="100" ry="50" style="fill:yellow;stroke:purple;stroke-width:2" /></svg>`.text, format: "svg")
|
#image.decode(`<svg xmlns="http://www.w3.org/2000/svg" height="140" width="500"><ellipse cx="200" cy="80" rx="100" ry="50" style="fill:yellow;stroke:purple;stroke-width:2" /></svg>`.text, format: "svg")
|
||||||
|
|
||||||
--- image-decode-bad-svg ---
|
--- image-decode-bad-svg ---
|
||||||
// Error: 15-152 failed to parse SVG (missing root node at 1:1)
|
// Error: 15-152 failed to parse SVG (missing root node at 1:1)
|
||||||
// Warning: 8-14 `image.decode` is deprecated, directly pass bytes to `image` instead
|
// Warning: 8-14 `image.decode` is deprecated, directly pass bytes to `image` instead
|
||||||
|
// Hint: 8-14 it will be removed in Typst 0.15.0
|
||||||
#image.decode(`<svg height="140" width="500"><ellipse cx="200" cy="80" rx="100" ry="50" style="fill:yellow;stroke:purple;stroke-width:2" /></svg>`.text, format: "svg")
|
#image.decode(`<svg height="140" width="500"><ellipse cx="200" cy="80" rx="100" ry="50" style="fill:yellow;stroke:purple;stroke-width:2" /></svg>`.text, format: "svg")
|
||||||
|
|
||||||
--- image-decode-detect-format ---
|
--- image-decode-detect-format ---
|
||||||
// Test format auto detect
|
// Test format auto detect
|
||||||
// Warning: 8-14 `image.decode` is deprecated, directly pass bytes to `image` instead
|
// Warning: 8-14 `image.decode` is deprecated, directly pass bytes to `image` instead
|
||||||
|
// Hint: 8-14 it will be removed in Typst 0.15.0
|
||||||
#image.decode(read("/assets/images/tiger.jpg", encoding: none), width: 80%)
|
#image.decode(read("/assets/images/tiger.jpg", encoding: none), width: 80%)
|
||||||
|
|
||||||
--- image-decode-specify-format ---
|
--- image-decode-specify-format ---
|
||||||
// Test format manual
|
// Test format manual
|
||||||
// Warning: 8-14 `image.decode` is deprecated, directly pass bytes to `image` instead
|
// Warning: 8-14 `image.decode` is deprecated, directly pass bytes to `image` instead
|
||||||
|
// Hint: 8-14 it will be removed in Typst 0.15.0
|
||||||
#image.decode(read("/assets/images/tiger.jpg", encoding: none), format: "jpg", width: 80%)
|
#image.decode(read("/assets/images/tiger.jpg", encoding: none), format: "jpg", width: 80%)
|
||||||
|
|
||||||
--- image-decode-specify-wrong-format ---
|
--- image-decode-specify-wrong-format ---
|
||||||
// Error: 2-91 failed to decode image (Format error decoding Png: Invalid PNG signature.)
|
// Error: 2-91 failed to decode image (Format error decoding Png: Invalid PNG signature.)
|
||||||
// Warning: 8-14 `image.decode` is deprecated, directly pass bytes to `image` instead
|
// Warning: 8-14 `image.decode` is deprecated, directly pass bytes to `image` instead
|
||||||
|
// Hint: 8-14 it will be removed in Typst 0.15.0
|
||||||
#image.decode(read("/assets/images/tiger.jpg", encoding: none), format: "png", width: 80%)
|
#image.decode(read("/assets/images/tiger.jpg", encoding: none), format: "png", width: 80%)
|
||||||
|
|
||||||
--- image-pixmap-empty ---
|
--- image-pixmap-empty ---
|
||||||
|
@ -161,5 +161,6 @@
|
|||||||
#set page(width: auto, height: auto, margin: 0pt)
|
#set page(width: auto, height: auto, margin: 0pt)
|
||||||
|
|
||||||
// Warning: 10-17 the name `pattern` is deprecated, use `tiling` instead
|
// Warning: 10-17 the name `pattern` is deprecated, use `tiling` instead
|
||||||
|
// Hint: 10-17 it will be removed in Typst 0.15.0
|
||||||
#let t = pattern(size: (10pt, 10pt), line(stroke: 4pt, start: (0%, 0%), end: (100%, 100%)))
|
#let t = pattern(size: (10pt, 10pt), line(stroke: 4pt, start: (0%, 0%), end: (100%, 100%)))
|
||||||
#rect(width: 50pt, height: 50pt, fill: t)
|
#rect(width: 50pt, height: 50pt, fill: t)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user