Compare commits

...

89 Commits

Author SHA1 Message Date
Tobias Schmitz
1358c05a55
Merge de72040ce6e55aa6949f160c6d711cf6f3057c70 into 78355421ad73fdcbe93b4acca890b439c4b6f98d 2025-07-23 07:29:59 +00:00
Tobias Schmitz
de72040ce6
Merge branch 'main' into pdf-accessibility 2025-07-22 23:31:41 +02:00
Laurenz
78355421ad
Add pdf extension to image autocompletions (#6643) 2025-07-22 12:07:29 +00:00
Laurenz Stampfl
af2253ba16
Add support for PDF embedding (#6623)
Co-authored-by: Laurenz <laurmaedje@gmail.com>
2025-07-22 11:06:44 +00:00
Laurenz
b036fd97ab Reformat with 2024 edition 2025-07-21 15:22:29 +02:00
Laurenz
e81a5a6ef2 Yeet if_chain macro 2025-07-21 15:22:29 +02:00
Laurenz
c9c2315ad3 Fix 2024 clippy warnings 2025-07-21 15:22:29 +02:00
Laurenz
4bbd4e195b Migrate to 2024 edition 2025-07-21 15:22:28 +02:00
Laurenz
eed75ca4d6 Bump MSRV to 1.88 2025-07-21 15:22:28 +02:00
Laurenz
a43b7e785c Bump CI Rust to 1.88 2025-07-21 13:48:48 +02:00
Tobias Schmitz
9649def108
feat: add alt parameter to math.equation 2025-07-18 16:38:54 +02:00
Tobias Schmitz
d2105dcc35
feat: report spans for missing alt text and unknown/duplicate tag ids 2025-07-18 16:38:54 +02:00
Tobias Schmitz
99815f449c
feat: best effort link alt text generation 2025-07-18 12:01:54 +02:00
Tobias Schmitz
79423f3033
refactor: revert some changes to main 2025-07-17 17:33:27 +02:00
Tobias Schmitz
f8f900d40b
feat: update krilla
bounding boxes for links are now automatically generated by krilla
2025-07-17 16:52:37 +02:00
Tobias Schmitz
0bd0dc6d92
feat: generate tags for bibliographies 2025-07-17 16:12:10 +02:00
Tobias Schmitz
8d2c8712d5
feat: wrap equations in Formula tags 2025-07-16 16:34:42 +02:00
Tobias Schmitz
66ca4dc9a0
feat: generate tags for quotes 2025-07-16 14:54:58 +02:00
Tobias Schmitz
bc09df0c8b
feat: insert footnotes after the reference in the reading order 2025-07-16 14:52:20 +02:00
Tobias Schmitz
bf75ab858d
feat: better alt text for footnote links 2025-07-16 14:04:22 +02:00
Tobias Schmitz
153c5d3a4a
refactor: update krilla 2025-07-16 13:58:04 +02:00
Tobias Schmitz
39a2c31169
Merge branch 'main' into pdf-accessibility 2025-07-16 12:59:06 +02:00
Tobias Schmitz
cd5d91a82d
fix: ensure link annotation object references are direct children of link tags 2025-07-15 17:23:11 +02:00
Tobias Schmitz
728d37efa0
feat: generate tags for footnotes 2025-07-15 16:54:15 +02:00
Tobias Schmitz
451b0815ff
feat: mark numbering prefix of heading and outline as Lbl 2025-07-14 17:11:02 +02:00
Tobias Schmitz
0df9da7ce6
feat: generate tags for terms 2025-07-14 16:37:16 +02:00
Tobias Schmitz
4b57373653
feat: derive Debug for StackEntry 2025-07-14 13:18:43 +02:00
Tobias Schmitz
e43b8bbb7f
fix: out of bounds access when tagging table cells 2025-07-14 12:40:18 +02:00
Tobias Schmitz
9bbfe4c14a
fix: make figure captions sibling elements
if the caption is contained within the figure screen readers might ignore it
2025-07-14 10:46:00 +02:00
Tobias Schmitz
e4021390a3
feat: don't wrap table cell content in paragraph 2025-07-14 10:46:00 +02:00
Tobias Schmitz
2621c6416e
feat!: revert making some elements Locatable 2025-07-13 18:06:03 +02:00
Tobias Schmitz
e5e5fba418
fix: revert making math elements Locatable 2025-07-13 17:53:55 +02:00
Tobias Schmitz
3c46056599
fix: public outline entry.inner() function 2025-07-13 17:46:55 +02:00
Tobias Schmitz
b5c6f7132b
feat!: remove unfinished manual tagging code for now 2025-07-13 17:38:19 +02:00
Tobias Schmitz
484f633e27
chore: remove left over file from merge 2025-07-13 17:30:37 +02:00
Tobias Schmitz
eb9a3359d5
feat: generate tags for lists and enums 2025-07-13 17:27:02 +02:00
Tobias Schmitz
a495724813
feat: mark all shapes as artifacts 2025-07-12 20:02:28 +02:00
Tobias Schmitz
e3c0855a2b
fix: update krilla 2025-07-12 14:15:13 +02:00
Tobias Schmitz
e0074d6e39
refactor: make TableCell::kind #[parse] instead of #[synthesized] 2025-07-11 14:45:45 +02:00
Tobias Schmitz
e8af101a79
Merge branch 'main' into pdf-accessibility 2025-07-11 14:13:34 +02:00
Tobias Schmitz
0a0830ff93
fix: update krilla 2025-07-10 15:41:31 +02:00
Tobias Schmitz
df10cb8570
feat: default to the url if no alt text is specified for a link 2025-07-09 10:18:48 +02:00
Tobias Schmitz
08719237c2
feat!: for now don't generate paragraphs 2025-07-09 10:18:48 +02:00
Tobias Schmitz
8998676acb
feat: group artifacts
span one artifact tag across all content inside an artifact
2025-07-09 10:18:39 +02:00
Tobias Schmitz
0c09c7d666
Merge branch 'main' into pdf-accessibility 2025-07-08 14:57:51 +02:00
Tobias Schmitz
edd213074f
refactor: remove general api to set cell kind and add pdf.(header|data)-cell 2025-07-08 14:14:37 +02:00
Tobias Schmitz
070a0faf5c
fixup! test: table header id generation 2025-07-08 14:14:21 +02:00
Tobias Schmitz
2445bb4361
fix: table header hierarchy resolution 2025-07-08 11:28:35 +02:00
Tobias Schmitz
7d5b9a716f
feat: wrap table cell content in a paragraph 2025-07-07 12:30:56 +02:00
Tobias Schmitz
b0d3c2dca4
test: table header id generation 2025-07-07 12:28:53 +02:00
Tobias Schmitz
58c6729df4
feat: generate human readable table cell IDs
in almost all real-world cases these IDs require less memory than the binary IDs
used before, and they are also require less storage in PDF files, since binary
data is encoded in hex escape sequences, taking up 4 bytes per byte of data.
2025-07-07 10:52:20 +02:00
Tobias Schmitz
157e0fa142
fix: generate cell id with correct indices 2025-07-04 15:56:39 +02:00
Tobias Schmitz
4dceb7f5ef
refactor: update krilla 2025-07-04 10:37:46 +02:00
Tobias Schmitz
3d4d548934
feat: [WIP] generate alt text for ref elements 2025-07-03 18:43:30 +02:00
Tobias Schmitz
254aadccfc
docs: fix comment 2025-07-03 18:43:20 +02:00
Tobias Schmitz
8e10356234
refactor: use krilla as git dependency 2025-07-03 18:43:20 +02:00
Tobias Schmitz
7892a8c726
chore: update krilla 2025-07-03 18:43:20 +02:00
Tobias Schmitz
f324accff9
feat: generate paragraphs 2025-07-03 18:43:20 +02:00
Tobias Schmitz
0bc39338a1
fix: handle some edge cases instead of panicking 2025-07-03 18:43:20 +02:00
Tobias Schmitz
377dc87325
refactor: split up pdf tagging code into multiple modules 2025-07-03 18:43:20 +02:00
Tobias Schmitz
50cd81ee1f
feat: generate headers attribute table cells
- fix marking repeated headers/footers as artifacts
- fix table row grouping with empty cells
2025-07-03 18:43:17 +02:00
Tobias Schmitz
746926c7da
fix: ignore repeated table headers/footers in tag tree 2025-07-03 18:43:15 +02:00
Tobias Schmitz
773efb5572
fix: bug due to table cell start tags in grid layout code 2025-07-03 18:43:13 +02:00
Tobias Schmitz
3404fecd36
feat: tag table headers and footers 2025-07-03 18:43:10 +02:00
Tobias Schmitz
bfcf2bd4cc
feat: support headings with level >= 7 2025-07-03 18:43:08 +02:00
Tobias Schmitz
605681d435
refactor: move link tagging code 2025-07-03 18:43:04 +02:00
Tobias Schmitz
6ebe85d678
fix: don't include outline title in TOC hierarchy 2025-07-03 18:43:02 +02:00
Tobias Schmitz
76d09b5673
fix: only use link annotation quadpoints when exporting a PDF/UA-1 document 2025-07-03 18:43:02 +02:00
Tobias Schmitz
d6307831dd
feat: hierarchical outline tags 2025-07-03 18:42:59 +02:00
Tobias Schmitz
09b2cd6de5
docs: fixup some comments 2025-07-03 18:42:57 +02:00
Tobias Schmitz
6717a18414
feat: mark RepeatElem as artifact 2025-07-03 18:42:54 +02:00
Tobias Schmitz
612aa8fc53
fix: mark table gutter and fill as artifacts 2025-07-03 18:42:52 +02:00
Tobias Schmitz
5bd9accb9c
feat: always write alt text in marked content sequence for images 2025-07-03 18:42:52 +02:00
Tobias Schmitz
0d35ae28ad
feat: add cli args for PDF/UA-1 standard and to disable tagging 2025-07-03 18:42:46 +02:00
Tobias Schmitz
4894a227d2
refactor: revert some changes to FrameItem::Link 2025-07-03 18:42:46 +02:00
Tobias Schmitz
2d6e3b6151
refactor: derive(Cast) for ArtifactKind 2025-07-03 18:42:44 +02:00
Tobias Schmitz
e6341c0fe4
fix: avoid empty marked-content sequences 2025-07-03 18:42:41 +02:00
Tobias Schmitz
8231439b11
feat: generate tags for tables 2025-07-03 18:42:38 +02:00
Tobias Schmitz
8075f551e2
feat: use local krilla version 2025-07-03 18:42:35 +02:00
Tobias Schmitz
ac6b9d6008
feat: pdf.tag function to manually create pdf tags 2025-07-03 18:42:31 +02:00
Tobias Schmitz
00c3b62f1d
feat: write tags for more elements 2025-07-03 18:42:23 +02:00
Tobias Schmitz
6c686bd460
feat: write tags for links and use quadpoints in link annotations 2025-07-03 18:42:10 +02:00
Tobias Schmitz
9e2235dbd8
feat: pdf.artifact element 2025-07-03 18:42:10 +02:00
Tobias Schmitz
1980430578
feat: mark artifacts 2025-07-03 18:41:48 +02:00
Tobias Schmitz
cc70a785dd
feat: [WIP] allow specifying alt text for links
skip-checks:true

# Please enter the commit message for your changes. Lines starting
# with '#' will be kept; you may remove them yourself if you want to.
# An empty message aborts the commit.
#
# Date:      Wed May 28 17:47:35 2025 +0200
#
# On branch pdf-accessibility
# Your branch and 'origin/pdf-accessibility' have diverged,
# and have 11 and 5 different commits each, respectively.
#
# Changes to be committed:
#	modified:   crates/typst-ide/src/jump.rs
#	modified:   crates/typst-layout/src/flow/distribute.rs
#	modified:   crates/typst-layout/src/modifiers.rs
#	modified:   crates/typst-library/src/foundations/content.rs
#	modified:   crates/typst-library/src/layout/frame.rs
#	modified:   crates/typst-library/src/model/bibliography.rs
#	modified:   crates/typst-library/src/model/footnote.rs
#	modified:   crates/typst-library/src/model/link.rs
#	modified:   crates/typst-library/src/model/outline.rs
#	modified:   crates/typst-library/src/model/reference.rs
#	modified:   crates/typst-pdf/src/convert.rs
#	modified:   crates/typst-pdf/src/link.rs
#	modified:   crates/typst-render/src/lib.rs
#	modified:   crates/typst-svg/src/lib.rs
#	modified:   tests/src/run.rs
#
2025-07-03 15:58:07 +02:00
Tobias Schmitz
e8ea837514
feat: [WIP] include links in tag tree
skip-checks:true
2025-07-03 15:58:07 +02:00
Tobias Schmitz
c6b3b371b0
feat: [WIP] write tags
skip-checks:true
2025-07-03 15:58:07 +02:00
Tobias Schmitz
ab7eea23f1
feat: [WIP] make more things locatable
skip-checks:true
2025-07-03 15:58:07 +02:00
Tobias Schmitz
c5dbd85a81
feat: [draft] generate accessibility tag tree for headings
skip-checks:true
2025-07-03 15:58:07 +02:00
328 changed files with 3968 additions and 1793 deletions

View File

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

View File

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

157
Cargo.lock generated
View File

@ -181,9 +181,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.8.0"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
dependencies = [
"serde",
]
@ -214,9 +214,9 @@ checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06"
[[package]]
name = "bytemuck"
version = "1.21.0"
version = "1.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422"
dependencies = [
"bytemuck_derive",
]
@ -592,6 +592,12 @@ dependencies = [
"syn",
]
[[package]]
name = "diff"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "dirs"
version = "6.0.0"
@ -964,6 +970,69 @@ dependencies = [
"url",
]
[[package]]
name = "hayro"
version = "0.1.0"
source = "git+https://github.com/LaurenzV/hayro?rev=e701f95#e701f9569157a2fe4ade68930dc9e9283782dcca"
dependencies = [
"bytemuck",
"hayro-interpret",
"image",
"kurbo",
"rustc-hash",
"smallvec",
]
[[package]]
name = "hayro-font"
version = "0.1.0"
source = "git+https://github.com/LaurenzV/hayro?rev=e701f95#e701f9569157a2fe4ade68930dc9e9283782dcca"
dependencies = [
"log",
"phf",
]
[[package]]
name = "hayro-interpret"
version = "0.1.0"
source = "git+https://github.com/LaurenzV/hayro?rev=e701f95#e701f9569157a2fe4ade68930dc9e9283782dcca"
dependencies = [
"bitflags 2.9.1",
"hayro-font",
"hayro-syntax",
"kurbo",
"log",
"phf",
"qcms",
"skrifa",
"smallvec",
"yoke 0.8.0",
]
[[package]]
name = "hayro-syntax"
version = "0.0.1"
source = "git+https://github.com/LaurenzV/hayro?rev=e701f95#e701f9569157a2fe4ade68930dc9e9283782dcca"
dependencies = [
"flate2",
"kurbo",
"log",
"rustc-hash",
"smallvec",
"zune-jpeg",
]
[[package]]
name = "hayro-write"
version = "0.1.0"
source = "git+https://github.com/LaurenzV/hayro?rev=e701f95#e701f9569157a2fe4ade68930dc9e9283782dcca"
dependencies = [
"flate2",
"hayro-syntax",
"log",
"pdf-writer",
]
[[package]]
name = "heck"
version = "0.5.0"
@ -1198,17 +1267,11 @@ dependencies = [
"icu_properties",
]
[[package]]
name = "if_chain"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
[[package]]
name = "image"
version = "0.25.5"
version = "0.25.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b"
checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a"
dependencies = [
"bytemuck",
"byteorder-lite",
@ -1271,7 +1334,7 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3"
dependencies = [
"bitflags 2.8.0",
"bitflags 2.9.1",
"inotify-sys",
"libc",
]
@ -1367,7 +1430,7 @@ dependencies = [
[[package]]
name = "krilla"
version = "0.4.0"
source = "git+https://github.com/LaurenzV/krilla?rev=20c14fe#20c14fefee5002566b3d6668b338bbe2168784e7"
source = "git+https://github.com/LaurenzV/krilla?branch=main#37b9a00bfac87ed0b347b7cf8e9d37a6f68fcccd"
dependencies = [
"base64",
"bumpalo",
@ -1376,6 +1439,7 @@ dependencies = [
"float-cmp 0.10.0",
"fxhash",
"gif",
"hayro-write",
"image-webp",
"imagesize",
"once_cell",
@ -1385,6 +1449,7 @@ dependencies = [
"rustybuzz",
"siphasher",
"skrifa",
"smallvec",
"subsetter",
"tiny-skia-path",
"xmp-writer",
@ -1395,7 +1460,7 @@ dependencies = [
[[package]]
name = "krilla-svg"
version = "0.1.0"
source = "git+https://github.com/LaurenzV/krilla?rev=20c14fe#20c14fefee5002566b3d6668b338bbe2168784e7"
source = "git+https://github.com/LaurenzV/krilla?branch=main#37b9a00bfac87ed0b347b7cf8e9d37a6f68fcccd"
dependencies = [
"flate2",
"fontdb",
@ -1408,9 +1473,9 @@ dependencies = [
[[package]]
name = "kurbo"
version = "0.11.1"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89234b2cc610a7dd927ebde6b41dd1a5d4214cffaef4cf1fb2195d592f92518f"
checksum = "1077d333efea6170d9ccb96d3c3026f300ca0773da4938cc4c811daa6df68b0c"
dependencies = [
"arrayvec",
"smallvec",
@ -1462,7 +1527,7 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags 2.8.0",
"bitflags 2.9.1",
"libc",
"redox_syscall",
]
@ -1628,7 +1693,7 @@ version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943"
dependencies = [
"bitflags 2.8.0",
"bitflags 2.9.1",
"filetime",
"fsevent-sys",
"inotify",
@ -1710,7 +1775,7 @@ version = "0.10.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da"
dependencies = [
"bitflags 2.8.0",
"bitflags 2.9.1",
"cfg-if",
"foreign-types",
"libc",
@ -1847,7 +1912,7 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ea27c5015ab81753fc61e49f8cde74999346605ee148bb20008ef3d3150e0dc"
dependencies = [
"bitflags 2.8.0",
"bitflags 2.9.1",
"itoa",
"memchr",
"ryu",
@ -1981,6 +2046,16 @@ dependencies = [
"zerocopy",
]
[[package]]
name = "pretty_assertions"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
dependencies = [
"diff",
"yansi",
]
[[package]]
name = "proc-macro2"
version = "1.0.93"
@ -2005,7 +2080,7 @@ version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b"
dependencies = [
"bitflags 2.8.0",
"bitflags 2.9.1",
"getopts",
"memchr",
"unicase",
@ -2118,7 +2193,7 @@ version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
dependencies = [
"bitflags 2.8.0",
"bitflags 2.9.1",
]
[[package]]
@ -2221,7 +2296,7 @@ version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
"bitflags 2.8.0",
"bitflags 2.9.1",
"errno",
"libc",
"linux-raw-sys",
@ -2240,7 +2315,7 @@ version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702"
dependencies = [
"bitflags 2.8.0",
"bitflags 2.9.1",
"bytemuck",
"core_maths",
"log",
@ -2288,7 +2363,7 @@ version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
"bitflags 2.8.0",
"bitflags 2.9.1",
"core-foundation",
"core-foundation-sys",
"libc",
@ -2451,9 +2526,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.13.2"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "spin"
@ -2861,7 +2936,7 @@ dependencies = [
[[package]]
name = "typst-assets"
version = "0.13.1"
source = "git+https://github.com/typst/typst-assets?rev=edf0d64#edf0d648376e29738a05a933af9ea99bb81557b1"
source = "git+https://github.com/typst/typst-assets?rev=fbf00f9#fbf00f9539fdb0825bef4d39fb57d5986c51b756"
[[package]]
name = "typst-cli"
@ -2911,7 +2986,7 @@ dependencies = [
[[package]]
name = "typst-dev-assets"
version = "0.13.1"
source = "git+https://github.com/typst/typst-dev-assets?rev=bfa947f#bfa947f3433d7d13a995168c40ae788a2ebfe648"
source = "git+https://github.com/typst/typst-dev-assets?rev=c6c2acf#c6c2acf6cdc31f99a23a478d3d614f8bf806a4f5"
[[package]]
name = "typst-docs"
@ -2943,7 +3018,6 @@ version = "0.13.1"
dependencies = [
"comemo",
"ecow",
"if_chain",
"indexmap 2.7.1",
"stacker",
"toml",
@ -2991,7 +3065,6 @@ version = "0.13.1"
dependencies = [
"comemo",
"ecow",
"if_chain",
"once_cell",
"pathdiff",
"serde",
@ -3063,7 +3136,7 @@ name = "typst-library"
version = "0.13.1"
dependencies = [
"az",
"bitflags 2.8.0",
"bitflags 2.9.1",
"bumpalo",
"chinese-number",
"ciborium",
@ -3075,6 +3148,7 @@ dependencies = [
"fontdb",
"glidesort",
"hayagriva",
"hayro-syntax",
"icu_properties",
"icu_provider",
"icu_provider_blob",
@ -3135,6 +3209,7 @@ dependencies = [
name = "typst-pdf"
version = "0.13.1"
dependencies = [
"az",
"bytemuck",
"comemo",
"ecow",
@ -3142,7 +3217,9 @@ dependencies = [
"infer",
"krilla",
"krilla-svg",
"pretty_assertions",
"serde",
"smallvec",
"typst-assets",
"typst-library",
"typst-macros",
@ -3173,11 +3250,13 @@ version = "0.13.1"
dependencies = [
"bytemuck",
"comemo",
"hayro",
"image",
"pixglyph",
"resvg",
"tiny-skia",
"ttf-parser",
"typst-assets",
"typst-library",
"typst-macros",
"typst-timing",
@ -3191,8 +3270,10 @@ dependencies = [
"comemo",
"ecow",
"flate2",
"hayro",
"image",
"ttf-parser",
"typst-assets",
"typst-library",
"typst-macros",
"typst-timing",
@ -3589,7 +3670,7 @@ version = "0.221.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9845c470a2e10b61dd42c385839cdd6496363ed63b5c9e420b5488b77bd22083"
dependencies = [
"bitflags 2.8.0",
"bitflags 2.9.1",
"indexmap 2.7.1",
]
@ -3724,7 +3805,7 @@ version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
dependencies = [
"bitflags 2.8.0",
"bitflags 2.9.1",
]
[[package]]
@ -3805,6 +3886,12 @@ dependencies = [
"linked-hash-map",
]
[[package]]
name = "yansi"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
[[package]]
name = "yoke"
version = "0.7.5"

View File

@ -5,9 +5,9 @@ resolver = "2"
[workspace.package]
version = "0.13.1"
rust-version = "1.83" # also change in ci.yml
rust-version = "1.88" # also change in ci.yml
authors = ["The Typst Project Developers"]
edition = "2021"
edition = "2024"
homepage = "https://typst.app"
repository = "https://github.com/typst/typst"
license = "Apache-2.0"
@ -32,8 +32,8 @@ typst-svg = { path = "crates/typst-svg", version = "0.13.1" }
typst-syntax = { path = "crates/typst-syntax", version = "0.13.1" }
typst-timing = { path = "crates/typst-timing", version = "0.13.1" }
typst-utils = { path = "crates/typst-utils", version = "0.13.1" }
typst-assets = { git = "https://github.com/typst/typst-assets", rev = "edf0d64" }
typst-dev-assets = { git = "https://github.com/typst/typst-dev-assets", rev = "bfa947f" }
typst-assets = { git = "https://github.com/typst/typst-assets", rev = "fbf00f9" }
typst-dev-assets = { git = "https://github.com/typst/typst-dev-assets", rev = "c6c2acf" }
arrayvec = "0.7.4"
az = "1.2"
base64 = "0.22"
@ -61,6 +61,8 @@ fontdb = { version = "0.23", default-features = false }
fs_extra = "1.3"
glidesort = "0.1.2"
hayagriva = "0.8.1"
hayro-syntax = { git = "https://github.com/LaurenzV/hayro", rev = "e701f95" }
hayro = { git = "https://github.com/LaurenzV/hayro", rev = "e701f95" }
heck = "0.5"
hypher = "0.1.4"
icu_properties = { version = "1.4", features = ["serde"] }
@ -68,13 +70,12 @@ icu_provider = { version = "1.4", features = ["sync"] }
icu_provider_adapters = "1.4"
icu_provider_blob = "1.4"
icu_segmenter = { version = "1.4", features = ["serde"] }
if_chain = "1"
image = { version = "0.25.5", default-features = false, features = ["png", "jpeg", "gif", "webp"] }
indexmap = { version = "2", features = ["serde"] }
infer = { version = "0.19.0", default-features = false }
kamadak-exif = "0.6"
krilla = { git = "https://github.com/LaurenzV/krilla", rev = "20c14fe", default-features = false, features = ["raster-images", "comemo", "rayon"] }
krilla-svg = { git = "https://github.com/LaurenzV/krilla", rev = "20c14fe" }
krilla = { git = "https://github.com/LaurenzV/krilla", branch = "main", default-features = false, features = ["raster-images", "comemo", "rayon", "pdf"] }
krilla-svg = { git = "https://github.com/LaurenzV/krilla", branch = "main" }
kurbo = "0.11"
libfuzzer-sys = "0.4"
lipsum = "0.9"
@ -92,6 +93,7 @@ phf = { version = "0.11", features = ["macros"] }
pixglyph = "0.6"
png = "0.17"
portable-atomic = "1.6"
pretty_assertions = "1.4.1"
proc-macro2 = "1"
pulldown-cmark = "0.9"
qcms = "0.3.0"

View File

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

View File

@ -258,6 +258,13 @@ pub struct CompileArgs {
#[arg(long = "pdf-standard", value_delimiter = ',')]
pub pdf_standard: Vec<PdfStandard>,
/// By default, even when not producing a `PDF/UA-1` document, a tagged PDF
/// document is written to provide a baseline of accessibility. In some
/// circumstances (for example when trying to reduce the size of a document)
/// it can be desirable to disable tagged PDF.
#[arg(long = "disable-pdf-tags")]
pub disable_pdf_tags: bool,
/// The PPI (pixels per inch) to use for PNG export.
#[arg(long = "ppi", default_value_t = 144.0)]
pub ppi: f32,
@ -518,6 +525,9 @@ pub enum PdfStandard {
/// PDF/A-4e.
#[value(name = "a-4e")]
A_4e,
/// PDF/UA-1.
#[value(name = "ua-1")]
Ua_1,
}
display_possible_values!(PdfStandard);

View File

@ -10,13 +10,13 @@ use ecow::eco_format;
use parking_lot::RwLock;
use pathdiff::diff_paths;
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use typst::WorldExt;
use typst::diag::{
bail, At, Severity, SourceDiagnostic, SourceResult, StrResult, Warned,
At, Severity, SourceDiagnostic, SourceResult, StrResult, Warned, bail,
};
use typst::foundations::{Datetime, Smart};
use typst::layout::{Frame, Page, PageRanges, PagedDocument};
use typst::syntax::{FileId, Lines, Span};
use typst::WorldExt;
use typst_html::HtmlDocument;
use typst_pdf::{PdfOptions, PdfStandards, Timestamp};
@ -65,6 +65,8 @@ pub struct CompileConfig {
pub open: Option<Option<String>>,
/// A list of standards the PDF should conform to.
pub pdf_standards: PdfStandards,
/// Whether to write PDF (accessibility) tags.
pub disable_pdf_tags: bool,
/// A path to write a Makefile rule describing the current compilation.
pub make_deps: Option<PathBuf>,
/// The PPI (pixels per inch) to use for PNG export.
@ -150,6 +152,7 @@ impl CompileConfig {
output_format,
pages,
pdf_standards,
disable_pdf_tags: args.disable_pdf_tags,
creation_timestamp: args.world.creation_timestamp,
make_deps: args.make_deps.clone(),
ppi: args.ppi,
@ -291,6 +294,7 @@ fn export_pdf(document: &PagedDocument, config: &CompileConfig) -> SourceResult<
timestamp,
page_ranges: config.pages.clone(),
standards: config.pdf_standards.clone(),
disable_tags: config.disable_pdf_tags,
};
let buffer = typst_pdf::pdf(document, &options)?;
config
@ -513,7 +517,9 @@ fn write_make_deps(
})
.collect::<Result<Vec<_>, _>>()
else {
bail!("failed to create make dependencies file because output path was not valid unicode")
bail!(
"failed to create make dependencies file because output path was not valid unicode"
)
};
if output_paths.is_empty() {
bail!("failed to create make dependencies file because output was stdout")
@ -773,6 +779,7 @@ impl From<PdfStandard> for typst_pdf::PdfStandard {
PdfStandard::A_4 => typst_pdf::PdfStandard::A_4,
PdfStandard::A_4f => typst_pdf::PdfStandard::A_4f,
PdfStandard::A_4e => typst_pdf::PdfStandard::A_4e,
PdfStandard::Ua_1 => typst_pdf::PdfStandard::Ua_1,
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -407,11 +407,11 @@ fn distribute<'a>(
// If there is still something remaining, apply it to the
// last region (it will overflow, but there's nothing else
// we can do).
if !remaining.approx_empty() {
if let Some(last) = buf.last_mut() {
if !remaining.approx_empty()
&& let Some(last) = buf.last_mut()
{
*last += remaining;
}
}
// Distribute the heights to the first region and the
// backlog. There is no last region, since the height is

View File

@ -2,10 +2,11 @@ use std::cell::{LazyCell, RefCell};
use std::fmt::{self, Debug, Formatter};
use std::hash::Hash;
use bumpalo::boxed::Box as BumpBox;
use bumpalo::Bump;
use bumpalo::boxed::Box as BumpBox;
use comemo::{Track, Tracked, TrackedMut};
use typst_library::diag::{bail, warning, SourceResult};
use typst_library::World;
use typst_library::diag::{SourceResult, bail, warning};
use typst_library::engine::{Engine, Route, Sink, Traced};
use typst_library::foundations::{Packed, Resolve, Smart, StyleChain};
use typst_library::introspection::{
@ -19,10 +20,9 @@ use typst_library::layout::{
use typst_library::model::ParElem;
use typst_library::routines::{Pair, Routines};
use typst_library::text::TextElem;
use typst_library::World;
use typst_utils::SliceExt;
use super::{layout_multi_block, layout_single_block, FlowMode};
use super::{FlowMode, layout_multi_block, layout_single_block};
use crate::inline::ParSituation;
use crate::modifiers::layout_and_modify;
@ -684,11 +684,11 @@ impl<T> CachedCell<T> {
let input_hash = typst_utils::hash128(&input);
let mut slot = self.0.borrow_mut();
if let Some((hash, output)) = &*slot {
if *hash == input_hash {
if let Some((hash, output)) = &*slot
&& *hash == input_hash
{
return output.clone();
}
}
let output = f(input);
*slot = Some((input_hash, output.clone()));

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,6 +7,7 @@ use typst_library::introspection::Locator;
use typst_library::layout::grid::resolve::{Cell, CellGrid};
use typst_library::layout::{Axes, Fragment, HAlignment, Regions, Sizing, VAlignment};
use typst_library::model::{EnumElem, ListElem, Numbering, ParElem, ParbreakElem};
use typst_library::pdf::PdfMarkerTag;
use typst_library::text::TextElem;
use crate::grid::GridLayouter;
@ -24,11 +25,7 @@ pub fn layout_list(
let body_indent = elem.body_indent.get(styles);
let tight = elem.tight.get(styles);
let gutter = elem.spacing.get(styles).unwrap_or_else(|| {
if tight {
styles.get(ParElem::leading)
} else {
styles.get(ParElem::spacing)
}
if tight { styles.get(ParElem::leading) } else { styles.get(ParElem::spacing) }
});
let Depth(depth) = styles.get(ListElem::depth);
@ -48,12 +45,16 @@ pub fn layout_list(
if !tight {
body += ParbreakElem::shared();
}
let body = body.set(ListElem::depth, Depth(1));
cells.push(Cell::new(Content::empty(), locator.next(&())));
cells.push(Cell::new(marker.clone(), locator.next(&marker.span())));
cells.push(Cell::new(
PdfMarkerTag::ListItemLabel(marker.clone()),
locator.next(&marker.span()),
));
cells.push(Cell::new(Content::empty(), locator.next(&())));
cells.push(Cell::new(
body.set(ListElem::depth, Depth(1)),
PdfMarkerTag::ListItemBody(body),
locator.next(&item.body.span()),
));
}
@ -88,22 +89,15 @@ pub fn layout_enum(
let body_indent = elem.body_indent.get(styles);
let tight = elem.tight.get(styles);
let gutter = elem.spacing.get(styles).unwrap_or_else(|| {
if tight {
styles.get(ParElem::leading)
} else {
styles.get(ParElem::spacing)
}
if tight { styles.get(ParElem::leading) } else { styles.get(ParElem::spacing) }
});
let mut cells = vec![];
let mut locator = locator.split();
let mut number = elem.start.get(styles).unwrap_or_else(|| {
if reversed {
elem.children.len() as u64
} else {
1
}
});
let mut number = elem
.start
.get(styles)
.unwrap_or_else(|| if reversed { elem.children.len() as u64 } else { 1 });
let mut parents = styles.get_cloned(EnumElem::parents);
let full = elem.full.get(styles);
@ -142,11 +136,13 @@ pub fn layout_enum(
body += ParbreakElem::shared();
}
let body = body.set(EnumElem::parents, smallvec![number]);
cells.push(Cell::new(Content::empty(), locator.next(&())));
cells.push(Cell::new(resolved, locator.next(&())));
cells.push(Cell::new(PdfMarkerTag::ListItemLabel(resolved), locator.next(&())));
cells.push(Cell::new(Content::empty(), locator.next(&())));
cells.push(Cell::new(
body.set(EnumElem::parents, smallvec![number]),
PdfMarkerTag::ListItemBody(body),
locator.next(&item.body.span()),
));
number =

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,5 @@
use comemo::{Track, Tracked, TrackedMut};
use typst_library::World;
use typst_library::diag::SourceResult;
use typst_library::engine::{Engine, Route, Sink, Traced};
use typst_library::foundations::{
@ -13,13 +14,13 @@ use typst_library::layout::{
VAlignment,
};
use typst_library::model::Numbering;
use typst_library::pdf::ArtifactKind;
use typst_library::routines::{Pair, Routines};
use typst_library::text::{LocalName, TextElem};
use typst_library::visualize::Paint;
use typst_library::World;
use typst_utils::Numeric;
use crate::flow::{layout_flow, FlowMode};
use crate::flow::{FlowMode, layout_flow};
/// A mostly finished layout for one page. Needs only knowledge of its exact
/// page number to be finalized into a `Page`. (Because the margins can depend
@ -202,6 +203,11 @@ fn layout_page_run_impl(
// Layout marginals.
let mut layouted = Vec::with_capacity(fragment.len());
let header = header.as_ref().map(|h| h.clone().artifact(ArtifactKind::Header));
let footer = footer.as_ref().map(|f| f.clone().artifact(ArtifactKind::Footer));
let background = background.as_ref().map(|b| b.clone().artifact(ArtifactKind::Page));
for inner in fragment {
let header_size = Size::new(inner.width(), margin.top - header_ascent);
let footer_size = Size::new(inner.width(), margin.bottom - footer_descent);
@ -212,9 +218,9 @@ fn layout_page_run_impl(
fill: fill.clone(),
numbering: numbering.clone(),
supplement: supplement.clone(),
header: layout_marginal(header, header_size, Alignment::BOTTOM)?,
footer: layout_marginal(footer, footer_size, Alignment::TOP)?,
background: layout_marginal(background, full_size, mid)?,
header: layout_marginal(&header, header_size, Alignment::BOTTOM)?,
footer: layout_marginal(&footer, footer_size, Alignment::TOP)?,
background: layout_marginal(&background, full_size, mid)?,
foreground: layout_marginal(foreground, full_size, mid)?,
margin,
binding,

View File

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

View File

@ -1,20 +1,19 @@
use std::num::NonZeroUsize;
use comemo::Track;
use ecow::{eco_format, EcoVec};
use ecow::{EcoVec, eco_format};
use smallvec::smallvec;
use typst_library::diag::{bail, At, SourceResult};
use typst_library::diag::{At, SourceResult, bail};
use typst_library::foundations::{
dict, Content, Context, NativeElement, NativeRuleMap, Packed, Resolve, ShowFn, Smart,
StyleChain, Target,
dict, Content, Context, LinkMarker, NativeElement, NativeRuleMap, Packed, Resolve, ShowFn, Smart, StyleChain, Target
};
use typst_library::introspection::{Counter, Locator, LocatorLink};
use typst_library::layout::{
Abs, AlignElem, Alignment, Axes, BlockBody, BlockElem, ColumnsElem, Em, GridCell,
GridChild, GridElem, GridItem, HAlignment, HElem, HideElem, InlineElem, LayoutElem,
Length, MoveElem, OuterVAlignment, PadElem, PlaceElem, PlacementScope, Region, Rel,
RepeatElem, RotateElem, ScaleElem, Sides, Size, Sizing, SkewElem, Spacing,
StackChild, StackElem, TrackSizings, VElem,
Length, MoveElem, OuterVAlignment, PadElem, PageElem, PlaceElem, PlacementScope,
Region, Rel, RepeatElem, RotateElem, ScaleElem, Sides, Size, Sizing, SkewElem,
Spacing, StackChild, StackElem, TrackSizings, VElem,
};
use typst_library::math::EquationElem;
use typst_library::model::{
@ -23,12 +22,12 @@ use typst_library::model::{
LinkElem, ListElem, Outlinable, OutlineElem, OutlineEntry, ParElem, ParbreakElem,
QuoteElem, RefElem, StrongElem, TableCell, TableElem, TermsElem, Works,
};
use typst_library::pdf::EmbedElem;
use typst_library::pdf::{ArtifactElem, EmbedElem, PdfMarkerTag};
use typst_library::text::{
DecoLine, Decoration, HighlightElem, ItalicToggle, LinebreakElem, LocalName,
OverlineElem, RawElem, RawLine, ScriptKind, ShiftSettings, Smallcaps, SmallcapsElem,
SpaceElem, StrikeElem, SubElem, SuperElem, TextElem, TextSize, UnderlineElem,
WeightDelta,
SmartQuoteElem, SmartQuotes, SpaceElem, StrikeElem, SubElem, SuperElem, TextElem,
TextSize, UnderlineElem, WeightDelta,
};
use typst_library::visualize::{
CircleElem, CurveElem, EllipseElem, ImageElem, LineElem, PathElem, PolygonElem,
@ -46,6 +45,7 @@ pub fn register(rules: &mut NativeRuleMap) {
rules.register(Paged, LIST_RULE);
rules.register(Paged, ENUM_RULE);
rules.register(Paged, TERMS_RULE);
rules.register(Paged, LINK_MARKER_RULE);
rules.register(Paged, LINK_RULE);
rules.register(Paged, HEADING_RULE);
rules.register(Paged, FIGURE_RULE);
@ -103,6 +103,8 @@ pub fn register(rules: &mut NativeRuleMap) {
// PDF.
rules.register(Paged, EMBED_RULE);
rules.register(Paged, PDF_ARTIFACT_RULE);
rules.register(Paged, PDF_MARKER_TAG_RULE);
}
const STRONG_RULE: ShowFn<StrongElem> = |elem, _, styles| {
@ -161,11 +163,7 @@ const TERMS_RULE: ShowFn<TermsElem> = |elem, _, styles| {
let indent = elem.indent.get(styles);
let hanging_indent = elem.hanging_indent.get(styles);
let gutter = elem.spacing.get(styles).unwrap_or_else(|| {
if tight {
styles.get(ParElem::leading)
} else {
styles.get(ParElem::spacing)
}
if tight { styles.get(ParElem::leading) } else { styles.get(ParElem::spacing) }
});
let pad = hanging_indent + indent;
@ -176,9 +174,9 @@ const TERMS_RULE: ShowFn<TermsElem> = |elem, _, styles| {
for child in elem.children.iter() {
let mut seq = vec![];
seq.extend(unpad.clone());
seq.push(child.term.clone().strong());
seq.push(PdfMarkerTag::ListItemLabel(child.term.clone().strong()));
seq.push(separator.clone());
seq.push(child.description.clone());
seq.push(PdfMarkerTag::ListItemBody(child.description.clone()));
// Text in wide term lists shall always turn into paragraphs.
if !tight {
@ -214,10 +212,16 @@ const TERMS_RULE: ShowFn<TermsElem> = |elem, _, styles| {
Ok(realized)
};
const LINK_RULE: ShowFn<LinkElem> = |elem, engine, _| {
const LINK_MARKER_RULE: ShowFn<LinkMarker> = |elem, _, _| Ok(elem.body.clone());
const LINK_RULE: ShowFn<LinkElem> = |elem, engine, styles| {
let body = elem.body.clone();
let dest = elem.dest.resolve(engine.introspector).at(elem.span())?;
Ok(body.linked(dest))
let alt = match elem.alt.get_cloned(styles) {
Some(alt) => Some(alt),
None => dest.alt_text(engine, styles)?,
};
Ok(body.linked(dest, alt))
};
const HEADING_RULE: ShowFn<HeadingElem> = |elem, engine, styles| {
@ -258,7 +262,7 @@ const HEADING_RULE: ShowFn<HeadingElem> = |elem, engine, styles| {
let spacing = HElem::new(SPACING_TO_NUMBERING.into()).with_weak(true).pack();
realized = numbering + spacing + realized;
realized = PdfMarkerTag::Label(numbering) + spacing + realized;
}
let block = if indent != Abs::zero() {
@ -277,7 +281,7 @@ const HEADING_RULE: ShowFn<HeadingElem> = |elem, engine, styles| {
const FIGURE_RULE: ShowFn<FigureElem> = |elem, _, styles| {
let span = elem.span();
let mut realized = elem.body.clone();
let mut realized = PdfMarkerTag::FigureBody(elem.body.clone());
// Build the caption, if any.
if let Some(caption) = elem.caption.get_cloned(styles) {
@ -376,10 +380,11 @@ const FOOTNOTE_RULE: ShowFn<FootnoteElem> = |elem, engine, styles| {
let numbering = elem.numbering.get_ref(styles);
let counter = Counter::of(FootnoteElem::ELEM);
let num = counter.display_at_loc(engine, loc, styles, numbering)?;
let sup = SuperElem::new(num).pack().spanned(span);
let alt = FootnoteElem::alt_text(styles, &num.plain_text());
let sup = PdfMarkerTag::Label(SuperElem::new(num).pack().spanned(span));
let loc = loc.variant(1);
// Add zero-width weak spacing to make the footnote "sticky".
Ok(HElem::hole().pack() + sup.linked(Destination::Location(loc)))
Ok(HElem::hole().pack() + sup.linked(Destination::Location(loc), Some(alt)))
};
const FOOTNOTE_ENTRY_RULE: ShowFn<FootnoteEntry> = |elem, engine, styles| {
@ -396,10 +401,9 @@ const FOOTNOTE_ENTRY_RULE: ShowFn<FootnoteEntry> = |elem, engine, styles| {
};
let num = counter.display_at_loc(engine, loc, styles, numbering)?;
let sup = SuperElem::new(num)
.pack()
.spanned(span)
.linked(Destination::Location(loc))
let alt = num.plain_text();
let sup = PdfMarkerTag::Label(SuperElem::new(num).pack().spanned(span))
.linked(Destination::Location(loc), Some(alt))
.located(loc.variant(1));
Ok(Content::sequence([
@ -430,6 +434,7 @@ const OUTLINE_RULE: ShowFn<OutlineElem> = |elem, engine, styles| {
let depth = elem.depth.get(styles).unwrap_or(NonZeroUsize::MAX);
// Build the outline entries.
let mut entries = vec![];
for elem in elems {
let Some(outlinable) = elem.with::<dyn Outlinable>() else {
bail!(span, "cannot outline {}", elem.func().name());
@ -438,10 +443,13 @@ const OUTLINE_RULE: ShowFn<OutlineElem> = |elem, engine, styles| {
let level = outlinable.level();
if outlinable.outlined() && level <= depth {
let entry = OutlineEntry::new(level, elem);
seq.push(entry.pack().spanned(span));
entries.push(entry.pack().spanned(span));
}
}
// Wrap the entries into a marker for pdf tagging.
seq.push(PdfMarkerTag::OutlineBody(Content::sequence(entries)));
Ok(Content::sequence(seq))
};
@ -451,7 +459,24 @@ const OUTLINE_ENTRY_RULE: ShowFn<OutlineEntry> = |elem, engine, styles| {
let context = context.track();
let prefix = elem.prefix(engine, context, span)?;
let inner = elem.inner(engine, context, span)?;
let body = elem.body().at(span)?;
let page = elem.page(engine, context, span)?;
let alt = {
let prefix = prefix.as_ref().map(|p| p.plain_text()).unwrap_or_default();
let body = body.plain_text();
let page_str = PageElem::local_name_in(styles);
let page_nr = page.plain_text();
let quotes = SmartQuotes::get(
styles.get_ref(SmartQuoteElem::quotes),
styles.get(TextElem::lang),
styles.get(TextElem::region),
styles.get(SmartQuoteElem::alternative),
);
let open = quotes.double_open;
let close = quotes.double_close;
eco_format!("{prefix} {open}{body}{close} {page_str} {page_nr}",)
};
let inner = elem.build_inner(context, span, body, page)?;
let block = if elem.element.is::<EquationElem>() {
let body = prefix.unwrap_or_default() + inner;
BlockElem::new()
@ -463,7 +488,7 @@ const OUTLINE_ENTRY_RULE: ShowFn<OutlineEntry> = |elem, engine, styles| {
};
let loc = elem.element_location().at(span)?;
Ok(block.linked(Destination::Location(loc)))
Ok(block.linked(Destination::Location(loc), Some(alt)))
};
const REF_RULE: ShowFn<RefElem> = |elem, engine, styles| elem.realize(engine, styles);
@ -511,25 +536,29 @@ const BIBLIOGRAPHY_RULE: ShowFn<BibliographyElem> = |elem, engine, styles| {
let mut cells = vec![];
for (prefix, reference) in references {
let prefix = PdfMarkerTag::ListItemLabel(prefix.clone().unwrap_or_default());
cells.push(GridChild::Item(GridItem::Cell(
Packed::new(GridCell::new(prefix.clone().unwrap_or_default()))
.spanned(span),
Packed::new(GridCell::new(prefix)).spanned(span),
)));
let reference = PdfMarkerTag::BibEntry(reference.clone());
cells.push(GridChild::Item(GridItem::Cell(
Packed::new(GridCell::new(reference.clone())).spanned(span),
Packed::new(GridCell::new(reference)).spanned(span),
)));
}
seq.push(
GridElem::new(cells)
let grid = GridElem::new(cells)
.with_columns(TrackSizings(smallvec![Sizing::Auto; 2]))
.with_column_gutter(TrackSizings(smallvec![COLUMN_GUTTER.into()]))
.with_row_gutter(TrackSizings(smallvec![row_gutter.into()]))
.pack()
.spanned(span),
);
.spanned(span);
// TODO(accessibility): infer list numbering from style?
seq.push(PdfMarkerTag::Bibliography(true, grid));
} else {
let mut body = vec![];
for (_, reference) in references {
let realized = reference.clone();
let realized = PdfMarkerTag::BibEntry(reference.clone());
let block = if works.hanging_indent {
let body = HElem::new((-INDENT).into()).pack() + realized;
let inset = Sides::default()
@ -541,8 +570,9 @@ const BIBLIOGRAPHY_RULE: ShowFn<BibliographyElem> = |elem, engine, styles| {
BlockElem::new().with_body(Some(BlockBody::Content(realized)))
};
seq.push(block.pack().spanned(span));
body.push(block.pack().spanned(span));
}
seq.push(PdfMarkerTag::Bibliography(false, Content::sequence(body)));
}
Ok(Content::sequence(seq))
@ -844,3 +874,7 @@ const EQUATION_RULE: ShowFn<EquationElem> = |elem, _, styles| {
};
const EMBED_RULE: ShowFn<EmbedElem> = |_, _, _| Ok(Content::empty());
const PDF_ARTIFACT_RULE: ShowFn<ArtifactElem> = |elem, _, _| Ok(elem.body.clone());
const PDF_MARKER_TAG_RULE: ShowFn<PdfMarkerTag> = |elem, _, _| Ok(elem.body.clone());

View File

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

View File

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

View File

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

View File

@ -31,6 +31,7 @@ flate2 = { workspace = true }
fontdb = { workspace = true }
glidesort = { workspace = true }
hayagriva = { workspace = true }
hayro-syntax = { workspace = true }
icu_properties = { workspace = true }
icu_provider = { workspace = true }
icu_provider_blob = { workspace = true }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,21 +17,22 @@ use std::iter::{self, Sum};
use std::ops::{Add, AddAssign, ControlFlow};
use comemo::Tracked;
use ecow::{eco_format, EcoString};
use ecow::{EcoString, eco_format};
use serde::{Serialize, Serializer};
use typst_syntax::Span;
use typst_utils::singleton;
use crate::diag::{SourceResult, StrResult};
use crate::diag::{SourceResult, StrResult, bail};
use crate::engine::Engine;
use crate::foundations::{
func, repr, scope, ty, Context, Dict, IntoValue, Label, Property, Recipe,
RecipeIndex, Repr, Selector, Str, Style, StyleChain, Styles, Value,
Args, Context, Dict, IntoValue, Label, Property, Recipe, RecipeIndex, Repr, Selector,
Str, Style, StyleChain, Styles, Value, func, repr, scope, ty,
};
use crate::introspection::Location;
use crate::introspection::{Locatable, Location};
use crate::layout::{AlignElem, Alignment, Axes, Length, MoveElem, PadElem, Rel, Sides};
use crate::model::{Destination, EmphElem, LinkElem, StrongElem};
use crate::pdf::{ArtifactElem, ArtifactKind};
use crate::text::UnderlineElem;
/// A piece of document content.
@ -174,11 +175,11 @@ impl Content {
id: u8,
styles: Option<StyleChain>,
) -> Result<Value, FieldAccessError> {
if id == 255 {
if let Some(label) = self.label() {
if id == 255
&& let Some(label) = self.label()
{
return Ok(label.into_value());
}
}
match self.0.handle().field(id) {
Some(handle) => match styles {
@ -476,8 +477,12 @@ impl Content {
}
/// Link the content somewhere.
pub fn linked(self, dest: Destination) -> Self {
self.set(LinkElem::current, Some(dest))
pub fn linked(self, dest: Destination, alt: Option<EcoString>) -> Self {
let span = self.span();
LinkMarker::new(self, dest.clone(), alt, span)
.pack()
.spanned(span)
.set(LinkElem::current, Some(dest))
}
/// Set alignments for this content.
@ -506,6 +511,12 @@ impl Content {
.pack()
.spanned(span)
}
/// Link the content somewhere.
pub fn artifact(self, kind: ArtifactKind) -> Self {
let span = self.span();
ArtifactElem::new(self).with_kind(kind).pack().spanned(span)
}
}
#[scope]
@ -773,6 +784,30 @@ impl Repr for StyledElem {
}
}
/// An element that associates the body of a link with the destination.
#[elem(Locatable, Construct)]
pub struct LinkMarker {
/// The content.
#[internal]
#[required]
pub body: Content,
#[internal]
#[required]
pub dest: Destination,
#[internal]
#[required]
pub alt: Option<EcoString>,
#[internal]
#[required]
pub span: Span,
}
impl Construct for LinkMarker {
fn construct(_: &mut Engine, args: &mut Args) -> SourceResult<Content> {
bail!(args.span, "cannot be constructed manually");
}
}
impl<T: NativeElement> IntoValue for T {
fn into_value(self) -> Value {
Value::Content(self.pack())

View File

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

View File

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

View File

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

View File

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

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