summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml4
-rw-r--r--assets/files/details.toml3
-rw-r--r--assets/files/informations.toml11
-rw-r--r--assets/files/toml-types.toml (renamed from assets/files/toml_types.toml)2
-rw-r--r--assets/files/yaml-types.yaml (renamed from assets/files/yamltypes.yaml)0
-rw-r--r--cli/Cargo.toml14
-rw-r--r--docs/Cargo.toml10
-rw-r--r--docs/src/lib.rs4
-rw-r--r--docs/src/reference/scripting.md38
-rw-r--r--docs/src/reference/styling.md20
-rw-r--r--docs/src/reference/types.md64
-rw-r--r--library/Cargo.toml6
-rw-r--r--library/src/compute/calc.rs22
-rw-r--r--library/src/compute/data.rs47
-rw-r--r--library/src/compute/foundations.rs3
-rw-r--r--library/src/layout/measure.rs3
-rw-r--r--library/src/layout/terms.rs8
-rw-r--r--library/src/lib.rs26
-rw-r--r--library/src/math/attach.rs44
-rw-r--r--library/src/math/cancel.rs34
-rw-r--r--library/src/meta/reference.rs82
-rw-r--r--library/src/text/raw.rs18
-rw-r--r--library/src/visualize/line.rs54
-rw-r--r--library/src/visualize/shape.rs4
-rw-r--r--macros/Cargo.toml2
-rw-r--r--src/eval/library.rs12
-rw-r--r--src/eval/mod.rs8
-rw-r--r--src/geom/stroke.rs10
-rw-r--r--src/model/content.rs1
-rw-r--r--src/model/styles.rs56
-rw-r--r--src/syntax/parser.rs15
-rw-r--r--tests/typ/bugs/args-underscore.typ5
-rw-r--r--tests/typ/compiler/show-node.typ2
-rw-r--r--tests/typ/compiler/show-selector.typ4
-rw-r--r--tests/typ/compute/data.typ4
-rw-r--r--tests/typ/math/matrix.typ8
-rw-r--r--tests/typ/meta/query-before-after.typ24
-rw-r--r--tests/typ/visualize/stroke.typ78
38 files changed, 449 insertions, 301 deletions
diff --git a/Cargo.toml b/Cargo.toml
index cab4f3a5..dbd25c33 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -29,6 +29,7 @@ flate2 = "1"
fontdb = "0.9"
if_chain = "1"
image = { version = "0.24", default-features = false, features = ["png", "jpeg", "gif"] }
+indexmap = "1.9.3"
log = "0.4"
miniz_oxide = "0.7"
once_cell = "1"
@@ -43,6 +44,7 @@ siphasher = "0.3"
subsetter = "0.1.1"
svg2pdf = "0.4"
tiny-skia = "0.6.6"
+tracing = "0.1.37"
ttf-parser = "0.18.1"
unicode-math-class = "0.1"
unicode-segmentation = "1"
@@ -50,8 +52,6 @@ unicode-xid = "0.2"
unscanny = "0.1"
usvg = { version = "0.22", default-features = false, features = ["text"] }
xmp-writer = "0.1"
-tracing = "0.1.37"
-indexmap = "1.9.3"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
stacker = "0.1.15"
diff --git a/assets/files/details.toml b/assets/files/details.toml
new file mode 100644
index 00000000..56cf166d
--- /dev/null
+++ b/assets/files/details.toml
@@ -0,0 +1,3 @@
+title = "Secret project"
+version = 2
+authors = ["Mr Robert", "Miss Enola"]
diff --git a/assets/files/informations.toml b/assets/files/informations.toml
deleted file mode 100644
index f6e3b5fb..00000000
--- a/assets/files/informations.toml
+++ /dev/null
@@ -1,11 +0,0 @@
-authors = ["Mr Robert", "Miss Enola", "Mr Jonathan"]
-version = "1.0.3"
-
-series = [
- { name = "attack on titan", fans = 500},
- { name = "demon slayer", fans = 10}
-]
-
-[informations]
-location = "room A204"
-pages = 47 \ No newline at end of file
diff --git a/assets/files/toml_types.toml b/assets/files/toml-types.toml
index 08f1118d..93feead8 100644
--- a/assets/files/toml_types.toml
+++ b/assets/files/toml-types.toml
@@ -8,4 +8,4 @@ inline_table = { first = "amazing", second = "greater" }
[table]
element = 5
-others = [false, "indeed", 7] \ No newline at end of file
+others = [false, "indeed", 7]
diff --git a/assets/files/yamltypes.yaml b/assets/files/yaml-types.yaml
index eb759777..eb759777 100644
--- a/assets/files/yamltypes.yaml
+++ b/assets/files/yaml-types.yaml
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index 8557281f..528ce614 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -22,24 +22,24 @@ doc = false
typst = { path = ".." }
typst-library = { path = "../library" }
chrono = { version = "0.4", default-features = false, features = ["clock", "std"] }
+clap = { version = "4.2.4", features = ["derive", "env"] }
codespan-reporting = "0.11"
comemo = "0.2.2"
dirs = "5"
elsa = "1.8"
+inferno = "0.11.15"
memmap2 = "0.5"
notify = "5"
once_cell = "1"
+open = "4.0.2"
same-file = "1"
siphasher = "0.3"
-walkdir = "2"
-clap = { version = "4.2.4", features = ["derive", "env"] }
-open = "4.0.2"
+tempfile = "3.5.0"
tracing = "0.1.37"
-tracing-subscriber = "0.3.17"
-tracing-flame = "0.2.0"
tracing-error = "0.2"
-inferno = "0.11.15"
-tempfile = "3.5.0"
+tracing-flame = "0.2.0"
+tracing-subscriber = "0.3.17"
+walkdir = "2"
[build-dependencies]
clap = { version = "4.2.4", features = ["derive", "string"] }
diff --git a/docs/Cargo.toml b/docs/Cargo.toml
index 77d0b375..e2f06e04 100644
--- a/docs/Cargo.toml
+++ b/docs/Cargo.toml
@@ -12,14 +12,14 @@ bench = false
[dependencies]
typst = { path = ".." }
typst-library = { path = "../library" }
-unscanny = "0.1"
+comemo = "0.2.2"
+heck = "0.4"
include_dir = "0.7"
+once_cell = "1"
pulldown-cmark = "0.9"
-comemo = "0.2.2"
serde = { version = "1", features = ["derive"] }
serde_yaml = "0.8"
-heck = "0.4"
-yaml-front-matter = "0.1"
unicode_names2 = "0.6.0"
-once_cell = "1"
+unscanny = "0.1"
ureq = { version = "2.6", features = ["json"] }
+yaml-front-matter = "0.1"
diff --git a/docs/src/lib.rs b/docs/src/lib.rs
index 7333090d..bc445624 100644
--- a/docs/src/lib.rs
+++ b/docs/src/lib.rs
@@ -300,7 +300,7 @@ pub struct FuncModel {
pub name: &'static str,
pub display: &'static str,
pub oneliner: &'static str,
- pub showable: bool,
+ pub element: bool,
pub details: Html,
pub params: Vec<ParamModel>,
pub returns: Vec<&'static str>,
@@ -340,7 +340,7 @@ fn func_model(resolver: &dyn Resolver, func: &Func, info: &FuncInfo) -> FuncMode
name: info.name,
display: info.display,
oneliner: oneliner(docs),
- showable: func.element().is_some(),
+ element: func.element().is_some(),
details: Html::markdown(resolver, docs),
params: info.params.iter().map(|param| param_model(resolver, param)).collect(),
returns: info.returns.clone(),
diff --git a/docs/src/reference/scripting.md b/docs/src/reference/scripting.md
index d125bf5b..6af17cc7 100644
--- a/docs/src/reference/scripting.md
+++ b/docs/src/reference/scripting.md
@@ -64,7 +64,7 @@ Content and code blocks can be nested arbitrarily. In the example below,
}
```
-## Let bindings { #bindings }
+## Bindings and Destructuring { #bindings }
As already demonstrated above, variables can be defined with `{let}` bindings.
The variable is assigned the value of the expression that follows the `=` sign.
The assignment of a value is optional, if no value is assigned, the variable
@@ -82,7 +82,10 @@ Sum is #add(2, 3).
```
Let bindings can also be used to destructure [arrays]($type/array) and
-[dictionaries]($type/dictionary).
+[dictionaries]($type/dictionary). In this case, the left-hand side of the
+assignment should mirror an array or dictionary. The `..` operator can be used
+once in the pattern to collect the remainder of the array's or dictionary's
+items.
```example
#let (x, y) = (1, 2)
@@ -117,6 +120,28 @@ You can use the underscore to discard elements in a destructuring pattern:
The y coordinate is #y.
```
+Destructuring also work in argument lists of functions ...
+
+```example
+#let left = (2, 4, 5)
+#let right = (3, 2, 6)
+#left.zip(right).map(
+ ((a,b)) => a + b
+)
+```
+
+... and on the left-hand side of normal assignments. This can be useful to
+swap variables among other things.
+
+```example
+#{
+ let a = 1
+ let b = 2
+ (a, b) = (b, a)
+ [a = #a, b = #b]
+}
+```
+
## Conditionals { #conditionals }
With a conditional, you can display or compute different things depending on
whether some condition is fulfilled. Typst supports `{if}`, `{else if}` and
@@ -206,12 +231,19 @@ can be either:
- a [dictionary]($type/dictionary) that has the specified key,
- a [symbol]($type/symbol) that has the specified modifier,
- a [module]($type/module) containing the specified definition,
-- [content]($type/content) that has the specified field.
+- [content]($type/content) consisting of an element that has the specified
+ field. The available fields match the arguments of the
+ [element function]($type/function/#element-functions) that were given when
+ the element was constructed.
```example
#let dict = (greet: "Hello")
#dict.greet \
#emoji.face
+
+#let it = [= Heading]
+#it.body \
+#it.level
```
## Methods { #methods }
diff --git a/docs/src/reference/styling.md b/docs/src/reference/styling.md
index a3cc3c2e..d088d61d 100644
--- a/docs/src/reference/styling.md
+++ b/docs/src/reference/styling.md
@@ -12,11 +12,12 @@ of elements.
## Set rules { #set-rules }
With set rules, you can customize the appearance of elements. They are written
-as a [function call]($type/function) to the respective function preceded by the
-`{set}` keyword (or `[#set]` in markup). Only optional parameters of that
-function can be provided to the set rule. Refer to each function's documentation
-to see which parameters are optional. In the example below, we use two set rules
-to change the [font family]($func/text.family) and
+as a [function call]($type/function) to an
+[element function]($type/function/#element-functions) preceded by the `{set}`
+keyword (or `[#set]` in markup). Only optional parameters of that function can
+be provided to the set rule. Refer to each function's documentation to see which
+parameters are optional. In the example below, we use two set rules to change
+the [font family]($func/text.family) and
[heading numbering]($func/heading.numbering).
```example
@@ -62,9 +63,10 @@ a _set-if_ rule.
## Show rules { #show-rules }
With show rules, you can deeply customize the look of a type of element. The
most basic form of show rule is a _show-set rule._ Such a rule is written as the
-`{show}` keyword followed by a function name, a colon and then a set rule. This
-lets the set rule only apply to the selected element. In the example below,
-headings become dark blue while all other text stays black.
+`{show}` keyword followed by a [selector]($type/selector), a colon and then a set rule. The most basic form of selector is an
+[element function]($type/function/#element-functions). This lets the set rule
+only apply to the selected element. In the example below, headings become dark
+blue while all other text stays black.
```example
#show heading: set text(navy)
@@ -78,7 +80,7 @@ achieve many different effects. But they still limit you to what is predefined
in Typst. For maximum flexibility, you can instead write a show rule that
defines how to format an element from scratch. To write such a show rule,
replace the set rule behind the colon with an arbitrary
-[function]($type/function). This functions receives the element in question and
+[function]($type/function). This function receives the element in question and
can return arbitrary content. Different
[fields]($scripting/#fields) are available on the element passed
to the function. Below, we define a show rule that formats headings for a
diff --git a/docs/src/reference/types.md b/docs/src/reference/types.md
index 1bae6b75..0714d1f7 100644
--- a/docs/src/reference/types.md
+++ b/docs/src/reference/types.md
@@ -644,14 +644,14 @@ Folds all items into a single value using an accumulator function.
Sums all items (works for any types that can be added).
- default: any (named)
- If set and the array is empty, sum will return this.
+ What to return if the array is empty. Must be set if the array can be empty.
- returns: any
### product()
Calculates the product all items (works for any types that can be multiplied)
- default: any (named)
- If set and the array is empty, sum will return this.
+ What to return if the array is empty. Must be set if the array can be empty.
- returns: any
### any()
@@ -814,6 +814,13 @@ documentation about [set]($styling/#set-rules) and
[show]($styling/#show-rules) rules to learn about additional ways
you can work with functions in Typst.
+### Element functions { #element-functions }
+Some functions are associated with _elements_ like [headings]($func/heading) or
+[tables]($func/table). When called, these create an element of their respective
+kind. In contrast to normal functions, they can further be used in
+[set rules]($styling/#set-rules), [show rules]($styling/#show-rules), and
+[selectors]($type/selector).
+
### Defining functions { #definitions }
You can define your own function with a
[let binding]($scripting/#bindings) that has a parameter list after
@@ -916,30 +923,11 @@ Returns the captured named arguments as a dictionary.
- returns: dictionary
-# Module
-An evaluated module, either built-in or resulting from a file.
-
-You can access definitions from the module using
-[field access notation]($scripting/#fields) and interact with it using the
-[import and include syntaxes]($scripting/#modules).
-
-## Example
-```example
-<<< #import "utils.typ"
-<<< #utils.add(2, 5)
-
-<<< #import utils: sub
-<<< #sub(1, 4)
->>> #7
->>>
->>> #(-3)
-```
-
# Selector
A filter for selecting elements within the document.
You can construct a selector in the following ways:
-- you can use an element function
+- you can use an element [function]($type/function)
- you can filter for an element function with
[specific fields]($type/function.where)
- you can use a [string]($type/string) or [regular expression]($func/regex)
@@ -948,13 +936,16 @@ You can construct a selector in the following ways:
- call the [`selector`]($func/selector) function to convert any of the above
types into a selector value and use the methods below to refine it
-A selector is what you can use to query the document for certain types
-of elements. It can also be used to apply styling rules to element. You can
-combine multiple selectors using the methods shown below.
+Selectors are used to [apply styling rules]($styling/#show-rules) to elements.
+You can also use selectors to [query]($func/query) the document for certain
+types of elements.
-Selectors can also be passed to several of Typst's built-in functions to
+Furthermore, you can pass a selector to several of Typst's built-in functions to
configure their behaviour. One such example is the [outline]($func/outline)
-where it can be use to change which elements are listed within the outline.
+where it can be used to change which elements are listed within the outline.
+
+Multiple selectors can be combined using the methods shown below. However, not
+all kinds of selectors are supported in all places, at the moment.
## Example
```example
@@ -1004,3 +995,22 @@ first match of the selector argument.
- inclusive: boolean (named)
Whether `start` itself should match or not. This is only relevant if both
selectors match the same type of element. Defaults to `{true}`.
+
+# Module
+An evaluated module, either built-in or resulting from a file.
+
+You can access definitions from the module using
+[field access notation]($scripting/#fields) and interact with it using the
+[import and include syntaxes]($scripting/#modules).
+
+## Example
+```example
+<<< #import "utils.typ"
+<<< #utils.add(2, 5)
+
+<<< #import utils: sub
+<<< #sub(1, 4)
+>>> #7
+>>>
+>>> #(-3)
+```
diff --git a/library/Cargo.toml b/library/Cargo.toml
index c0c20c41..bc68b564 100644
--- a/library/Cargo.toml
+++ b/library/Cargo.toml
@@ -16,6 +16,7 @@ bench = false
[dependencies]
typst = { path = ".." }
+chinese-number = { version = "0.7.2", default-features = false, features = ["number-to-chinese"] }
comemo = "0.2.2"
csv = "1"
ecow = "0.1"
@@ -31,6 +32,8 @@ serde_json = "1"
serde_yaml = "0.8"
smallvec = "1.10"
syntect = { version = "5", default-features = false, features = ["default-syntaxes", "regex-fancy"] }
+toml = { version = "0.7.3", default-features = false, features = ["parse"] }
+tracing = "0.1.37"
ttf-parser = "0.18.1"
typed-arena = "2"
unicode-bidi = "0.3.13"
@@ -38,6 +41,3 @@ unicode-math-class = "0.1"
unicode-script = "0.5"
unicode-segmentation = "1"
xi-unicode = "0.3"
-chinese-number = { version = "0.7.2", default-features = false, features = ["number-to-chinese"] }
-tracing = "0.1.37"
-toml = { version = "0.7.3", default-features = false, features = ["parse"]}
diff --git a/library/src/compute/calc.rs b/library/src/compute/calc.rs
index b6b2442f..708d795d 100644
--- a/library/src/compute/calc.rs
+++ b/library/src/compute/calc.rs
@@ -41,6 +41,7 @@ pub fn module() -> Module {
scope.define("even", even);
scope.define("odd", odd);
scope.define("rem", rem);
+ scope.define("mod", mod_);
scope.define("quo", quo);
scope.define("inf", Value::Float(f64::INFINITY));
scope.define("nan", Value::Float(f64::NAN));
@@ -912,6 +913,27 @@ pub fn rem(
dividend.apply2(divisor.v, Rem::rem, Rem::rem).value()
}
+/// Calculate the modulus of two numbers. (Deprecated)
+///
+/// **This function is deprecated in favor of `rem`. It will be removed in
+/// a future update.**
+///
+/// Display: Modulus
+/// Category: calculate
+/// Returns: integer or float
+#[func]
+pub fn mod_(
+ /// The dividend of the remainder.
+ dividend: Num,
+ /// The divisor of the remainder.
+ divisor: Spanned<Num>,
+) -> Value {
+ if divisor.v.float() == 0.0 {
+ bail!(divisor.span, "divisor must not be zero");
+ }
+ dividend.apply2(divisor.v, Rem::rem, Rem::rem).value()
+}
+
/// Calculate the quotient of two numbers.
///
/// ## Example
diff --git a/library/src/compute/data.rs b/library/src/compute/data.rs
index 02586ddc..14ae304b 100644
--- a/library/src/compute/data.rs
+++ b/library/src/compute/data.rs
@@ -201,7 +201,6 @@ fn convert_json(value: serde_json::Value) -> Value {
}
/// Format the user-facing JSON error message.
-#[track_caller]
fn format_json_error(error: serde_json::Error) -> EcoString {
assert!(error.is_syntax() || error.is_eof());
eco_format!("failed to parse json file: syntax error in line {}", error.line())
@@ -209,30 +208,24 @@ fn format_json_error(error: serde_json::Error) -> EcoString {
/// Read structured data from a TOML file.
///
-/// The file must contain a valid TOML table. Tables will be
+/// The file must contain a valid TOML table. TOML tables will be
/// converted into Typst dictionaries, and TOML arrays will be converted into
/// Typst arrays. Strings and booleans will be converted into the Typst
-/// equivalents, numbers will be converted to floats or integers depending on
-/// whether they are whole numbers. TOML DateTim will be converted to strings.
+/// equivalents and numbers will be converted to floats or integers depending on
+/// whether they are whole numbers. For the time being, datetimes will be
+/// converted to strings as Typst does not have a built-in datetime yet.
///
-/// The function returns a dictionary.
-///
-/// The JSON files in the example contain objects with the keys `temperature`,
-/// `unit`, and `weather`.
+/// The TOML file in the example consists of a table with the keys `title`,
+/// `version`, and `authors`.
///
/// ## Example
/// ```example
-/// #let informations(content) = {
-/// [This work is made by #content.authors.join(", ", last: " and "). We are currently at version #content.version.
-/// The favorites series of the audience are ]
-/// for serie in content.series [
-/// - #serie.name with #serie.fans fans.
-/// ]
-/// [We need to submit our work in #content.informations.location, we currently have #content.informations.pages pages.]
-/// }
-///
+/// #let details = toml("details.toml")
///
-/// #informations(toml("informations.toml"))
+/// Title: #details.title \
+/// Version: #details.version \
+/// Authors: #(details.authors
+/// .join(", ", last: " and "))
/// ```
///
/// Display: TOML
@@ -268,23 +261,22 @@ fn convert_toml(value: toml::Value) -> Value {
.map(|(key, value)| (key.into(), convert_toml(value)))
.collect(),
),
- // Todo: make it use native date/time object(s) once it is implemented.
+ // TODO: Make it use native date/time object(s) once it is implemented.
toml::Value::Datetime(v) => Value::Str(v.to_string().into()),
}
}
/// Format the user-facing TOML error message.
-#[track_caller]
-fn format_toml_error(error: toml::de::Error) -> String {
+fn format_toml_error(error: toml::de::Error) -> EcoString {
if let Some(range) = error.span() {
- format!(
- "failed to parse toml file: {message}, index {start}-{end}",
- message = error.message(),
- start = range.start,
- end = range.end
+ eco_format!(
+ "failed to parse toml file: {}, index {}-{}",
+ error.message(),
+ range.start,
+ range.end
)
} else {
- format!("failed to parse toml file: {message}", message = error.message())
+ eco_format!("failed to parse toml file: {}", error.message())
}
}
@@ -373,7 +365,6 @@ fn convert_yaml_key(key: serde_yaml::Value) -> Option<Str> {
}
/// Format the user-facing YAML error message.
-#[track_caller]
fn format_yaml_error(error: serde_yaml::Error) -> EcoString {
eco_format!("failed to parse yaml file: {}", error.to_string().trim())
}
diff --git a/library/src/compute/foundations.rs b/library/src/compute/foundations.rs
index d5397e60..bad9f8ab 100644
--- a/library/src/compute/foundations.rs
+++ b/library/src/compute/foundations.rs
@@ -31,6 +31,9 @@ pub fn type_(
/// in monospace with syntax-highlighting. The exceptions are `{none}`,
/// integers, floats, strings, content, and functions.
///
+/// **Note:** This function is for debugging purposes. Its output should not be
+/// considered stable and may change at any time!
+///
/// ## Example
/// ```example
/// #none vs #repr(none) \
diff --git a/library/src/layout/measure.rs b/library/src/layout/measure.rs
index 0b7b9ee7..473bf5c6 100644
--- a/library/src/layout/measure.rs
+++ b/library/src/layout/measure.rs
@@ -5,6 +5,9 @@ use crate::prelude::*;
/// The `measure` function lets you determine the layouted size of content.
/// Note that an infinite space is assumed, therefore the measured height/width
/// may not necessarily match the final height/width of the measured content.
+/// If you want to measure in the current layout dimensions, you can combined
+/// `measure` and [`layout`]($func/layout).
+///
/// The same content can have a different size depending on the styles that
/// are active when it is layouted. For example, in the example below
/// `[#content]` is of course bigger when we increase the font size.
diff --git a/library/src/layout/terms.rs b/library/src/layout/terms.rs
index ecda7668..b6919eec 100644
--- a/library/src/layout/terms.rs
+++ b/library/src/layout/terms.rs
@@ -8,10 +8,6 @@ use crate::prelude::*;
/// descriptions span over multiple lines, they use hanging indent to
/// communicate the visual hierarchy.
///
-/// ## Syntax
-/// This function also has dedicated syntax: Starting a line with a slash,
-/// followed by a term, a colon and a description creates a term list item.
-///
/// ## Example
/// ```example
/// / Ligature: A merged glyph.
@@ -19,6 +15,10 @@ use crate::prelude::*;
/// between two adjacent letters.
/// ```
///
+/// ## Syntax
+/// This function also has dedicated syntax: Starting a line with a slash,
+/// followed by a term, a colon and a description creates a term list item.
+///
/// Display: Term List
/// Category: layout
#[element(Layout)]
diff --git a/library/src/lib.rs b/library/src/lib.rs
index 0350746d..3701894d 100644
--- a/library/src/lib.rs
+++ b/library/src/lib.rs
@@ -228,25 +228,25 @@ fn items() -> LangItems {
equation: |body, block| math::EquationElem::new(body).with_block(block).pack(),
math_align_point: || math::AlignPointElem::new().pack(),
math_delimited: |open, body, close| math::LrElem::new(open + body + close).pack(),
- math_attach: |base, top, bottom, topleft, bottomleft, topright, bottomright| {
+ math_attach: |base, t, b, tl, bl, tr, br| {
let mut elem = math::AttachElem::new(base);
- if let Some(top) = top {
- elem.push_t(Some(top));
+ if let Some(t) = t {
+ elem.push_t(Some(t));
}
- if let Some(bottom) = bottom {
- elem.push_b(Some(bottom));
+ if let Some(b) = b {
+ elem.push_b(Some(b));
}
- if let Some(topleft) = topleft {
- elem.push_tl(Some(topleft));
+ if let Some(tl) = tl {
+ elem.push_tl(Some(tl));
}
- if let Some(bottomleft) = bottomleft {
- elem.push_bl(Some(bottomleft));
+ if let Some(bl) = bl {
+ elem.push_bl(Some(bl));
}
- if let Some(topright) = topright {
- elem.push_tr(Some(topright));
+ if let Some(tr) = tr {
+ elem.push_tr(Some(tr));
}
- if let Some(bottomright) = bottomright {
- elem.push_br(Some(bottomright));
+ if let Some(br) = br {
+ elem.push_br(Some(br));
}
elem.pack()
},
diff --git a/library/src/math/attach.rs b/library/src/math/attach.rs
index 34c24e17..e7cffdd3 100644
--- a/library/src/math/attach.rs
+++ b/library/src/math/attach.rs
@@ -2,16 +2,23 @@ use super::*;
/// A base with optional attachments.
///
-/// ## Syntax
-/// This function also has dedicated syntax for attachments after the base: Use the
-/// underscore (`_`) to indicate a subscript i.e. bottom attachment and the hat (`^`)
-/// to indicate a superscript i.e. top attachment.
-///
/// ## Example
/// ```example
+/// // With syntax.
/// $ sum_(i=0)^n a_i = 2^(1+i) $
+///
+/// // With function call.
+/// $ attach(
+/// Pi, t: alpha, b: beta,
+/// tl: 1, tr: 2, bl: 3, br: 4,
+/// ) $
/// ```
///
+/// ## Syntax
+/// This function also has dedicated syntax for attachments after the base: Use
+/// the underscore (`_`) to indicate a subscript i.e. bottom attachment and the
+/// hat (`^`) to indicate a superscript i.e. top attachment.
+///
/// Display: Attachment
/// Category: math
#[element(LayoutMath)]
@@ -21,41 +28,42 @@ pub struct AttachElem {
pub base: Content,
/// The top attachment, smartly positioned at top-right or above the base.
- /// Use limits() or scripts() on the base to override the smart positioning.
+ ///
+ /// You can wrap the base in `{limits()}` or `{scripts()}` to override the
+ /// smart positioning.
pub t: Option<Content>,
- /// The bottom attachment, smartly positioned at the bottom-right or below the base.
- /// Use limits() or scripts() on the base to override the smart positioning.
+ /// The bottom attachment, smartly positioned at the bottom-right or below
+ /// the base. You can wrap the base in `{limits()}` or `{scripts()}` to
+ /// override the smart positioning.
pub b: Option<Content>,
- /// The top-left attachment before the base.
+ /// The top-left attachment (before the base).
pub tl: Option<Content>,
- /// The bottom-left attachment before base.
+ /// The bottom-left attachment (before base).
pub bl: Option<Content>,
- /// The top-right attachment after the base.
+ /// The top-right attachment (after the base).
pub tr: Option<Content>,
- /// The bottom-right attachment after the base.
+ /// The bottom-right attachment (after the base).
pub br: Option<Content>,
}
-type GetAttachmentContent =
- fn(&AttachElem, styles: ::typst::model::StyleChain) -> Option<Content>;
-
impl LayoutMath for AttachElem {
#[tracing::instrument(skip(ctx))]
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- let base = ctx.layout_fragment(&self.base())?;
-
- let getarg = |ctx: &mut MathContext, getter: GetAttachmentContent| {
+ type GetAttachment = fn(&AttachElem, styles: StyleChain) -> Option<Content>;
+ let getarg = |ctx: &mut MathContext, getter: GetAttachment| {
getter(self, ctx.styles())
.map(|elem| ctx.layout_fragment(&elem))
.transpose()
.unwrap()
};
+ let base = ctx.layout_fragment(&self.base())?;
+
ctx.style(ctx.style.for_superscript());
let arg_tl = getarg(ctx, Self::tl);
let arg_tr = getarg(ctx, Self::tr);
diff --git a/library/src/math/cancel.rs b/library/src/math/cancel.rs
index 0ea7b1d2..edc2ba1e 100644
--- a/library/src/math/cancel.rs
+++ b/library/src/math/cancel.rs
@@ -2,10 +2,14 @@ use super::*;
/// Displays a diagonal line over a part of an equation.
///
+/// This is commonly used to show the eliminiation of a term.
+///
/// ## Example
/// ```example
+/// >>> #set page(width: 140pt)
/// Here, we can simplify:
-/// $ (a dot.c b dot.c cancel(x)) / cancel(x) $
+/// $ (a dot b dot cancel(x)) /
+/// cancel(x) $
/// ```
///
/// Display: Cancel
@@ -23,18 +27,22 @@ pub struct CancelElem {
/// Defaults to `{100% + 3pt}`.
///
/// ```example
- /// $ a + cancel(x, length: #200%) - b - cancel(x, length: #200%) $
+ /// >>> #set page(width: 140pt)
+ /// $ a + cancel(x, length: #200%)
+ /// - cancel(x, length: #200%) $
/// ```
#[default(Rel::new(Ratio::one(), Abs::pt(3.0).into()))]
pub length: Rel<Length>,
- /// If the cancel line should be inverted (heading northwest instead of
- /// northeast).
+ /// If the cancel line should be inverted (pointing to the top left instead
+ /// of top right).
///
/// Defaults to `{false}`.
///
/// ```example
- /// $ (a cancel((b + c), inverted: #true)) / cancel(b + c, inverted: #true) $
+ /// >>> #set page(width: 140pt)
+ /// $ (a cancel((b + c), inverted: #true)) /
+ /// cancel(b + c, inverted: #true) $
/// ```
#[default(false)]
pub inverted: bool,
@@ -45,7 +53,8 @@ pub struct CancelElem {
/// Defaults to `{false}`.
///
/// ```example
- /// $ cancel(x, cross: #true) $
+ /// >>> #set page(width: 140pt)
+ /// $ cancel(Pi, cross: #true) $
/// ```
#[default(false)]
pub cross: bool,
@@ -54,7 +63,8 @@ pub struct CancelElem {
/// [line's documentation]($func/line.angle) for more details.
///
/// ```example
- /// $ cancel(x, rotation: #30deg) $
+ /// >>> #set page(width: 140pt)
+ /// $ cancel(Pi, rotation: #30deg) $
/// ```
#[default(Angle::zero())]
pub rotation: Angle,
@@ -63,7 +73,15 @@ pub struct CancelElem {
/// [line's documentation]($func/line.stroke) for more details.
///
/// ```example
- /// $ cancel(x, stroke: #{red + 1.5pt}) $
+ /// >>> #set page(width: 140pt)
+ /// $ cancel(
+ /// sum x,
+ /// stroke: #(
+ /// paint: red,
+ /// thickness: 1.5pt,
+ /// dash: "dashed",
+ /// ),
+ /// ) $
/// ```
#[resolve]
#[fold]
diff --git a/library/src/meta/reference.rs b/library/src/meta/reference.rs
index 29b28828..68837b89 100644
--- a/library/src/meta/reference.rs
+++ b/library/src/meta/reference.rs
@@ -19,7 +19,7 @@ use crate::text::TextElem;
/// If you just want to link to a labelled element and not get an automatic
/// textual reference, consider using the [`link`]($func/link) function instead.
///
-/// # Example
+/// ## Example
/// ```example
/// #set heading(numbering: "1.")
/// #set math.equation(numbering: "(1)")
@@ -51,6 +51,36 @@ use crate::text::TextElem;
/// To customize the supplement, add content in square brackets after the
/// reference: `[@intro[Chapter]]`.
///
+/// ## Customization
+/// If you write a show rule for references, you can access the referenced
+/// element through the `element` field of the reference. The `element` may
+/// be `{none}` even if it exists if Typst hasn't discovered it yet, so you
+/// always need to handle that case in your code.
+///
+/// ```example
+/// #set heading(numbering: "1.")
+/// #set math.equation(numbering: "(1)")
+///
+/// #show ref: it => {
+/// let eq = math.equation
+/// let el = it.element
+/// if el != none and el.func() == eq {
+/// // Override equation references.
+/// numbering(
+/// el.numbering,
+/// ..counter(eq).at(el.location())
+/// )
+/// } else {
+/// // Other references as usual.
+/// it
+/// }
+/// }
+///
+/// = Beginnings <beginning>
+/// In @beginning we prove @pythagoras.
+/// $ a^2 + b^2 = c^2 $ <pythagoras>
+/// ```
+///
/// Display: Reference
/// Category: meta
#[element(Synthesize, Locatable, Show)]
@@ -86,35 +116,7 @@ pub struct RefElem {
#[synthesized]
pub citation: Option<CiteElem>,
- /// Content of the element, it should be referable.
- ///
- /// ```example
- /// #set heading(numbering: (..nums) => {
- /// nums.pos().map(str).join(".")
- /// }, supplement: [Chapt])
- ///
- /// #show ref: it => {
- /// if it.has("element") and it.element.func() == heading {
- /// let element = it.element
- /// "["
- /// element.supplement
- /// "-"
- /// numbering(element.numbering, ..counter(heading).at(element.location()))
- /// "]"
- /// } else {
- /// it
- /// }
- /// }
- ///
- /// = Introduction <intro>
- /// = Summary <sum>
- /// == Subsection <sub>
- /// @intro
- ///
- /// @sum
- ///
- /// @sub
- /// ```
+ /// The referenced element.
#[synthesized]
pub element: Option<Content>,
}
@@ -123,22 +125,14 @@ impl Synthesize for RefElem {
fn synthesize(&mut self, vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
let citation = self.to_citation(vt, styles)?;
self.push_citation(Some(citation));
+ self.push_element(None);
- if !vt.introspector.init() {
- self.push_element(None);
- return Ok(());
- }
-
- // find the element content
let target = self.target();
- let elem = vt.introspector.query_label(&self.target());
- // not in bibliography, but in document, then push the element
- if let (false, Ok(elem)) =
- (BibliographyElem::has(vt, &target.0), elem.at(self.span()))
- {
- self.push_element(Some(elem));
- } else {
- self.push_element(None);
+ if vt.introspector.init() && !BibliographyElem::has(vt, &target.0) {
+ if let Ok(elem) = vt.introspector.query_label(&target) {
+ self.push_element(Some(elem));
+ return Ok(());
+ }
}
Ok(())
diff --git a/library/src/text/raw.rs b/library/src/text/raw.rs
index 1c216483..091026b1 100644
--- a/library/src/text/raw.rs
+++ b/library/src/text/raw.rs
@@ -9,19 +9,11 @@ use crate::layout::BlockElem;
use crate::meta::{Figurable, LocalName};
use crate::prelude::*;
-/// Raw text with optional syntax highlighting.
+/// Raw text with optionalw syntax highlighting.
///
/// Displays the text verbatim and in a monospace font. This is typically used
/// to embed computer code into your document.
///
-/// ## Syntax
-/// This function also has dedicated syntax. You can enclose text in 1 or 3+
-/// backticks (`` ` ``) to make it raw. Two backticks produce empty raw text.
-/// When you use three or more backticks, you can additionally specify a
-/// language tag for syntax highlighting directly after the opening backticks.
-/// Within raw blocks, everything is rendered as is, in particular, there are no
-/// escape sequences.
-///
/// ## Example
/// ````example
/// Adding `rbx` to `rcx` gives
@@ -34,6 +26,14 @@ use crate::prelude::*;
/// ```
/// ````
///
+/// ## Syntax
+/// This function also has dedicated syntax. You can enclose text in 1 or 3+
+/// backticks (`` ` ``) to make it raw. Two backticks produce empty raw text.
+/// When you use three or more backticks, you can additionally specify a
+/// language tag for syntax highlighting directly after the opening backticks.
+/// Within raw blocks, everything is rendered as is, in particular, there are no
+/// escape sequences.
+///
/// Display: Raw Text / Code
/// Category: text
#[element(Synthesize, Show, Finalize, LocalName, Figurable)]
diff --git a/library/src/visualize/line.rs b/library/src/visualize/line.rs
index 0555ddec..4a1cb87c 100644
--- a/library/src/visualize/line.rs
+++ b/library/src/visualize/line.rs
@@ -5,8 +5,13 @@ use crate::prelude::*;
/// ## Example
/// ```example
/// #set page(height: 100pt)
+///
/// #line(length: 100%)
/// #line(end: (50%, 50%))
+/// #line(
+/// length: 4cm,
+/// stroke: 2pt + maroon,
+/// )
/// ```
///
/// Display: Line
@@ -41,30 +46,37 @@ pub struct LineElem {
/// - A stroke combined from color and thickness using the `+` operator as
/// in `{2pt + red}`.
/// - A stroke described by a dictionary with any of the following keys:
- /// - `color`: the color to use for the stroke
- /// - `thickness`: the stroke's thickness
- /// - `cap`: one of `"butt"`, `"round"` or `"square"`, the line cap of the stroke
- /// - `join`: one of `"miter"`, `"round"` or `"bevel"`, the line join of the stroke
- /// - `miter-limit`: the miter limit to use if `join` is `"miter"`, defaults to 4.0
- /// - `dash`: the dash pattern to use. Can be any of the following:
- /// - One of the strings `"solid"`, `"dotted"`, `"densely-dotted"`, `"loosely-dotted"`,
- /// `"dashed"`, `"densely-dashed"`, `"loosely-dashed"`, `"dashdotted"`,
- /// `"densely-dashdotted"` or `"loosely-dashdotted"`
- /// - An array with elements that specify the lengths of dashes and gaps, alternating.
- /// Elements can also be the string `"dot"` for a length equal to the line thickness.
- /// - A dict with the keys `array`, same as the array above, and `phase`, the offset to
- /// the start of the first dash.
- ///
+ /// - `paint`: The [color]($type/color) to use for the stroke.
+ /// - `thickness`: The stroke's thickness as a [length]($type/length).
+ /// - `cap`: How the line terminates. One of `{"butt"}`, `{"round"}`, or
+ /// `{"square"}`.
+ /// - `join`: How sharp turns of a contour are rendered. One of
+ /// `{"miter"}`, `{"round"}`, or `{"bevel"}`. Not applicable to lines
+ /// but to [polygons]($func/polygon) or [paths]($func/path).
+ /// - `miter-limit`: Number at which protruding sharp angles are rendered
+ /// with a bevel instead. The higher the number, the sharper an angle
+ /// can be before it is bevelled. Only applicable if `join` is
+ /// `{"miter"}`. Defaults to `{4.0}`.
+ /// - `dash`: The dash pattern to use. Can be any of the following:
+ /// - One of the predefined patterns `{"solid"}`, `{"dotted"}`,
+ /// `{"densely-dotted"}`, `{"loosely-dotted"}`, `{"dashed"}`,
+ /// `{"densely-dashed"}`, `{"loosely-dashed"}`, `{"dash-dotted"}`,
+ /// `{"densely-dash-dotted"}` or `{"loosely-dash-dotted"}`
+ /// - An [array]($type/array) with alternating lengths for dashes and
+ /// gaps. You can also use the string `{"dot"}` for a length equal to
+ /// the line thickness.
+ /// - A [dictionary]($type/dictionary) with the keys `array` (same as
+ /// the array above), and `phase` (of type [length]($type/length)),
+ /// which defines where in the pattern to start drawing.
///
/// ```example
+ /// #set line(length: 100%)
/// #stack(
- /// line(length: 100%, stroke: 2pt + red),
- /// v(1em),
- /// line(length: 100%, stroke: (color: blue, thickness: 4pt, cap: "round")),
- /// v(1em),
- /// line(length: 100%, stroke: (color: blue, thickness: 1pt, dash: "dashed")),
- /// v(1em),
- /// line(length: 100%, stroke: (color: blue, thickness: 1pt, dash: ("dot", 2pt, 4pt, 2pt))),
+ /// spacing: 1em,
+ /// line(stroke: 2pt + red),
+ /// line(stroke: (paint: blue, thickness: 4pt, cap: "round")),
+ /// line(stroke: (paint: blue, thickness: 1pt, dash: "dashed")),
+ /// line(stroke: (paint: blue, thickness: 1pt, dash: ("dot", 2pt, 4pt, 2pt))),
/// )
/// ```
#[resolve]
diff --git a/library/src/visualize/shape.rs b/library/src/visualize/shape.rs
index 27ef0e3f..75f03f67 100644
--- a/library/src/visualize/shape.rs
+++ b/library/src/visualize/shape.rs
@@ -55,8 +55,8 @@ pub struct RectElem {
/// - `miter-limit`: the miter limit to use if `join` is `"miter"`, defaults to 4.0
/// - `dash`: the dash pattern to use. Can be any of the following:
/// - One of the strings `"solid"`, `"dotted"`, `"densely-dotted"`, `"loosely-dotted"`,
- /// `"dashed"`, `"densely-dashed"`, `"loosely-dashed"`, `"dashdotted"`,
- /// `"densely-dashdotted"` or `"loosely-dashdotted"`
+ /// `"dashed"`, `"densely-dashed"`, `"loosely-dashed"`, `"dash-dotted"`,
+ /// `"densely-dash-dotted"` or `"loosely-dash-dotted"`
/// - An array with elements that specify the lengths of dashes and gaps, alternating.
/// Elements can also be the string `"dot"` for a length equal to the line thickness.
/// - A dict with the keys `array`, same as the array above, and `phase`, the offset to
diff --git a/macros/Cargo.toml b/macros/Cargo.toml
index 337c03a6..a1cd0c27 100644
--- a/macros/Cargo.toml
+++ b/macros/Cargo.toml
@@ -16,7 +16,7 @@ doctest = false
bench = false
[dependencies]
+heck = "0.4"
proc-macro2 = "1"
quote = "1"
syn = { version = "1", features = ["full", "extra-traits"] }
-heck = "0.4"
diff --git a/src/eval/library.rs b/src/eval/library.rs
index a92d8bd1..13825d7e 100644
--- a/src/eval/library.rs
+++ b/src/eval/library.rs
@@ -91,13 +91,13 @@ pub struct LangItems {
pub math_attach: fn(
base: Content,
// Positioned smartly.
- top: Option<Content>,
- bottom: Option<Content>,
+ t: Option<Content>,
+ b: Option<Content>,
// Fixed positions.
- topleft: Option<Content>,
- bottomleft: Option<Content>,
- topright: Option<Content>,
- bottomright: Option<Content>,
+ tl: Option<Content>,
+ bl: Option<Content>,
+ tr: Option<Content>,
+ br: Option<Content>,
) -> Content,
/// A base with an accent: `arrow(x)`.
pub math_accent: fn(base: Content, accent: char) -> Content,
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index e8e1af51..68163bf6 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -48,8 +48,9 @@ use unicode_segmentation::UnicodeSegmentation;
use crate::diag::{
bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint,
};
+use crate::model::ShowableSelector;
use crate::model::{
- Content, Introspector, Label, Recipe, Selector, StabilityProvider, Styles, Transform,
+ Content, Introspector, Label, Recipe, StabilityProvider, Styles, Transform,
Unlabellable, Vt,
};
use crate::syntax::ast::AstNode;
@@ -1428,8 +1429,9 @@ impl Eval for ast::ShowRule {
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
let selector = self
.selector()
- .map(|sel| sel.eval(vm)?.cast::<Selector>().at(sel.span()))
- .transpose()?;
+ .map(|sel| sel.eval(vm)?.cast::<ShowableSelector>().at(sel.span()))
+ .transpose()?
+ .map(|selector| selector.0);
let transform = self.transform();
let span = transform.span();
diff --git a/src/geom/stroke.rs b/src/geom/stroke.rs
index 344da3c5..ead30cbb 100644
--- a/src/geom/stroke.rs
+++ b/src/geom/stroke.rs
@@ -256,9 +256,9 @@ cast_from_value! {
"dashed" => vec![Abs::pt(3.0).into(), Abs::pt(3.0).into()].into(),
"densely-dashed" => vec![Abs::pt(3.0).into(), Abs::pt(2.0).into()].into(),
"loosely-dashed" => vec![Abs::pt(3.0).into(), Abs::pt(6.0).into()].into(),
- "dashdotted" => vec![Abs::pt(3.0).into(), Abs::pt(2.0).into(), DashLength::LineWidth, Abs::pt(2.0).into()].into(),
- "densely-dashdotted" => vec![Abs::pt(3.0).into(), Abs::pt(1.0).into(), DashLength::LineWidth, Abs::pt(1.0).into()].into(),
- "loosely-dashdotted" => vec![Abs::pt(3.0).into(), Abs::pt(4.0).into(), DashLength::LineWidth, Abs::pt(4.0).into()].into(),
+ "dash-dotted" => vec![Abs::pt(3.0).into(), Abs::pt(2.0).into(), DashLength::LineWidth, Abs::pt(2.0).into()].into(),
+ "densely-dash-dotted" => vec![Abs::pt(3.0).into(), Abs::pt(1.0).into(), DashLength::LineWidth, Abs::pt(1.0).into()].into(),
+ "loosely-dash-dotted" => vec![Abs::pt(3.0).into(), Abs::pt(4.0).into(), DashLength::LineWidth, Abs::pt(4.0).into()].into(),
array: Vec<DashLength> => {
Self {
array,
@@ -314,14 +314,14 @@ cast_from_value! {
.transpose()?.map(Smart::Custom).unwrap_or(Smart::Auto))
}
- let paint = take::<Paint>(&mut dict, "color")?;
+ let paint = take::<Paint>(&mut dict, "paint")?;
let thickness = take::<Length>(&mut dict, "thickness")?;
let line_cap = take::<LineCap>(&mut dict, "cap")?;
let line_join = take::<LineJoin>(&mut dict, "join")?;
let dash_pattern = take::<Option<DashPattern>>(&mut dict, "dash")?;
let miter_limit = take::<f64>(&mut dict, "miter-limit")?;
- dict.finish(&["color", "thickness", "cap", "join", "dash", "miter-limit"])?;
+ dict.finish(&["paint", "thickness", "cap", "join", "dash", "miter-limit"])?;
Self {
paint,
diff --git a/src/model/content.rs b/src/model/content.rs
index c2c70f9d..4af4e655 100644
--- a/src/model/content.rs
+++ b/src/model/content.rs
@@ -584,7 +584,6 @@ impl Fold for Vec<Meta> {
/// The missing key access error message.
#[cold]
-#[track_caller]
fn missing_field(key: &str) -> EcoString {
eco_format!("content does not contain field {:?}", Str::from(key))
}
diff --git a/src/model/styles.rs b/src/model/styles.rs
index 9ef74276..6757ed5d 100644
--- a/src/model/styles.rs
+++ b/src/model/styles.rs
@@ -412,9 +412,7 @@ impl Selector {
Self::Or(selectors) => selectors.iter().any(move |sel| sel.matches(target)),
Self::And(selectors) => selectors.iter().all(move |sel| sel.matches(target)),
Self::Location(location) => target.location() == Some(*location),
- Self::Before { .. } | Self::After { .. } => {
- panic!("Cannot match a `Selector::Before` or `Selector::After` selector")
- }
+ Self::Before { .. } | Self::After { .. } => false,
}
}
}
@@ -491,7 +489,7 @@ impl Cast for LocatableSelector {
fn cast(value: Value) -> StrResult<Self> {
fn validate(selector: &Selector) -> StrResult<()> {
- match &selector {
+ match selector {
Selector::Elem(elem, _) => {
if !elem.can::<dyn Locatable>() {
Err(eco_format!("{} is not locatable", elem.name()))?
@@ -533,6 +531,56 @@ impl Cast for LocatableSelector {
])
}
}
+
+/// A selector that can be used with show rules.
+#[derive(Clone, PartialEq, Hash)]
+pub struct ShowableSelector(pub Selector);
+
+impl Cast for ShowableSelector {
+ fn is(value: &Value) -> bool {
+ matches!(value, Value::Str(_) | Value::Label(_) | Value::Func(_))
+ || value.type_name() == "regular expression"
+ || value.type_name() == "selector"
+ }
+
+ fn cast(value: Value) -> StrResult<Self> {
+ fn validate(selector: &Selector) -> StrResult<()> {
+ match selector {
+ Selector::Elem(_, _) => {}
+ Selector::Label(_) => {}
+ Selector::Regex(_) => {}
+ Selector::Or(_)
+ | Selector::And(_)
+ | Selector::Location(_)
+ | Selector::Can(_)
+ | Selector::Before { .. }
+ | Selector::After { .. } => {
+ Err("this selector cannot be used with show")?
+ }
+ }
+ Ok(())
+ }
+
+ if !Self::is(&value) {
+ return <Self as Cast>::error(value);
+ }
+
+ let selector = Selector::cast(value)?;
+ validate(&selector)?;
+ Ok(Self(selector))
+ }
+
+ fn describe() -> CastInfo {
+ CastInfo::Union(vec![
+ CastInfo::Type("function"),
+ CastInfo::Type("label"),
+ CastInfo::Type("string"),
+ CastInfo::Type("regular expression"),
+ CastInfo::Type("selector"),
+ ])
+ }
+}
+
/// A show rule transformation that can be applied to a match.
#[derive(Clone, PartialEq, Hash)]
pub enum Transform {
diff --git a/src/syntax/parser.rs b/src/syntax/parser.rs
index ab73479e..1198774b 100644
--- a/src/syntax/parser.rs
+++ b/src/syntax/parser.rs
@@ -850,12 +850,19 @@ fn item(p: &mut Parser, keyed: bool) -> SyntaxKind {
return SyntaxKind::Spread;
}
- if !p.eat_if(SyntaxKind::Underscore) {
- code_expr_or_pattern(p);
- } else {
- return SyntaxKind::Underscore;
+ if p.at(SyntaxKind::Underscore) {
+ // This is a temporary workaround to fix `v.map(_ => {})`.
+ let mut lexer = p.lexer.clone();
+ let next =
+ std::iter::from_fn(|| Some(lexer.next())).find(|kind| !kind.is_trivia());
+ if next != Some(SyntaxKind::Arrow) {
+ p.eat();
+ return SyntaxKind::Underscore;
+ }
}
+ code_expr_or_pattern(p);
+
if !p.eat_if(SyntaxKind::Colon) {
return SyntaxKind::Int;
}
diff --git a/tests/typ/bugs/args-underscore.typ b/tests/typ/bugs/args-underscore.typ
new file mode 100644
index 00000000..ca3c0ff8
--- /dev/null
+++ b/tests/typ/bugs/args-underscore.typ
@@ -0,0 +1,5 @@
+// Test that lone underscore works.
+// Ref: false
+
+---
+#test((1, 2, 3).map(_ => {}).len(), 3)
diff --git a/tests/typ/compiler/show-node.typ b/tests/typ/compiler/show-node.typ
index 42aee506..99a4364e 100644
--- a/tests/typ/compiler/show-node.typ
+++ b/tests/typ/compiler/show-node.typ
@@ -96,7 +96,7 @@ Hey
= Heading
---
-// Error: 7-10 expected function, label, string, regular expression, location, or selector, found color
+// Error: 7-10 expected function, label, string, regular expression, or selector, found color
#show red: []
---
diff --git a/tests/typ/compiler/show-selector.typ b/tests/typ/compiler/show-selector.typ
index d1229eee..19f43fee 100644
--- a/tests/typ/compiler/show-selector.typ
+++ b/tests/typ/compiler/show-selector.typ
@@ -37,3 +37,7 @@ the ```rs &mut T``` reference.
= Red
== Blue
=== Green
+
+---
+// Error: 7-35 this selector cannot be used with show
+#show selector(heading).or(strong): none
diff --git a/tests/typ/compute/data.typ b/tests/typ/compute/data.typ
index 8b50f7c4..96b655c0 100644
--- a/tests/typ/compute/data.typ
+++ b/tests/typ/compute/data.typ
@@ -43,7 +43,7 @@
---
// Test reading TOML data.
-#let data = toml("/toml_types.toml")
+#let data = toml("/toml-types.toml")
#test(data.string, "wonderful")
#test(data.integer, 42)
#test(data.float, 3.14)
@@ -60,7 +60,7 @@
---
// Test reading YAML data
-#let data = yaml("/yamltypes.yaml")
+#let data = yaml("/yaml-types.yaml")
#test(data.len(), 7)
#test(data.null_key, (none, none))
#test(data.string, "text")
diff --git a/tests/typ/math/matrix.typ b/tests/typ/math/matrix.typ
index c1fba627..514827c3 100644
--- a/tests/typ/math/matrix.typ
+++ b/tests/typ/math/matrix.typ
@@ -23,10 +23,10 @@ $ mat(
---
// Test baseline alignment.
$ mat(
- a, b^2;
- sum_(x \ y) x, a^(1/2);
- zeta, alpha;
- ) $
+ a, b^2;
+ sum_(x \ y) x, a^(1/2);
+ zeta, alpha;
+) $
---
// Test alternative delimiter with set rule.
diff --git a/tests/typ/meta/query-before-after.typ b/tests/typ/meta/query-before-after.typ
index 734e84b4..7488a2d5 100644
--- a/tests/typ/meta/query-before-after.typ
+++ b/tests/typ/meta/query-before-after.typ
@@ -11,18 +11,18 @@
#set text(size: 12pt, weight: "regular")
#outline(
- title: "Chapter outline",
- indent: true,
- target: heading
- .where(level: 1)
- .or(heading.where(level: 2))
- .after(it.location(), inclusive: true)
- .before(
- heading
- .where(level: 1, outlined: true)
- .after(it.location(), inclusive: false),
- inclusive: false,
- )
+ title: "Chapter outline",
+ indent: true,
+ target: heading
+ .where(level: 1)
+ .or(heading.where(level: 2))
+ .after(it.location(), inclusive: true)
+ .before(
+ heading
+ .where(level: 1, outlined: true)
+ .after(it.location(), inclusive: false),
+ inclusive: false,
+ )
)
]
diff --git a/tests/typ/visualize/stroke.typ b/tests/typ/visualize/stroke.typ
index 60733d78..844cd8c2 100644
--- a/tests/typ/visualize/stroke.typ
+++ b/tests/typ/visualize/stroke.typ
@@ -2,22 +2,19 @@
---
// Some simple test lines
-
#line(length: 60pt, stroke: red)
#v(3pt)
#line(length: 60pt, stroke: 2pt)
#v(3pt)
#line(length: 60pt, stroke: blue + 1.5pt)
#v(3pt)
-#line(length: 60pt, stroke: (color: red, thickness: 1pt, dash: "dashed"))
+#line(length: 60pt, stroke: (paint: red, thickness: 1pt, dash: "dashed"))
#v(3pt)
-#line(length: 60pt, stroke: (color: red, thickness: 4pt, cap: "round"))
+#line(length: 60pt, stroke: (paint: red, thickness: 4pt, cap: "round"))
---
// Set rules with stroke
-
-#set line(stroke: (color: red, thickness: 1pt, cap: "butt", dash: "dashdotted"))
-
+#set line(stroke: (paint: red, thickness: 1pt, cap: "butt", dash: "dash-dotted"))
#line(length: 60pt)
#v(3pt)
#line(length: 60pt, stroke: blue)
@@ -26,79 +23,78 @@
---
// Rectangle strokes
-
#rect(width: 20pt, height: 20pt, stroke: red)
#v(3pt)
-#rect(width: 20pt, height: 20pt, stroke: (rest: red, top: (color: blue, dash: "dashed")))
+#rect(width: 20pt, height: 20pt, stroke: (rest: red, top: (paint: blue, dash: "dashed")))
#v(3pt)
#rect(width: 20pt, height: 20pt, stroke: (thickness: 5pt, join: "round"))
---
// Dashing
-#line(length: 60pt, stroke: (color: red, thickness: 1pt, dash: ("dot", 1pt)))
+#line(length: 60pt, stroke: (paint: red, thickness: 1pt, dash: ("dot", 1pt)))
#v(3pt)
-#line(length: 60pt, stroke: (color: red, thickness: 1pt, dash: ("dot", 1pt, 4pt, 2pt)))
+#line(length: 60pt, stroke: (paint: red, thickness: 1pt, dash: ("dot", 1pt, 4pt, 2pt)))
#v(3pt)
-#line(length: 60pt, stroke: (color: red, thickness: 1pt, dash: (array: ("dot", 1pt, 4pt, 2pt), phase: 5pt)))
+#line(length: 60pt, stroke: (paint: red, thickness: 1pt, dash: (array: ("dot", 1pt, 4pt, 2pt), phase: 5pt)))
#v(3pt)
-#line(length: 60pt, stroke: (color: red, thickness: 1pt, dash: ()))
+#line(length: 60pt, stroke: (paint: red, thickness: 1pt, dash: ()))
#v(3pt)
-#line(length: 60pt, stroke: (color: red, thickness: 1pt, dash: (1pt, 3pt, 9pt)))
+#line(length: 60pt, stroke: (paint: red, thickness: 1pt, dash: (1pt, 3pt, 9pt)))
---
// Line joins
-#stack(dir: ltr,
- polygon(stroke: (thickness: 4pt, color: blue, join: "round"),
+#stack(
+ dir: ltr,
+ spacing: 1em,
+ polygon(stroke: (thickness: 4pt, paint: blue, join: "round"),
(0pt, 20pt), (15pt, 0pt), (0pt, 40pt), (15pt, 45pt)),
- h(1em),
- polygon(stroke: (thickness: 4pt, color: blue, join: "bevel"),
+ polygon(stroke: (thickness: 4pt, paint: blue, join: "bevel"),
(0pt, 20pt), (15pt, 0pt), (0pt, 40pt), (15pt, 45pt)),
- h(1em),
- polygon(stroke: (thickness: 4pt, color: blue, join: "miter"),
+ polygon(stroke: (thickness: 4pt, paint: blue, join: "miter"),
(0pt, 20pt), (15pt, 0pt), (0pt, 40pt), (15pt, 45pt)),
- h(1em),
- polygon(stroke: (thickness: 4pt, color: blue, join: "miter", miter-limit: 20.0),
+ polygon(stroke: (thickness: 4pt, paint: blue, join: "miter", miter-limit: 20.0),
(0pt, 20pt), (15pt, 0pt), (0pt, 40pt), (15pt, 45pt)),
)
---
-// Error: 29-56 unexpected key "thicknes", valid keys are "color", "thickness", "cap", "join", "dash", and "miter-limit"
-#line(length: 60pt, stroke: (color: red, thicknes: 1pt))
+// Error: 29-56 unexpected key "thicknes", valid keys are "paint", "thickness", "cap", "join", "dash", and "miter-limit"
+#line(length: 60pt, stroke: (paint: red, thicknes: 1pt))
---
-// Error: 29-55 expected "solid", "dotted", "densely-dotted", "loosely-dotted", "dashed", "densely-dashed", "loosely-dashed", "dashdotted", "densely-dashdotted", "loosely-dashdotted", array, dictionary, dash pattern, or none
-#line(length: 60pt, stroke: (color: red, dash: "dash"))
+// Error: 29-55 expected "solid", "dotted", "densely-dotted", "loosely-dotted", "dashed", "densely-dashed", "loosely-dashed", "dash-dotted", "densely-dash-dotted", "loosely-dash-dotted", array, dictionary, dash pattern, or none
+#line(length: 60pt, stroke: (paint: red, dash: "dash"))
---
// 0pt strokes must function exactly like 'none' strokes and not draw anything
+
#rect(width: 10pt, height: 10pt, stroke: none)
#rect(width: 10pt, height: 10pt, stroke: 0pt)
-
#rect(width: 10pt, height: 10pt, stroke: none, fill: blue)
#rect(width: 10pt, height: 10pt, stroke: 0pt + red, fill: blue)
#line(length: 30pt, stroke: 0pt)
-#line(length: 30pt, stroke: (color: red, thickness: 0pt, dash: ("dot", 1pt)))
+#line(length: 30pt, stroke: (paint: red, thickness: 0pt, dash: ("dot", 1pt)))
#table(columns: 2, stroke: none)[A][B]
#table(columns: 2, stroke: 0pt)[A][B]
#path(
- fill: red,
- stroke: none,
- closed: true,
- ((0%, 0%), (4%, -4%)),
- ((50%, 50%), (4%, -4%)),
- ((0%, 50%), (4%, 4%)),
- ((50%, 0%), (4%, 4%)),
+ fill: red,
+ stroke: none,
+ closed: true,
+ ((0%, 0%), (4%, -4%)),
+ ((50%, 50%), (4%, -4%)),
+ ((0%, 50%), (4%, 4%)),
+ ((50%, 0%), (4%, 4%)),
)
+
#path(
- fill: red,
- stroke: 0pt,
- closed: true,
- ((0%, 0%), (4%, -4%)),
- ((50%, 50%), (4%, -4%)),
- ((0%, 50%), (4%, 4%)),
- ((50%, 0%), (4%, 4%)),
+ fill: red,
+ stroke: 0pt,
+ closed: true,
+ ((0%, 0%), (4%, -4%)),
+ ((50%, 50%), (4%, -4%)),
+ ((0%, 50%), (4%, 4%)),
+ ((50%, 0%), (4%, 4%)),
)