diff --git a/.github/workflows/release-crates.yml b/.github/workflows/release-crates.yml index 2da90608..7f7de10e 100644 --- a/.github/workflows/release-crates.yml +++ b/.github/workflows/release-crates.yml @@ -48,42 +48,30 @@ jobs: run: | sudo apt-get update sudo apt-get install llvm - - name: Install AArch64 target toolchain - if: matrix.rust-target == 'aarch64-unknown-linux-gnu' && (fromJson(env.isRelease) || fromJson(env.isNightly)) - run: | - sudo apt-get update - sudo apt-get install gcc-aarch64-linux-gnu - - name: Install ARM target toolchain - if: matrix.rust-target == 'arm-unknown-linux-gnueabihf' && (fromJson(env.isRelease) || fromJson(env.isNightly)) - run: | - sudo apt-get update - sudo apt-get install gcc-arm-linux-gnueabihf # - name: Run rust-cache # uses: Swatinem/rust-cache@v2 # if: (fromJson(env.isRelease) || fromJson(env.isNightly)) - name: Publish crates run: | - cargo publish --no-verify -p typst-shim - cargo publish --no-verify -p sync-lsp - cargo publish --no-verify -p tinymist-derive - cargo publish --no-verify -p tinymist-analysis - cargo publish --no-verify -p tinymist-std - cargo publish --no-verify -p tinymist-vfs - cargo publish --no-verify -p tinymist-world - cargo publish --no-verify -p tinymist-task - cargo publish --no-verify -p tinymist-project - cargo publish --no-verify -p typlite - cargo publish --no-verify -p crityp + cargo publish --no-verify -p typst-shim || true + cargo publish --no-verify -p tinymist-derive || true + cargo publish --no-verify -p tinymist-analysis || true + cargo publish --no-verify -p tinymist-std || true + cargo publish --no-verify -p tinymist-vfs || true + cargo publish --no-verify -p tinymist-world || true + cargo publish --no-verify -p tinymist-task || true + cargo publish --no-verify -p tinymist-project || true + cargo publish --no-verify -p typlite || true + cargo publish --no-verify -p crityp || true - name: Verifies crate health (Optional) run: | - cargo publish -p typst-shim - cargo publish -p sync-lsp - cargo publish -p tinymist-derive - cargo publish -p tinymist-analysis - cargo publish -p tinymist-std - cargo publish -p tinymist-vfs - cargo publish -p tinymist-world - cargo publish -p tinymist-task - cargo publish -p tinymist-project - cargo publish -p typlite - cargo publish -p crityp + cargo publish --dry-run -p typst-shim + cargo publish --dry-run -p tinymist-derive + cargo publish --dry-run -p tinymist-analysis + cargo publish --dry-run -p tinymist-std + cargo publish --dry-run -p tinymist-vfs + cargo publish --dry-run -p tinymist-world + cargo publish --dry-run -p tinymist-task + cargo publish --dry-run -p tinymist-project + cargo publish --dry-run -p typlite + cargo publish --dry-run -p crityp diff --git a/Cargo.lock b/Cargo.lock index 85ab38c3..6d950c0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -595,6 +595,12 @@ dependencies = [ "unicode-width 0.1.14", ] +[[package]] +name = "codex" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "724d27a0ee38b700e5e164350e79aba601a0db673ac47fce1cb74c3e38864036" + [[package]] name = "color_quant" version = "1.1.0" @@ -735,7 +741,7 @@ dependencies = [ [[package]] name = "crityp" -version = "0.12.20" +version = "0.12.21" dependencies = [ "anyhow", "base64", @@ -747,7 +753,7 @@ dependencies = [ "tinymist-project", "tinymist-std", "typst", - "typst-syntax", + "typst-syntax 0.13.0", ] [[package]] @@ -1169,6 +1175,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "fontconfig-parser" version = "0.5.7" @@ -1428,6 +1440,9 @@ name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "foldhash", +] [[package]] name = "hashlink" @@ -1890,12 +1905,6 @@ dependencies = [ "serde", ] -[[package]] -name = "indexmap-nostd" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" - [[package]] name = "inotify" version = "0.9.6" @@ -1991,6 +2000,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.14" @@ -2009,9 +2027,9 @@ dependencies = [ [[package]] name = "kamadak-exif" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4fc70d0ab7e5b6bafa30216a6b48705ea964cdfc29c050f2412295eba58077" +checksum = "1130d80c7374efad55a117d715a3af9368f0fa7a2c54573afc15a188cd984837" dependencies = [ "mutate_once", ] @@ -2277,17 +2295,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" -[[package]] -name = "num-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", -] - [[package]] name = "num-integer" version = "0.1.46" @@ -2851,9 +2858,9 @@ dependencies = [ [[package]] name = "reflexo" -version = "0.5.4" +version = "0.5.5-rc3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b13fc37349d5c5bbf5d2ad365a55f2439d59deb78acb5578575b1d88cb8e66" +checksum = "52998674c1fcc34a81b730e4cfb370f84c1356a2ec445e288806594264b30487" dependencies = [ "base64", "bitvec", @@ -2861,10 +2868,8 @@ dependencies = [ "dashmap", "ecow", "fxhash", - "js-sys", "parking_lot", "path-clean", - "reflexo-typst-shim", "rkyv", "rustc-hash 2.1.1", "serde", @@ -2873,15 +2878,16 @@ dependencies = [ "serde_with", "siphasher", "tiny-skia-path", - "wasm-bindgen", + "tinymist-std", + "typst", "web-time", ] [[package]] name = "reflexo-typst" -version = "0.5.4" +version = "0.5.5-rc3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fe8e18c1d82e428e598c4099af771e7b1b3cc56e58ae5cd450867d595f95f1" +checksum = "ea122a5ba883bfc3bfdbe8ea06e5f571383aa7459f78b420f770747b6b2467be" dependencies = [ "codespan-reporting", "comemo", @@ -2892,44 +2898,36 @@ dependencies = [ "js-sys", "log", "nohash-hasher", - "notify", "parking_lot", "pathdiff", "rayon", "reflexo", "reflexo-typst2vec", - "reflexo-vfs", - "reflexo-world", + "reflexo-vec2svg", "serde", "serde_json", "tar", + "tinymist-project", + "tinymist-task", + "tinymist-world", "tokio", "typst", + "typst-eval", "web-sys", ] -[[package]] -name = "reflexo-typst-shim" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41e26fc8d9096f2ab25dabca6407544961172f1bd6b255668e5d3e91f2ded2c1" -dependencies = [ - "cfg-if", - "typst", - "typst-syntax", -] - [[package]] name = "reflexo-typst2vec" -version = "0.5.4" +version = "0.5.5-rc3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5f0f3aa5ce66a27c72a9baddeb0f46281d8e9ef6296bfd51cf7aec002c48e07" +checksum = "2fcc41da7110413ad6b5ddc37a6295e2146ab4ad7a8aad33f15d419b4667d2fa" dependencies = [ "bitvec", "comemo", "crossbeam-queue", "dashmap", "flate2", + "image", "log", "parking_lot", "rayon", @@ -2947,9 +2945,9 @@ dependencies = [ [[package]] name = "reflexo-vec2svg" -version = "0.5.4" +version = "0.5.5-rc3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7dce08258ec590d103b4eb839a1ad7297274135b6d4d5cb36d178de9ef73ad8" +checksum = "cab6e537ac83993b221f4be0586dc6a17ef6885573d89de66e1c3f4a8f7f40db" dependencies = [ "base64", "comemo", @@ -2959,57 +2957,6 @@ dependencies = [ "typst", ] -[[package]] -name = "reflexo-vfs" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5513a7f75fc88d8cc4e151832f0596b0e925fa74aaf374351977df06c6ef6eb0" -dependencies = [ - "indexmap 2.7.1", - "js-sys", - "log", - "nohash-hasher", - "parking_lot", - "reflexo", - "rpds", - "typst", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "reflexo-world" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e30d61064c8821db1ace4da82b6129f19560f203430f20159f4e42ae025c19" -dependencies = [ - "chrono", - "codespan-reporting", - "comemo", - "dirs", - "ecow", - "flate2", - "fontdb", - "hex", - "js-sys", - "log", - "parking_lot", - "reflexo", - "reflexo-typst-shim", - "reflexo-vfs", - "reqwest", - "serde", - "serde-wasm-bindgen", - "serde_json", - "serde_with", - "sha2", - "strum", - "tar", - "typst", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "regex" version = "1.11.1" @@ -3618,12 +3565,11 @@ dependencies = [ [[package]] name = "string-interner" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c6a0d765f5807e98a091107bae0a56ea3799f66a5de47b2c84c94a39c09974e" +checksum = "1a3275464d7a9f2d4cac57c89c2ef96a8524dba2864c8d6f82e3980baf136f9b" dependencies = [ - "cfg-if", - "hashbrown 0.14.5", + "hashbrown 0.15.2", "serde", ] @@ -3721,7 +3667,7 @@ dependencies = [ [[package]] name = "sync-lsp" -version = "0.12.20" +version = "0.12.21" dependencies = [ "anyhow", "clap", @@ -3843,7 +3789,7 @@ dependencies = [ [[package]] name = "tests" -version = "0.12.20" +version = "0.12.21" dependencies = [ "insta", "lsp-server", @@ -3966,7 +3912,7 @@ dependencies = [ [[package]] name = "tinymist" -version = "0.12.20" +version = "0.12.21" dependencies = [ "anyhow", "async-trait", @@ -4009,7 +3955,7 @@ dependencies = [ "serde_yaml", "strum", "sync-lsp", - "tinymist-assets 0.12.20 (registry+https://github.com/rust-lang/crates.io-index)", + "tinymist-assets 0.12.20", "tinymist-core", "tinymist-project", "tinymist-query", @@ -4023,6 +3969,7 @@ dependencies = [ "typlite", "typst", "typst-ansi-hl", + "typst-html", "typst-pdf", "typst-preview", "typst-render", @@ -4038,7 +3985,7 @@ dependencies = [ [[package]] name = "tinymist-analysis" -version = "0.12.20" +version = "0.12.21" dependencies = [ "ecow", "insta", @@ -4050,19 +3997,19 @@ dependencies = [ "typst", ] -[[package]] -name = "tinymist-assets" -version = "0.12.20" - [[package]] name = "tinymist-assets" version = "0.12.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f73b4b64dfed7f28335992d79570ea1dd8828c921883284cf5d9f415f726986a" +[[package]] +name = "tinymist-assets" +version = "0.12.21" + [[package]] name = "tinymist-core" -version = "0.12.20" +version = "0.12.21" dependencies = [ "anyhow", "cargo_metadata", @@ -4074,7 +4021,7 @@ dependencies = [ [[package]] name = "tinymist-derive" -version = "0.12.20" +version = "0.12.21" dependencies = [ "quote", "syn 2.0.98", @@ -4082,7 +4029,7 @@ dependencies = [ [[package]] name = "tinymist-project" -version = "0.12.20" +version = "0.12.21" dependencies = [ "anyhow", "chrono", @@ -4107,12 +4054,11 @@ dependencies = [ "toml", "typst", "typst-assets", - "typst-preview", ] [[package]] name = "tinymist-query" -version = "0.12.20" +version = "0.12.21" dependencies = [ "anyhow", "base64", @@ -4166,7 +4112,7 @@ dependencies = [ [[package]] name = "tinymist-render" -version = "0.12.20" +version = "0.12.21" dependencies = [ "base64", "log", @@ -4179,7 +4125,7 @@ dependencies = [ [[package]] name = "tinymist-std" -version = "0.12.20" +version = "0.12.21" dependencies = [ "anyhow", "base64", @@ -4215,7 +4161,7 @@ dependencies = [ [[package]] name = "tinymist-task" -version = "0.12.20" +version = "0.12.21" dependencies = [ "anyhow", "chrono", @@ -4240,6 +4186,8 @@ dependencies = [ "toml", "typst", "typst-assets", + "typst-eval", + "typst-html", "typst-pdf", "typst-render", "typst-shim", @@ -4248,7 +4196,7 @@ dependencies = [ [[package]] name = "tinymist-vfs" -version = "0.12.20" +version = "0.12.21" dependencies = [ "comemo", "ecow", @@ -4266,7 +4214,7 @@ dependencies = [ [[package]] name = "tinymist-world" -version = "0.12.20" +version = "0.12.21" dependencies = [ "anyhow", "chrono", @@ -4555,7 +4503,7 @@ checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "typlite" -version = "0.12.20" +version = "0.12.21" dependencies = [ "base64", "clap", @@ -4568,75 +4516,25 @@ dependencies = [ "tinymist-std", "typst", "typst-svg", - "typst-syntax", + "typst-syntax 0.13.0", ] [[package]] name = "typst" -version = "0.12.0" -source = "git+https://github.com/Myriad-Dreamin/typst.git?tag=tinymist-v0.12.0#58426a90a7ef721738a01be09793d33e55eb75a9" +version = "0.13.0" +source = "git+https://github.com/ParaN3xus/typst.git?tag=tinymist-nightly-v0.12.21-content-hint#bba9db89a228e7fe1e6f9e6f2ff531f92a1a4856" dependencies = [ - "arrayvec 0.7.6", - "az", - "bitflags 2.8.0", - "bumpalo", - "chinese-number", - "ciborium", "comemo", - "csv", "ecow", - "flate2", - "fontdb", - "hayagriva", - "hypher", - "icu_properties", - "icu_provider", - "icu_provider_adapters", - "icu_provider_blob", - "icu_segmenter", - "if_chain", - "image", - "indexmap 2.7.1", - "kamadak-exif", - "kurbo", - "lipsum", - "log", - "once_cell", - "palette", - "phf", - "png", - "portable-atomic", - "qcms", - "rayon", - "regex", - "roxmltree", - "rust_decimal", - "rustybuzz", - "serde", - "serde_json", - "serde_yaml", - "siphasher", - "smallvec", - "stacker", - "syntect", - "time", - "toml", - "ttf-parser", - "two-face", - "typed-arena", - "typst-assets", + "typst-eval", + "typst-html", + "typst-layout", + "typst-library", "typst-macros", - "typst-syntax", + "typst-realize", + "typst-syntax 0.13.0", "typst-timing", - "typst-utils", - "unicode-bidi", - "unicode-math-class", - "unicode-script", - "unicode-segmentation", - "unscanny", - "usvg", - "wasmi", - "xmlwriter", + "typst-utils 0.13.0", ] [[package]] @@ -4650,19 +4548,142 @@ dependencies = [ "termcolor", "thiserror 1.0.69", "two-face", - "typst-syntax", + "typst-syntax 0.12.0", ] [[package]] name = "typst-assets" version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fe00da1b24da2c4a7da532fc33d0c3bd43a902ca4c408ee2c36eabe70f2f4ba" +source = "git+https://github.com/typst/typst-assets?rev=8cccef9#8cccef93b5da73a1c80389722cf2b655b624f577" + +[[package]] +name = "typst-eval" +version = "0.13.0" +source = "git+https://github.com/ParaN3xus/typst.git?tag=tinymist-nightly-v0.12.21-content-hint#bba9db89a228e7fe1e6f9e6f2ff531f92a1a4856" +dependencies = [ + "comemo", + "ecow", + "if_chain", + "indexmap 2.7.1", + "stacker", + "toml", + "typst-library", + "typst-macros", + "typst-syntax 0.13.0", + "typst-timing", + "typst-utils 0.13.0", + "unicode-segmentation", +] + +[[package]] +name = "typst-html" +version = "0.13.0" +source = "git+https://github.com/ParaN3xus/typst.git?tag=tinymist-nightly-v0.12.21-content-hint#bba9db89a228e7fe1e6f9e6f2ff531f92a1a4856" +dependencies = [ + "comemo", + "ecow", + "typst-library", + "typst-macros", + "typst-svg", + "typst-syntax 0.13.0", + "typst-timing", + "typst-utils 0.13.0", +] + +[[package]] +name = "typst-layout" +version = "0.13.0" +source = "git+https://github.com/ParaN3xus/typst.git?tag=tinymist-nightly-v0.12.21-content-hint#bba9db89a228e7fe1e6f9e6f2ff531f92a1a4856" +dependencies = [ + "az", + "bumpalo", + "comemo", + "ecow", + "hypher", + "icu_properties", + "icu_provider", + "icu_provider_adapters", + "icu_provider_blob", + "icu_segmenter", + "kurbo", + "rustybuzz", + "smallvec", + "ttf-parser", + "typst-assets", + "typst-library", + "typst-macros", + "typst-syntax 0.13.0", + "typst-timing", + "typst-utils 0.13.0", + "unicode-bidi", + "unicode-math-class", + "unicode-script", + "unicode-segmentation", +] + +[[package]] +name = "typst-library" +version = "0.13.0" +source = "git+https://github.com/ParaN3xus/typst.git?tag=tinymist-nightly-v0.12.21-content-hint#bba9db89a228e7fe1e6f9e6f2ff531f92a1a4856" +dependencies = [ + "az", + "bitflags 2.8.0", + "bumpalo", + "chinese-number", + "ciborium", + "codex", + "comemo", + "csv", + "ecow", + "flate2", + "fontdb", + "hayagriva", + "icu_properties", + "icu_provider", + "icu_provider_blob", + "image", + "indexmap 2.7.1", + "kamadak-exif", + "kurbo", + "lipsum", + "palette", + "phf", + "png", + "qcms", + "rayon", + "regex", + "regex-syntax", + "roxmltree", + "rust_decimal", + "rustybuzz", + "serde", + "serde_json", + "serde_yaml", + "siphasher", + "smallvec", + "syntect", + "time", + "toml", + "ttf-parser", + "two-face", + "typed-arena", + "typst-assets", + "typst-macros", + "typst-syntax 0.13.0", + "typst-timing", + "typst-utils 0.13.0", + "unicode-math-class", + "unicode-segmentation", + "unscanny", + "usvg", + "wasmi", + "xmlwriter", +] [[package]] name = "typst-macros" -version = "0.12.0" -source = "git+https://github.com/Myriad-Dreamin/typst.git?tag=tinymist-v0.12.0#58426a90a7ef721738a01be09793d33e55eb75a9" +version = "0.13.0" +source = "git+https://github.com/ParaN3xus/typst.git?tag=tinymist-nightly-v0.12.21-content-hint#bba9db89a228e7fe1e6f9e6f2ff531f92a1a4856" dependencies = [ "heck", "proc-macro2", @@ -4672,8 +4693,8 @@ dependencies = [ [[package]] name = "typst-pdf" -version = "0.12.0" -source = "git+https://github.com/Myriad-Dreamin/typst.git?tag=tinymist-v0.12.0#58426a90a7ef721738a01be09793d33e55eb75a9" +version = "0.13.0" +source = "git+https://github.com/ParaN3xus/typst.git?tag=tinymist-nightly-v0.12.21-content-hint#bba9db89a228e7fe1e6f9e6f2ff531f92a1a4856" dependencies = [ "arrayvec 0.7.6", "base64", @@ -4683,23 +4704,23 @@ dependencies = [ "image", "indexmap 2.7.1", "miniz_oxide", - "once_cell", "pdf-writer", "serde", "subsetter", "svg2pdf", "ttf-parser", - "typst", "typst-assets", + "typst-library", "typst-macros", + "typst-syntax 0.13.0", "typst-timing", - "unscanny", + "typst-utils 0.13.0", "xmp-writer", ] [[package]] name = "typst-preview" -version = "0.12.20" +version = "0.12.21" dependencies = [ "clap", "comemo", @@ -4713,55 +4734,73 @@ dependencies = [ "reflexo-vec2svg", "serde", "serde_json", - "tinymist-assets 0.12.20 (registry+https://github.com/rust-lang/crates.io-index)", + "tinymist-assets 0.12.20", "tinymist-std", "tokio", "typst", "typst-assets", ] +[[package]] +name = "typst-realize" +version = "0.13.0" +source = "git+https://github.com/ParaN3xus/typst.git?tag=tinymist-nightly-v0.12.21-content-hint#bba9db89a228e7fe1e6f9e6f2ff531f92a1a4856" +dependencies = [ + "arrayvec 0.7.6", + "bumpalo", + "comemo", + "ecow", + "regex", + "typst-library", + "typst-macros", + "typst-syntax 0.13.0", + "typst-timing", + "typst-utils 0.13.0", +] + [[package]] name = "typst-render" -version = "0.12.0" -source = "git+https://github.com/Myriad-Dreamin/typst.git?tag=tinymist-v0.12.0#58426a90a7ef721738a01be09793d33e55eb75a9" +version = "0.13.0" +source = "git+https://github.com/ParaN3xus/typst.git?tag=tinymist-nightly-v0.12.21-content-hint#bba9db89a228e7fe1e6f9e6f2ff531f92a1a4856" dependencies = [ "bytemuck", "comemo", "image", "pixglyph", "resvg", - "roxmltree", "tiny-skia", "ttf-parser", - "typst", + "typst-library", "typst-macros", "typst-timing", - "usvg", ] [[package]] name = "typst-shim" -version = "0.12.20" +version = "0.12.21" dependencies = [ "cfg-if", "comemo", "typst", - "typst-syntax", + "typst-eval", + "typst-syntax 0.13.0", ] [[package]] name = "typst-svg" -version = "0.12.0" -source = "git+https://github.com/Myriad-Dreamin/typst.git?tag=tinymist-v0.12.0#58426a90a7ef721738a01be09793d33e55eb75a9" +version = "0.13.0" +source = "git+https://github.com/ParaN3xus/typst.git?tag=tinymist-nightly-v0.12.21-content-hint#bba9db89a228e7fe1e6f9e6f2ff531f92a1a4856" dependencies = [ "base64", "comemo", "ecow", "flate2", + "image", "ttf-parser", - "typst", + "typst-library", "typst-macros", "typst-timing", + "typst-utils 0.13.0", "xmlparser", "xmlwriter", ] @@ -4769,13 +4808,31 @@ dependencies = [ [[package]] name = "typst-syntax" version = "0.12.0" -source = "git+https://github.com/Myriad-Dreamin/typst.git?tag=tinymist-v0.12.0#58426a90a7ef721738a01be09793d33e55eb75a9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b7be8b6ed6b2cb39ca495947d548a28d7db0ba244008e44c5a759120327693" dependencies = [ "ecow", "once_cell", "serde", "toml", - "typst-utils", + "typst-utils 0.12.0", + "unicode-ident", + "unicode-math-class", + "unicode-script", + "unicode-segmentation", + "unscanny", +] + +[[package]] +name = "typst-syntax" +version = "0.13.0" +source = "git+https://github.com/ParaN3xus/typst.git?tag=tinymist-nightly-v0.12.21-content-hint#bba9db89a228e7fe1e6f9e6f2ff531f92a1a4856" +dependencies = [ + "ecow", + "serde", + "toml", + "typst-timing", + "typst-utils 0.13.0", "unicode-ident", "unicode-math-class", "unicode-script", @@ -4785,19 +4842,19 @@ dependencies = [ [[package]] name = "typst-timing" -version = "0.12.0" -source = "git+https://github.com/Myriad-Dreamin/typst.git?tag=tinymist-v0.12.0#58426a90a7ef721738a01be09793d33e55eb75a9" +version = "0.13.0" +source = "git+https://github.com/ParaN3xus/typst.git?tag=tinymist-nightly-v0.12.21-content-hint#bba9db89a228e7fe1e6f9e6f2ff531f92a1a4856" dependencies = [ "parking_lot", "serde", "serde_json", - "typst-syntax", ] [[package]] name = "typst-utils" version = "0.12.0" -source = "git+https://github.com/Myriad-Dreamin/typst.git?tag=tinymist-v0.12.0#58426a90a7ef721738a01be09793d33e55eb75a9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f0305443ed97f0b658471487228f86bf835705e7525fbdcc671cebd864f7a40" dependencies = [ "once_cell", "portable-atomic", @@ -4806,6 +4863,19 @@ dependencies = [ "thin-vec", ] +[[package]] +name = "typst-utils" +version = "0.13.0" +source = "git+https://github.com/ParaN3xus/typst.git?tag=tinymist-nightly-v0.12.21-content-hint#bba9db89a228e7fe1e6f9e6f2ff531f92a1a4856" +dependencies = [ + "once_cell", + "portable-atomic", + "rayon", + "siphasher", + "thin-vec", + "unicode-math-class", +] + [[package]] name = "typstfmt" version = "0.12.1" @@ -4819,21 +4889,20 @@ dependencies = [ "serde", "toml", "tracing", - "typst-syntax", + "typst-syntax 0.12.0", "unicode-width 0.2.0", ] [[package]] name = "typstyle-core" version = "0.12.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e221e78ecf6aedf3fc0efc883cace3dce79b4b1416caedce06409ba866b4dcd" +source = "git+https://github.com/ParaN3xus/typstyle/?tag=tinymist-nightly-v0.12.21-rc1#37529defcada013bb4c8342c70968daff8097f24" dependencies = [ "ecow", - "itertools 0.13.0", + "itertools 0.14.0", "pretty", "rustc-hash 2.1.1", - "typst-syntax", + "typst-syntax 0.13.0", ] [[package]] @@ -5142,51 +5211,56 @@ dependencies = [ [[package]] name = "wasmi" -version = "0.35.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbaac6e702fa7b52258e5ac90d6e20a40afb37a1fbe7c645d0903ee42c5f85f4" +checksum = "a19af97fcb96045dd1d6b4d23e2b4abdbbe81723dbc5c9f016eb52145b320063" dependencies = [ "arrayvec 0.7.6", "multi-stash", - "num-derive", - "num-traits", "smallvec", "spin", "wasmi_collections", "wasmi_core", - "wasmparser-nostd", + "wasmi_ir", + "wasmparser", ] [[package]] name = "wasmi_collections" -version = "0.35.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ff59e30e550a509cc689ec638e5042be4d78ec9f6dd8a71fd02ee28776a74fd" +checksum = "e80d6b275b1c922021939d561574bf376613493ae2b61c6963b15db0e8813562" dependencies = [ - "ahash 0.8.11", - "hashbrown 0.14.5", "string-interner", ] [[package]] name = "wasmi_core" -version = "0.35.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e10c674add0f92f47bf8ad57c55ee3ac1762a0d9baf07535e27e22b758a916" +checksum = "3a8c51482cc32d31c2c7ff211cd2bedd73c5bd057ba16a2ed0110e7a96097c33" dependencies = [ "downcast-rs", "libm", - "num-traits", - "paste", ] [[package]] -name = "wasmparser-nostd" -version = "0.100.2" +name = "wasmi_ir" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" +checksum = "6e431a14c186db59212a88516788bd68ed51f87aa1e08d1df742522867b5289a" dependencies = [ - "indexmap-nostd", + "wasmi_core", +] + +[[package]] +name = "wasmparser" +version = "0.221.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d06bfa36ab3ac2be0dee563380147a5b81ba10dd8885d7fbbc9eb574be67d185" +dependencies = [ + "bitflags 2.8.0", + "indexmap 2.7.1", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 3d45b7ad..c5700685 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace.package] description = "An integrated language service for Typst." authors = ["Myriad-Dreamin ", "Nathan Varner"] -version = "0.12.20" +version = "0.12.21" edition = "2021" readme = "README.md" license = "Apache-2.0" @@ -32,11 +32,7 @@ triomphe = { version = "0.1.10", default-features = false, features = ["std"] } async-trait = "0.1.77" futures = "0.3" rayon = "1.10.0" -tokio = { version = "1.42.0", features = [ - "macros", - "rt-multi-thread", - "io-std", -] } +tokio = { version = "1.42.0", features = ["macros"] } tokio-util = { version = "0.7.13", features = ["compat"] } # System @@ -129,26 +125,26 @@ env_logger = "0.11.3" log = "0.4" # Typst -reflexo = { version = "=0.5.4", default-features = false, features = [ +reflexo = { version = "=0.5.5-rc3", default-features = false, features = [ "flat-vector", ] } -reflexo-typst = { version = "=0.5.4", default-features = false } -reflexo-vec2svg = { version = "=0.5.4" } -reflexo-typst-shim = { version = "=0.5.4", features = ["nightly"] } +reflexo-typst = { version = "=0.5.5-rc3", default-features = false } +reflexo-vec2svg = { version = "=0.5.5-rc3" } -typst = "0.12.0" -typst-timing = "0.12.0" -typst-svg = "0.12.0" -typst-render = "0.12.0" -typst-pdf = "0.12.0" -typst-syntax = "0.12.0" +typst = "0.13.0-rc1" +typst-html = "0.13.0-rc1" +typst-timing = "0.13.0-rc1" +typst-svg = "0.13.0-rc1" +typst-render = "0.13.0-rc1" +typst-pdf = "0.13.0-rc1" +typst-syntax = "0.13.0-rc1" +typst-eval = "0.13.0-rc1" typst-assets = "0.12.0" typstfmt = { git = "https://github.com/Myriad-Dreamin/typstfmt", tag = "v0.12.1" } typst-ansi-hl = "0.3.0" typstyle-core = { version = "=0.12.15", default-features = false } typlite = { path = "./crates/typlite" } -typst-shim = { path = "./crates/typst-shim" } # LSP crossbeam-channel = "0.5.12" @@ -178,19 +174,20 @@ insta = { version = "1.39", features = ["glob"] } # Our Own Crates -typst-preview = { path = "./crates/typst-preview" } +typst-preview = { path = "./crates/typst-preview", version = "0.12.21" } +typst-shim = { path = "./crates/typst-shim", version = "0.12.21" } tinymist-assets = { version = "=0.12.20" } -tinymist = { path = "./crates/tinymist/" } -tinymist-std = { path = "./crates/tinymist-std/", default-features = false } -tinymist-vfs = { path = "./crates/tinymist-vfs/", default-features = false } -tinymist-core = { path = "./crates/tinymist-core/", default-features = false } -tinymist-world = { path = "./crates/tinymist-world/", default-features = false } -tinymist-project = { path = "./crates/tinymist-project/" } -tinymist-task = { path = "./crates/tinymist-task/" } -tinymist-derive = { path = "./crates/tinymist-derive/" } -tinymist-analysis = { path = "./crates/tinymist-analysis/" } -tinymist-query = { path = "./crates/tinymist-query/" } -tinymist-render = { path = "./crates/tinymist-render/" } +tinymist = { path = "./crates/tinymist/", version = "0.12.21" } +tinymist-std = { path = "./crates/tinymist-std/", version = "0.12.21", default-features = false } +tinymist-vfs = { path = "./crates/tinymist-vfs/", version = "0.12.21", default-features = false } +tinymist-core = { path = "./crates/tinymist-core/", version = "0.12.21", default-features = false } +tinymist-world = { path = "./crates/tinymist-world/", version = "0.12.21", default-features = false } +tinymist-project = { path = "./crates/tinymist-project/", version = "0.12.21" } +tinymist-task = { path = "./crates/tinymist-task/", version = "0.12.21" } +tinymist-derive = { path = "./crates/tinymist-derive/", version = "0.12.21" } +tinymist-analysis = { path = "./crates/tinymist-analysis/", version = "0.12.21" } +tinymist-query = { path = "./crates/tinymist-query/", version = "0.12.21" } +tinymist-render = { path = "./crates/tinymist-render/", version = "0.12.21" } [profile.dev.package.insta] opt-level = 3 @@ -257,12 +254,15 @@ extend-exclude = ["/.git", "fixtures"] # These patches use a different version of `typst`, which only exports some private functions and information for code analysis. # # A regular build MUST use `tag` or `rev` to specify the version of the patched crate to ensure stability. -typst = { git = "https://github.com/Myriad-Dreamin/typst.git", tag = "tinymist-v0.12.0" } -typst-timing = { git = "https://github.com/Myriad-Dreamin/typst.git", tag = "tinymist-v0.12.0" } -typst-svg = { git = "https://github.com/Myriad-Dreamin/typst.git", tag = "tinymist-v0.12.0" } -typst-render = { git = "https://github.com/Myriad-Dreamin/typst.git", tag = "tinymist-v0.12.0" } -typst-pdf = { git = "https://github.com/Myriad-Dreamin/typst.git", tag = "tinymist-v0.12.0" } -typst-syntax = { git = "https://github.com/Myriad-Dreamin/typst.git", tag = "tinymist-v0.12.0" } +typst = { git = "https://github.com/ParaN3xus/typst.git", tag = "tinymist-nightly-v0.12.21-content-hint" } +typst-html = { git = "https://github.com/ParaN3xus/typst.git", tag = "tinymist-nightly-v0.12.21-content-hint" } +typst-timing = { git = "https://github.com/ParaN3xus/typst.git", tag = "tinymist-nightly-v0.12.21-content-hint" } +typst-svg = { git = "https://github.com/ParaN3xus/typst.git", tag = "tinymist-nightly-v0.12.21-content-hint" } +typst-render = { git = "https://github.com/ParaN3xus/typst.git", tag = "tinymist-nightly-v0.12.21-content-hint" } +typst-pdf = { git = "https://github.com/ParaN3xus/typst.git", tag = "tinymist-nightly-v0.12.21-content-hint" } +typst-syntax = { git = "https://github.com/ParaN3xus/typst.git", tag = "tinymist-nightly-v0.12.21-content-hint" } +typst-eval = { git = "https://github.com/ParaN3xus/typst.git", tag = "tinymist-nightly-v0.12.21-content-hint" } +typst-assets = { git = "https://github.com/typst/typst-assets", rev = "8cccef9" } # These patches use local `typst` for development. # typst = { path = "../typst/crates/typst" } @@ -275,16 +275,29 @@ typst-syntax = { git = "https://github.com/Myriad-Dreamin/typst.git", tag = "tin # These patches use a different version of `reflexo`. # # A regular build MUST use `tag` or `rev` to specify the version of the patched crate to ensure stability. -# reflexo = { git = "https://github.com/Myriad-Dreamin/typst.ts/", rev = "1b6e29c650ad6d3095e5ea18d93a2428c1ae77b9" } -# reflexo-typst = { git = "https://github.com/Myriad-Dreamin/typst.ts/", rev = "1b6e29c650ad6d3095e5ea18d93a2428c1ae77b9" } -# reflexo-typst2vec = { git = "https://github.com/Myriad-Dreamin/typst.ts/", rev = "1b6e29c650ad6d3095e5ea18d93a2428c1ae77b9" } -# reflexo-vec2svg = { git = "https://github.com/Myriad-Dreamin/typst.ts/", rev = "1b6e29c650ad6d3095e5ea18d93a2428c1ae77b9" } -# reflexo-typst-shim = { git = "https://github.com/Myriad-Dreamin/typst.ts/", rev = "1b6e29c650ad6d3095e5ea18d93a2428c1ae77b9" } +# reflexo = { git = "https://github.com/Myriad-Dreamin/typst.ts/", tag = "v0.5.5-rc3" } +# reflexo-typst = { git = "https://github.com/Myriad-Dreamin/typst.ts/", tag = "v0.5.5-rc3" } +# reflexo-vec2svg = { git = "https://github.com/Myriad-Dreamin/typst.ts/", tag = "v0.5.5-rc3" } # These patches use local `reflexo` for development. # reflexo = { path = "../typst.ts/crates/reflexo/" } # reflexo-typst = { path = "../typst.ts/crates/reflexo-typst/" } -# reflexo-typst2vec = { path = "../typst.ts/crates/conversion/typst2vec/" } # reflexo-vec2svg = { path = "../typst.ts/crates/conversion/vec2svg/" } -# reflexo-typst-shim = { path = "../typst.ts/crates/reflexo-typst-shim/" } -# typstyle = { path = "../typstyle" } +typstyle-core = { git = "https://github.com/ParaN3xus/typstyle/", tag = "tinymist-nightly-v0.12.21-rc1" } + +typst-shim = { path = "crates/typst-shim" } +tinymist-analysis = { path = "crates/tinymist-analysis" } +tinymist-std = { path = "crates/tinymist-std" } +tinymist-vfs = { path = "crates/tinymist-vfs" } +tinymist-world = { path = "crates/tinymist-world" } +tinymist-project = { path = "crates/tinymist-project" } +tinymist-task = { path = "crates/tinymist-task" } + +# If reflexo use the tinymist from git, you should use the following patch. +# [patch."https://github.com/Myriad-Dreamin/tinymist.git"] +# typst-shim = { path = "crates/typst-shim" } +# tinymist-analysis = { path = "crates/tinymist-analysis" } +# tinymist-std = { path = "crates/tinymist-std" } +# tinymist-world = { path = "crates/tinymist-world" } +# tinymist-project = { path = "crates/tinymist-project" } +# tinymist-task = { path = "crates/tinymist-task" } diff --git a/crates/crityp/Cargo.toml b/crates/crityp/Cargo.toml index 9539b9b6..b43ac687 100644 --- a/crates/crityp/Cargo.toml +++ b/crates/crityp/Cargo.toml @@ -30,7 +30,7 @@ criterion = "0.5.1" comemo.workspace = true ecow.workspace = true tinymist-std.workspace = true -tinymist-project = { workspace = true, features = ["system"] } +tinymist-project = { workspace = true, features = ["lsp"] } typst.workspace = true typst-syntax.workspace = true diff --git a/crates/crityp/src/lib.rs b/crates/crityp/src/lib.rs index b1f32b56..a8749527 100644 --- a/crates/crityp/src/lib.rs +++ b/crates/crityp/src/lib.rs @@ -40,12 +40,12 @@ pub fn bench(c: &mut Criterion, world: &mut LspWorld) -> anyhow::Result<()> { // Collects all benchmarks. let mut goals: Vec<(EcoString, &Func)> = vec![]; - for (name, value, _) in module.scope().iter() { + for (name, bind) in module.scope().iter() { if !name.starts_with("bench") { continue; } - if let Value::Func(func) = value { + if let Value::Func(func) = bind.read() { goals.push((eco_format!("{main_path}@{name}"), func)); } } @@ -55,6 +55,7 @@ pub fn bench(c: &mut Criterion, world: &mut LspWorld) -> anyhow::Result<()> { let route = Route::default(); let mut sink = Sink::default(); let engine = &mut Engine { + routines: &typst::ROUTINES, world: ((world) as &dyn World).track(), introspector: introspector.track(), traced: traced.track(), diff --git a/crates/sync-lsp/README.md b/crates/sync-lsp/README.md new file mode 100644 index 00000000..300716d7 --- /dev/null +++ b/crates/sync-lsp/README.md @@ -0,0 +1,6 @@ +# sync-lsp + +Sync LSP server inspired by async-lsp, primarily for tinymist. The author of this crate thinks that async-lsp is better than sync-lsp, so please use async-lsp whenever possible unless you have a good reason to use sync-lsp. Some random points: + +- The `req_queue` and `transport` are extracted from the rust-analyzer project. +- The sync-lsp should have better performance on stdio transport than async-lsp, especially on windows, but the author have forgotten the idea. diff --git a/crates/tinymist-analysis/README.md b/crates/tinymist-analysis/README.md new file mode 100644 index 00000000..4c93e575 --- /dev/null +++ b/crates/tinymist-analysis/README.md @@ -0,0 +1,3 @@ +# tinymist-analysis + +Typst Static Analyzers for Tinymist. diff --git a/crates/tinymist-analysis/src/syntax/matcher.rs b/crates/tinymist-analysis/src/syntax/matcher.rs index 03a0f37f..8a3c7dce 100644 --- a/crates/tinymist-analysis/src/syntax/matcher.rs +++ b/crates/tinymist-analysis/src/syntax/matcher.rs @@ -325,7 +325,7 @@ pub fn interpret_mode_at(mut leaf: Option<&LinkedNode>) -> InterpretMode { pub(crate) fn interpret_mode_at_kind(kind: SyntaxKind) -> Option { use SyntaxKind::*; Some(match kind { - LineComment | BlockComment => InterpretMode::Comment, + LineComment | BlockComment | Shebang => InterpretMode::Comment, Raw => InterpretMode::Raw, Str => InterpretMode::String, CodeBlock | Code => InterpretMode::Code, @@ -342,7 +342,7 @@ pub(crate) fn interpret_mode_at_kind(kind: SyntaxKind) -> Option Strong | Emph | Link | Ref | RefMarker | Heading | HeadingMarker | ListItem | ListMarker | EnumItem | EnumMarker | TermItem | TermMarker => InterpretMode::Markup, MathIdent | MathAlignPoint | MathDelimited | MathAttach | MathPrimes | MathFrac - | MathRoot | MathShorthand => InterpretMode::Math, + | MathRoot | MathShorthand | MathText => InterpretMode::Math, Let | Set | Show | Context | If | Else | For | In | While | Break | Continue | Return | Import | Include | Closure | Params | LetBinding | SetRule | ShowRule | Contextual | Conditional | WhileLoop | ForLoop | LoopBreak | ModuleImport | ImportItems @@ -727,8 +727,10 @@ pub fn classify_syntax(node: LinkedNode, cursor: usize) -> Option, - - /// When to run the task - #[arg(long = "when")] - pub when: Option, - - /// Preview arguments - #[clap(flatten)] - pub preview: PreviewArgs, - - /// Preview mode - #[clap(long = "preview-mode", default_value = "document", value_name = "MODE")] - pub preview_mode: PreviewMode, -} diff --git a/crates/tinymist-project/src/compiler.rs b/crates/tinymist-project/src/compiler.rs index 2afeed41..2d5cb8d8 100644 --- a/crates/tinymist-project/src/compiler.rs +++ b/crates/tinymist-project/src/compiler.rs @@ -1,12 +1,14 @@ //! Project compiler for tinymist. use core::fmt; +use std::any::TypeId; use std::collections::HashSet; use std::path::Path; use std::sync::{Arc, OnceLock}; use ecow::{eco_vec, EcoVec}; use tinymist_std::error::prelude::Result; +use tinymist_std::typst::{TypstHtmlDocument, TypstPagedDocument}; use tinymist_std::{typst::TypstDocument, ImmutPath}; use tinymist_world::vfs::notify::{ FilesystemEvent, MemoryEvent, NotifyDeps, NotifyMessage, UpstreamUpdateEvent, @@ -14,19 +16,11 @@ use tinymist_world::vfs::notify::{ use tinymist_world::vfs::{FileId, FsProvider, RevisingVfs, WorkspaceResolver}; use tinymist_world::{ CompileSnapshot, CompilerFeat, CompilerUniverse, EntryReader, EntryState, ExportSignal, - ProjectInsId, TaskInputs, WorldDeps, + ProjectInsId, TaskInputs, WorldComputeGraph, WorldDeps, }; use tokio::sync::mpsc; use typst::diag::{SourceDiagnostic, SourceResult, Warned}; -use crate::LspCompilerFeat; - -/// LSP compile snapshot. -pub type LspCompileSnapshot = CompileSnapshot; -/// LSP compiled artifact. -pub type LspCompiledArtifact = CompiledArtifact; -/// LSP interrupt. -pub type LspInterrupt = Interrupt; /// A compiled artifact. pub struct CompiledArtifact { /// The used snapshot. @@ -39,7 +33,7 @@ pub struct CompiledArtifact { pub deps: OnceLock>, } -impl fmt::Display for CompiledArtifact { +impl fmt::Display for CompiledArtifact { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let rev = self.world.revision(); write!(f, "CompiledArtifact({:?}, rev={rev:?})", self.id) @@ -88,12 +82,49 @@ impl CompiledArtifact { } /// Runs the compiler and returns the compiled document. - pub fn from_snapshot(mut snap: CompileSnapshot) -> CompiledArtifact { + pub fn from_snapshot(snap: CompileSnapshot) -> CompiledArtifact { + let is_html = snap.world.library.features.is_enabled(typst::Feature::Html); + + if is_html { + Self::from_snapshot_inner::(snap) + } else { + Self::from_snapshot_inner::(snap) + } + } + + /// Runs the compiler and returns the compiled document. + fn from_snapshot_inner(mut snap: CompileSnapshot) -> CompiledArtifact + where + D: typst::Document + 'static, + Arc: Into, + { snap.world.set_is_compiling(true); - let res = ::typst::compile(&snap.world); + let res = ::typst::compile::(&snap.world); + snap.world.set_is_compiling(false); + + Self::from_snapshot_result( + snap, + Warned { + output: res.output.map(Arc::new), + warnings: res.warnings, + }, + ) + } + + /// Runs the compiler and returns the compiled document. + pub fn from_snapshot_result( + snap: CompileSnapshot, + res: Warned>>, + ) -> CompiledArtifact + where + D: typst::Document + 'static, + Arc: Into, + { + let is_html_compilation = TypeId::of::() == TypeId::of::(); + let warned = match res.output { Ok(doc) => Ok(Warned { - output: Arc::new(doc), + output: doc, warnings: res.warnings, }), Err(diags) => match (res.warnings.is_empty(), diags.is_empty()) { @@ -107,15 +138,27 @@ impl CompiledArtifact { } }, }; - snap.world.set_is_compiling(false); let (doc, warnings) = match warned { - Ok(doc) => (Ok(TypstDocument::Paged(doc.output)), doc.warnings), + Ok(doc) => (Ok(doc.output.into()), doc.warnings), Err(err) => (Err(err), EcoVec::default()), }; + + let exclude_html_warnings = if !is_html_compilation { + warnings + } else if warnings.len() == 1 + && warnings[0] + .message + .starts_with("html export is under active development") + { + EcoVec::new() + } else { + warnings + }; + CompiledArtifact { snap, doc, - warnings, + warnings: exclude_html_warnings, deps: OnceLock::default(), } } @@ -244,7 +287,7 @@ pub enum Interrupt { Fs(FilesystemEvent), } -impl fmt::Debug for Interrupt { +impl fmt::Debug for Interrupt { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Interrupt::Compile(id) => write!(f, "Compile({id:?})"), @@ -454,19 +497,31 @@ impl ProjectCom dedicates.iter_mut().find(|e| e.id == *id).unwrap() } + /// Clear all dedicate projects. + pub fn clear_dedicates(&mut self) { + self.dedicates.clear(); + } + /// Restart a dedicate project. - pub fn restart_dedicate(&mut self, group: &str, entry: EntryState) -> Result { + pub fn restart_dedicate( + &mut self, + group: &str, + entry: EntryState, + enable_html: bool, + ) -> Result { let id = ProjectInsId(group.into()); let verse = CompilerUniverse::::new_raw( entry, + enable_html, Some(self.primary.verse.inputs().clone()), self.primary.verse.vfs().fork(), self.primary.verse.registry.clone(), self.primary.verse.font_resolver.clone(), ); - let proj = Self::create_project(id.clone(), verse, self.handler.clone()); + let mut proj = Self::create_project(id.clone(), verse, self.handler.clone()); + proj.reason.see(reason_by_entry_change()); self.remove_dedicates(&id); self.dedicates.push(proj); @@ -761,6 +816,26 @@ impl ProjectInsState { } } + /// Compile the document once if there is any reason and the entry is + /// active. (this is used for experimenting typst.node compilations) + #[must_use] + pub fn may_compile2( + &mut self, + compute: impl FnOnce(&Arc>), + ) -> Option Arc>> { + if !self.reason.any() || self.verse.entry_state().is_inactive() { + return None; + } + + let snap = self.snapshot(); + self.reason = Default::default(); + Some(move || { + let compiled = WorldComputeGraph::new(snap); + compute(&compiled); + compiled + }) + } + /// Compile the document once if there is any reason and the entry is /// active. #[must_use] diff --git a/crates/tinymist-project/src/lib.rs b/crates/tinymist-project/src/lib.rs index a5012fb6..365ef50f 100644 --- a/crates/tinymist-project/src/lib.rs +++ b/crates/tinymist-project/src/lib.rs @@ -3,19 +3,34 @@ mod args; mod compiler; mod entry; -pub mod font; -mod lock; mod model; + +#[cfg(feature = "lsp")] +pub mod font; +#[cfg(feature = "lsp")] +mod lock; +#[cfg(feature = "lsp")] +mod lsp; +#[cfg(feature = "system")] mod watch; +#[cfg(feature = "system")] pub mod world; + pub use args::*; pub use compiler::*; pub use entry::*; -pub use lock::*; pub use model::*; + +#[cfg(feature = "lsp")] +pub use lock::*; +#[cfg(feature = "lsp")] +pub use lsp::*; #[cfg(feature = "system")] pub use watch::*; #[cfg(feature = "system")] pub use world::*; pub use tinymist_world::{CompileSnapshot, ExportSignal, ProjectInsId}; + +/// The default project route priority assigned to user actions. +pub const PROJECT_ROUTE_USER_ACTION_PRIORITY: u32 = 256; diff --git a/crates/tinymist-project/src/lock.rs b/crates/tinymist-project/src/lock.rs index abdcf4d4..74cd2736 100644 --- a/crates/tinymist-project/src/lock.rs +++ b/crates/tinymist-project/src/lock.rs @@ -16,8 +16,6 @@ use crate::{LockFile, LockFileCompat, LspWorld, ProjectPathMaterial, LOCK_VERSIO pub const LOCK_FILENAME: &str = "tinymist.lock"; -pub const PROJECT_ROUTE_USER_ACTION_PRIORITY: u32 = 256; - impl LockFile { pub fn get_document(&self, id: &Id) -> Option<&ProjectInput> { self.document.iter().find(|i| &i.id == id) diff --git a/crates/tinymist-project/src/lsp.rs b/crates/tinymist-project/src/lsp.rs new file mode 100644 index 00000000..6bc33e79 --- /dev/null +++ b/crates/tinymist-project/src/lsp.rs @@ -0,0 +1,260 @@ +use std::path::Path; +use std::{borrow::Cow, sync::Arc}; + +use tinymist_std::error::prelude::*; +use tinymist_std::{bail, ImmutPath}; +use tinymist_task::ExportTarget; +use tinymist_world::args::*; +use tinymist_world::config::CompileFontOpts; +use tinymist_world::font::system::SystemFontSearcher; +use tinymist_world::package::{http::HttpRegistry, RegistryPathMapper}; +use tinymist_world::vfs::{system::SystemAccessModel, Vfs}; +use tinymist_world::{ + CompileSnapshot, CompilerFeat, CompilerUniverse, CompilerWorld, EntryOpts, EntryState, +}; +use typst::foundations::{Dict, Str, Value}; +use typst::utils::LazyHash; + +use crate::ProjectInput; + +use crate::font::TinymistFontResolver; +use crate::{CompiledArtifact, Interrupt}; + +/// Compiler feature for LSP universe and worlds without typst.ts to implement +/// more for tinymist. type trait of [`CompilerUniverse`]. +#[derive(Debug, Clone, Copy)] +pub struct LspCompilerFeat; + +impl CompilerFeat for LspCompilerFeat { + /// Uses [`TinymistFontResolver`] directly. + type FontResolver = TinymistFontResolver; + /// It accesses a physical file system. + type AccessModel = SystemAccessModel; + /// It performs native HTTP requests for fetching package data. + type Registry = HttpRegistry; +} + +/// LSP universe that spawns LSP worlds. +pub type LspUniverse = CompilerUniverse; +/// LSP world that holds compilation resources +pub type LspWorld = CompilerWorld; +/// LSP compile snapshot. +pub type LspCompileSnapshot = CompileSnapshot; +/// LSP compiled artifact. +pub type LspCompiledArtifact = CompiledArtifact; +/// LSP interrupt. +pub type LspInterrupt = Interrupt; +/// Immutable prehashed reference to dictionary. +pub type ImmutDict = Arc>; + +/// World provider for LSP universe and worlds. +pub trait WorldProvider { + /// Get the entry options from the arguments. + fn entry(&self) -> Result; + /// Get a universe instance from the given arguments. + fn resolve(&self) -> Result; +} + +impl WorldProvider for CompileOnceArgs { + fn resolve(&self) -> Result { + let entry = self.entry()?.try_into()?; + let inputs = self.resolve_inputs().unwrap_or_default(); + let fonts = Arc::new(LspUniverseBuilder::resolve_fonts(self.font.clone())?); + let package = LspUniverseBuilder::resolve_package( + self.cert.as_deref().map(From::from), + Some(&self.package), + ); + + // todo: more export targets + Ok(LspUniverseBuilder::build( + entry, + ExportTarget::Paged, + inputs, + fonts, + package, + )) + } + + fn entry(&self) -> Result { + let mut cwd = None; + let mut cwd = move || { + cwd.get_or_insert_with(|| { + std::env::current_dir().context("failed to get current directory") + }) + .clone() + }; + + let main = { + let input = self.input.as_ref().context("entry file must be provided")?; + let input = Path::new(&input); + if input.is_absolute() { + input.to_owned() + } else { + cwd()?.join(input) + } + }; + + let root = if let Some(root) = &self.root { + if root.is_absolute() { + root.clone() + } else { + cwd()?.join(root) + } + } else { + main.parent() + .context("entry file don't have a valid parent as root")? + .to_owned() + }; + + let relative_main = match main.strip_prefix(&root) { + Ok(relative_main) => relative_main, + Err(_) => { + log::error!("entry file must be inside the root, file: {main:?}, root: {root:?}"); + bail!("entry file must be inside the root, file: {main:?}, root: {root:?}"); + } + }; + + Ok(EntryOpts::new_rooted( + root.clone(), + Some(relative_main.to_owned()), + )) + } +} + +// todo: merge me with the above impl +impl WorldProvider for (ProjectInput, ImmutPath) { + fn resolve(&self) -> Result { + let (proj, lock_dir) = self; + let entry = self.entry()?.try_into()?; + let inputs = proj + .inputs + .iter() + .map(|(k, v)| (Str::from(k.as_str()), Value::Str(Str::from(v.as_str())))) + .collect(); + let fonts = LspUniverseBuilder::resolve_fonts(CompileFontArgs { + font_paths: { + proj.font_paths + .iter() + .flat_map(|p| p.to_abs_path(lock_dir)) + .collect::>() + }, + ignore_system_fonts: !proj.system_fonts, + })?; + let package = LspUniverseBuilder::resolve_package( + // todo: recover certificate path + None, + Some(&CompilePackageArgs { + package_path: proj + .package_path + .as_ref() + .and_then(|p| p.to_abs_path(lock_dir)), + package_cache_path: proj + .package_cache_path + .as_ref() + .and_then(|p| p.to_abs_path(lock_dir)), + }), + ); + + // todo: more export targets + Ok(LspUniverseBuilder::build( + entry, + ExportTarget::Paged, + Arc::new(LazyHash::new(inputs)), + Arc::new(fonts), + package, + )) + } + + fn entry(&self) -> Result { + let (proj, lock_dir) = self; + + let entry = proj + .main + .to_abs_path(lock_dir) + .context("failed to resolve entry file")?; + + let root = if let Some(root) = &proj.root { + root.to_abs_path(lock_dir) + .context("failed to resolve root")? + } else { + lock_dir.as_ref().to_owned() + }; + + if !entry.starts_with(&root) { + bail!("entry file must be in the root directory, {entry:?}, {root:?}"); + } + + let relative_entry = match entry.strip_prefix(&root) { + Ok(relative_entry) => relative_entry, + Err(_) => bail!("entry path must be inside the root: {}", entry.display()), + }; + + Ok(EntryOpts::new_rooted( + root.clone(), + Some(relative_entry.to_owned()), + )) + } +} + +/// Builder for LSP universe. +pub struct LspUniverseBuilder; + +impl LspUniverseBuilder { + /// Create [`LspUniverse`] with the given options. + /// See [`LspCompilerFeat`] for instantiation details. + pub fn build( + entry: EntryState, + export_target: ExportTarget, + inputs: ImmutDict, + font_resolver: Arc, + package_registry: HttpRegistry, + ) -> LspUniverse { + let registry = Arc::new(package_registry); + let resolver = Arc::new(RegistryPathMapper::new(registry.clone())); + + LspUniverse::new_raw( + entry, + matches!(export_target, ExportTarget::Html), + Some(inputs), + Vfs::new(resolver, SystemAccessModel {}), + registry, + font_resolver, + ) + } + + /// Resolve fonts from given options. + pub fn only_embedded_fonts() -> Result { + let mut searcher = SystemFontSearcher::new(); + searcher.resolve_opts(CompileFontOpts { + font_profile_cache_path: Default::default(), + font_paths: vec![], + no_system_fonts: true, + with_embedded_fonts: typst_assets::fonts().map(Cow::Borrowed).collect(), + })?; + Ok(searcher.into()) + } + + /// Resolve fonts from given options. + pub fn resolve_fonts(args: CompileFontArgs) -> Result { + let mut searcher = SystemFontSearcher::new(); + searcher.resolve_opts(CompileFontOpts { + font_profile_cache_path: Default::default(), + font_paths: args.font_paths, + no_system_fonts: args.ignore_system_fonts, + with_embedded_fonts: typst_assets::fonts().map(Cow::Borrowed).collect(), + })?; + Ok(searcher.into()) + } + + /// Resolve package registry from given options. + pub fn resolve_package( + cert_path: Option, + args: Option<&CompilePackageArgs>, + ) -> HttpRegistry { + HttpRegistry::new( + cert_path, + args.and_then(|args| Some(args.package_path.clone()?.into())), + args.and_then(|args| Some(args.package_cache_path.clone()?.into())), + ) + } +} diff --git a/crates/tinymist-project/src/watch.rs b/crates/tinymist-project/src/watch.rs index a2281875..cfe06da8 100644 --- a/crates/tinymist-project/src/watch.rs +++ b/crates/tinymist-project/src/watch.rs @@ -17,7 +17,7 @@ use tinymist_world::vfs::notify::NotifyDeps; use tokio::sync::mpsc; use typst::diag::FileError; -use crate::vfs::{ +use tinymist_world::vfs::{ notify::{FilesystemEvent, NotifyMessage, UpstreamUpdateEvent}, system::SystemAccessModel, FileChangeSet, FileSnapshot, PathAccessModel, @@ -174,7 +174,7 @@ impl NotifyActor { // function entries to handle some event match event { ActorEvent::Message(None) => { - log::info!("failed to get event, exiting..."); + log::info!("NotifyActor: failed to get event, exiting..."); break 'event_loop; } ActorEvent::Message(Some(Settle)) => { @@ -520,7 +520,7 @@ pub async fn watch_deps( inbox: mpsc::UnboundedReceiver, interrupted_by_events: impl FnMut(FilesystemEvent) + Send + Sync + 'static, ) { - log::debug!("start watching files..."); + log::info!("NotifyActor: start watching files..."); // Watch messages to notify tokio::spawn(NotifyActor::new(interrupted_by_events).run(inbox)); } diff --git a/crates/tinymist-project/src/world.rs b/crates/tinymist-project/src/world.rs index 082cadc3..05c7a61b 100644 --- a/crates/tinymist-project/src/world.rs +++ b/crates/tinymist-project/src/world.rs @@ -8,240 +8,3 @@ pub use tinymist_world::{font, package, vfs}; pub use tinymist_world::{ CompilerUniverse, CompilerWorld, EntryOpts, EntryState, RevisingUniverse, TaskInputs, }; - -use std::path::Path; -use std::{borrow::Cow, sync::Arc}; - -use tinymist_std::error::prelude::*; -use tinymist_std::{bail, ImmutPath}; -use tinymist_world::font::system::SystemFontSearcher; -use tinymist_world::package::{http::HttpRegistry, RegistryPathMapper}; -use tinymist_world::vfs::{system::SystemAccessModel, Vfs}; -use tinymist_world::CompilerFeat; -use typst::foundations::{Dict, Str, Value}; -use typst::utils::LazyHash; - -use crate::font::TinymistFontResolver; -use crate::ProjectInput; - -/// Compiler feature for LSP universe and worlds without typst.ts to implement -/// more for tinymist. type trait of [`CompilerUniverse`]. -#[derive(Debug, Clone, Copy)] -pub struct LspCompilerFeat; - -impl CompilerFeat for LspCompilerFeat { - /// Uses [`TinymistFontResolver`] directly. - type FontResolver = TinymistFontResolver; - /// It accesses a physical file system. - type AccessModel = SystemAccessModel; - /// It performs native HTTP requests for fetching package data. - type Registry = HttpRegistry; -} - -/// LSP universe that spawns LSP worlds. -pub type LspUniverse = CompilerUniverse; -/// LSP world that holds compilation resources -pub type LspWorld = CompilerWorld; -/// Immutable prehashed reference to dictionary. -pub type ImmutDict = Arc>; - -/// World provider for LSP universe and worlds. -pub trait WorldProvider { - /// Get the entry options from the arguments. - fn entry(&self) -> Result; - /// Get a universe instance from the given arguments. - fn resolve(&self) -> Result; -} - -impl WorldProvider for CompileOnceArgs { - fn resolve(&self) -> Result { - let entry = self.entry()?.try_into()?; - let inputs = self.resolve_inputs().unwrap_or_default(); - let fonts = Arc::new(LspUniverseBuilder::resolve_fonts(self.font.clone())?); - let package = LspUniverseBuilder::resolve_package( - self.cert.as_deref().map(From::from), - Some(&self.package), - ); - - Ok(LspUniverseBuilder::build(entry, inputs, fonts, package)) - } - - fn entry(&self) -> Result { - let mut cwd = None; - let mut cwd = move || { - cwd.get_or_insert_with(|| { - std::env::current_dir().context("failed to get current directory") - }) - .clone() - }; - - let main = { - let input = self.input.as_ref().context("entry file must be provided")?; - let input = Path::new(&input); - if input.is_absolute() { - input.to_owned() - } else { - cwd()?.join(input) - } - }; - - let root = if let Some(root) = &self.root { - if root.is_absolute() { - root.clone() - } else { - cwd()?.join(root) - } - } else { - main.parent() - .context("entry file don't have a valid parent as root")? - .to_owned() - }; - - let relative_main = match main.strip_prefix(&root) { - Ok(relative_main) => relative_main, - Err(_) => { - log::error!("entry file must be inside the root, file: {main:?}, root: {root:?}"); - bail!("entry file must be inside the root, file: {main:?}, root: {root:?}"); - } - }; - - Ok(EntryOpts::new_rooted( - root.clone(), - Some(relative_main.to_owned()), - )) - } -} - -// todo: merge me with the above impl -impl WorldProvider for (ProjectInput, ImmutPath) { - fn resolve(&self) -> Result { - let (proj, lock_dir) = self; - let entry = self.entry()?.try_into()?; - let inputs = proj - .inputs - .iter() - .map(|(k, v)| (Str::from(k.as_str()), Value::Str(Str::from(v.as_str())))) - .collect(); - let fonts = LspUniverseBuilder::resolve_fonts(CompileFontArgs { - font_paths: { - proj.font_paths - .iter() - .flat_map(|p| p.to_abs_path(lock_dir)) - .collect::>() - }, - ignore_system_fonts: !proj.system_fonts, - })?; - let package = LspUniverseBuilder::resolve_package( - // todo: recover certificate path - None, - Some(&CompilePackageArgs { - package_path: proj - .package_path - .as_ref() - .and_then(|p| p.to_abs_path(lock_dir)), - package_cache_path: proj - .package_cache_path - .as_ref() - .and_then(|p| p.to_abs_path(lock_dir)), - }), - ); - - Ok(LspUniverseBuilder::build( - entry, - Arc::new(LazyHash::new(inputs)), - Arc::new(fonts), - package, - )) - } - - fn entry(&self) -> Result { - let (proj, lock_dir) = self; - - let entry = proj - .main - .to_abs_path(lock_dir) - .context("failed to resolve entry file")?; - - let root = if let Some(root) = &proj.root { - root.to_abs_path(lock_dir) - .context("failed to resolve root")? - } else { - lock_dir.as_ref().to_owned() - }; - - if !entry.starts_with(&root) { - bail!("entry file must be in the root directory, {entry:?}, {root:?}"); - } - - let relative_entry = match entry.strip_prefix(&root) { - Ok(relative_entry) => relative_entry, - Err(_) => bail!("entry path must be inside the root: {}", entry.display()), - }; - - Ok(EntryOpts::new_rooted( - root.clone(), - Some(relative_entry.to_owned()), - )) - } -} - -/// Builder for LSP universe. -pub struct LspUniverseBuilder; - -impl LspUniverseBuilder { - /// Create [`LspUniverse`] with the given options. - /// See [`LspCompilerFeat`] for instantiation details. - pub fn build( - entry: EntryState, - inputs: ImmutDict, - font_resolver: Arc, - package_registry: HttpRegistry, - ) -> LspUniverse { - let registry = Arc::new(package_registry); - let resolver = Arc::new(RegistryPathMapper::new(registry.clone())); - - LspUniverse::new_raw( - entry, - Some(inputs), - Vfs::new(resolver, SystemAccessModel {}), - registry, - font_resolver, - ) - } - - /// Resolve fonts from given options. - pub fn only_embedded_fonts() -> Result { - let mut searcher = SystemFontSearcher::new(); - searcher.resolve_opts(CompileFontOpts { - font_profile_cache_path: Default::default(), - font_paths: vec![], - no_system_fonts: true, - with_embedded_fonts: typst_assets::fonts().map(Cow::Borrowed).collect(), - })?; - Ok(searcher.into()) - } - - /// Resolve fonts from given options. - pub fn resolve_fonts(args: CompileFontArgs) -> Result { - let mut searcher = SystemFontSearcher::new(); - searcher.resolve_opts(CompileFontOpts { - font_profile_cache_path: Default::default(), - font_paths: args.font_paths, - no_system_fonts: args.ignore_system_fonts, - with_embedded_fonts: typst_assets::fonts().map(Cow::Borrowed).collect(), - })?; - Ok(searcher.into()) - } - - /// Resolve package registry from given options. - pub fn resolve_package( - cert_path: Option, - args: Option<&CompilePackageArgs>, - ) -> HttpRegistry { - HttpRegistry::new( - cert_path, - args.and_then(|args| Some(args.package_path.clone()?.into())), - args.and_then(|args| Some(args.package_cache_path.clone()?.into())), - ) - } -} diff --git a/crates/tinymist-query/Cargo.toml b/crates/tinymist-query/Cargo.toml index a7d7d9c8..29eceec5 100644 --- a/crates/tinymist-query/Cargo.toml +++ b/crates/tinymist-query/Cargo.toml @@ -56,7 +56,7 @@ triomphe.workspace = true base64.workspace = true typlite.workspace = true tinymist-world = { workspace = true } -tinymist-project = { workspace = true, features = ["system"] } +tinymist-project = { workspace = true, features = ["lsp"] } tinymist-analysis.workspace = true tinymist-derive.workspace = true tinymist-std.workspace = true @@ -71,7 +71,6 @@ sha2 = { version = "0.10" } hex = { version = "0.4" } [features] -no-content-hint = ["tinymist-project/no-content-hint"] [lints] workspace = true diff --git a/crates/tinymist-query/src/analysis.rs b/crates/tinymist-query/src/analysis.rs index 559b704e..1bc27118 100644 --- a/crates/tinymist-query/src/analysis.rs +++ b/crates/tinymist-query/src/analysis.rs @@ -358,7 +358,7 @@ mod type_check_tests { .map(|bounds| (bounds.name(), bounds)) .collect::>(); - vars.sort_by(|x, y| x.1.var.cmp(&y.1.var)); + vars.sort_by(|x, y| x.1.var.strict_cmp(&y.1.var)); for (name, bounds) in vars { writeln!(f, "{name:?} = {:?}", info.simplify(bounds.as_type(), true))?; @@ -590,7 +590,7 @@ mod call_info_tests { }; let mut w = ci.arg_mapping.iter().collect::>(); - w.sort_by(|x, y| x.0.span().number().cmp(&y.0.span().number())); + w.sort_by(|x, y| x.0.span().into_raw().cmp(&y.0.span().into_raw())); for (arg, arg_call_info) in w { writeln!(f, "{} -> {:?}", arg.clone().into_text(), arg_call_info)?; diff --git a/crates/tinymist-query/src/analysis/completion/field_access.rs b/crates/tinymist-query/src/analysis/completion/field_access.rs index 51d66e5f..eb01bccd 100644 --- a/crates/tinymist-query/src/analysis/completion/field_access.rs +++ b/crates/tinymist-query/src/analysis/completion/field_access.rs @@ -37,13 +37,13 @@ impl CompletionPair<'_, '_, '_> { /// Add completions for all fields on a value. fn value_field_access_completions(&mut self, target: &LinkedNode) -> Option<()> { let (value, styles) = self.worker.ctx.analyze_expr(target).into_iter().next()?; - for (name, value, _) in value.ty().scope().iter() { - self.value_completion(Some(name.clone()), value, true, None); + for (name, bind) in value.ty().scope().iter() { + self.value_completion(Some(name.clone()), bind.read(), true, None); } if let Some(scope) = value.scope() { - for (name, value, _) in scope.iter() { - self.value_completion(Some(name.clone()), value, true, None); + for (name, bind) in scope.iter() { + self.value_completion(Some(name.clone()), bind.read(), true, None); } } @@ -55,7 +55,7 @@ impl CompletionPair<'_, '_, '_> { // this value's type, so accessing it should not fail. self.value_completion( Some(field.into()), - &value.field(field).unwrap(), + &value.field(field, ()).unwrap(), false, None, ); @@ -108,15 +108,6 @@ impl CompletionPair<'_, '_, '_> { } } } - Value::Plugin(plugin) => { - for name in plugin.iter() { - self.push_completion(Completion { - kind: CompletionKind::Func, - label: name.clone(), - ..Completion::default() - }) - } - } _ => {} } diff --git a/crates/tinymist-query/src/analysis/completion/import.rs b/crates/tinymist-query/src/analysis/completion/import.rs index 9f1970eb..628d4f98 100644 --- a/crates/tinymist-query/src/analysis/completion/import.rs +++ b/crates/tinymist-query/src/analysis/completion/import.rs @@ -95,9 +95,9 @@ impl CompletionPair<'_, '_, '_> { ) { // Select the source by `comps` let value = self.worker.ctx.module_by_syntax(source); - let value = comps - .iter() - .fold(value.as_ref(), |value, comp| value?.scope()?.get(comp)); + let value = comps.iter().fold(value.as_ref(), |value, comp| { + value?.scope()?.get(comp)?.read().into() + }); let Some(scope) = value.and_then(|v| v.scope()) else { return; }; @@ -125,9 +125,9 @@ impl CompletionPair<'_, '_, '_> { self.snippet_completion("*", "*", "Import everything."); } - for (name, value, _) in scope.iter() { + for (name, bind) in scope.iter() { if seen.iter().all(|item| item.as_str() != name) { - self.value_completion(Some(name.clone()), value, false, None); + self.value_completion(Some(name.clone()), bind.read(), false, None); } } } diff --git a/crates/tinymist-query/src/analysis/completion/kind.rs b/crates/tinymist-query/src/analysis/completion/kind.rs index 403d5c25..c6abbf60 100644 --- a/crates/tinymist-query/src/analysis/completion/kind.rs +++ b/crates/tinymist-query/src/analysis/completion/kind.rs @@ -114,7 +114,7 @@ impl FnCompletionFeat { | Value::Fraction(..) | Value::Color(..) | Value::Gradient(..) - | Value::Pattern(..) + | Value::Tiling(..) | Value::Symbol(..) | Value::Version(..) | Value::Str(..) @@ -129,7 +129,6 @@ impl FnCompletionFeat { | Value::Dict(..) | Value::Args(..) | Value::Module(..) - | Value::Plugin(..) | Value::Dyn(..) => {} }, Ty::Func(sig) => self.check_sig(sig, pos), @@ -263,7 +262,7 @@ fn fold_ty_kind<'a>(tys: impl Iterator) -> CompletionKind { pub(crate) fn value_to_completion_kind(value: &Value) -> CompletionKind { match value { Value::Func(..) => CompletionKind::Func, - Value::Plugin(..) | Value::Module(..) => CompletionKind::Module, + Value::Module(..) => CompletionKind::Module, Value::Type(..) => CompletionKind::Type, Value::Symbol(s) => CompletionKind::Symbol(s.get()), Value::None @@ -278,7 +277,7 @@ pub(crate) fn value_to_completion_kind(value: &Value) -> CompletionKind { | Value::Fraction(..) | Value::Color(..) | Value::Gradient(..) - | Value::Pattern(..) + | Value::Tiling(..) | Value::Version(..) | Value::Str(..) | Value::Bytes(..) diff --git a/crates/tinymist-query/src/analysis/completion/mode.rs b/crates/tinymist-query/src/analysis/completion/mode.rs index e3208521..5dae6f1a 100644 --- a/crates/tinymist-query/src/analysis/completion/mode.rs +++ b/crates/tinymist-query/src/analysis/completion/mode.rs @@ -120,7 +120,7 @@ impl CompletionPair<'_, '_, '_> { if !is_triggered_by_punc(self.worker.trigger_character) && matches!( self.cursor.leaf.kind(), - SyntaxKind::Text | SyntaxKind::MathIdent + SyntaxKind::Text | SyntaxKind::MathIdent | SyntaxKind::MathText ) { self.cursor.from = self.cursor.leaf.offset(); diff --git a/crates/tinymist-query/src/analysis/completion/param.rs b/crates/tinymist-query/src/analysis/completion/param.rs index 9e1159f9..bca8f08a 100644 --- a/crates/tinymist-query/src/analysis/completion/param.rs +++ b/crates/tinymist-query/src/analysis/completion/param.rs @@ -49,10 +49,10 @@ impl CompletionPair<'_, '_, '_> { let captures = visitor.finish(); // Converts the captures into completions. - for (name, value, _) in captures.iter() { + for (name, bind) in captures.iter() { if !bindings.contains(name) { let docs = "Parametrizes the captured variable."; - self.value_completion(Some(name.clone()), value, false, Some(docs)); + self.value_completion(Some(name.clone()), bind.read(), false, Some(docs)); } } diff --git a/crates/tinymist-query/src/analysis/completion/scope.rs b/crates/tinymist-query/src/analysis/completion/scope.rs index 89490766..a1785af0 100644 --- a/crates/tinymist-query/src/analysis/completion/scope.rs +++ b/crates/tinymist-query/src/analysis/completion/scope.rs @@ -28,9 +28,9 @@ impl Defines { pub fn insert_scope(&mut self, scope: &Scope) { // filter(Some(value)) && - for (name, value, _) in scope.iter() { + for (name, bind) in scope.iter() { if !self.defines.contains_key(name) { - self.insert(name.clone(), Ty::Value(InsTy::new(value.clone()))); + self.insert(name.clone(), Ty::Value(InsTy::new(bind.read().clone()))); } } } @@ -301,8 +301,8 @@ impl CompletionScopeChecker<'_> { for name in fields_on(ty) { self.defines.insert((*name).into(), Ty::Any); } - for (name, value, _) in ty.scope().iter() { - let ty = Ty::Value(InsTy::new(value.clone())); + for (name, bind) in ty.scope().iter() { + let ty = Ty::Value(InsTy::new(bind.read().clone())); self.defines.insert(name.into(), ty); } } diff --git a/crates/tinymist-query/src/analysis/completion/typst_specific.rs b/crates/tinymist-query/src/analysis/completion/typst_specific.rs index d675a42a..389c9109 100644 --- a/crates/tinymist-query/src/analysis/completion/typst_specific.rs +++ b/crates/tinymist-query/src/analysis/completion/typst_specific.rs @@ -113,7 +113,7 @@ impl CompletionPair<'_, '_, '_> { if !self.worker.seen_casts.insert(hash128(&label)) { continue; } - let label: EcoString = label.as_str().into(); + let label: EcoString = label.resolve().as_str().into(); let completion = Completion { kind: CompletionKind::Reference, apply: Some(eco_format!( diff --git a/crates/tinymist-query/src/analysis/definition.rs b/crates/tinymist-query/src/analysis/definition.rs index a3c19249..0fcc8fc4 100644 --- a/crates/tinymist-query/src/analysis/definition.rs +++ b/crates/tinymist-query/src/analysis/definition.rs @@ -144,7 +144,7 @@ fn bib_definition( key: &str, ) -> Option { let bib_elem = BibliographyElem::find(introspector.track()).ok()?; - let Value::Array(paths) = bib_elem.path().clone().into_value() else { + let Value::Array(paths) = bib_elem.sources.clone().into_value() else { return None; }; @@ -164,7 +164,7 @@ fn ref_definition( name: &str, ref_expr: ast::Expr, ) -> Option { - let label = Label::new(name); + let label = Label::construct(name.into()); let sel = Selector::Label(label); // if it is a label, we put the selection range to itself @@ -243,7 +243,7 @@ pub fn resolve_call_target(ctx: &Arc, node: &SyntaxNode) -> Optio let field = access.field().get(); let values = ctx.analyze_expr(target.to_untyped()); if let Some((this, func_ptr)) = values.into_iter().find_map(|(this, _styles)| { - if let Some(Value::Func(func)) = this.ty().scope().get(field) { + if let Some(Value::Func(func)) = this.ty().scope().get(field).map(|b| b.read()) { return Some((this, func.clone())); } @@ -288,7 +288,8 @@ fn is_same_native_func(x: Option<&Func>, y: &Func) -> bool { static WITH_FUNC: LazyLock> = LazyLock::new(|| { let fn_ty = Type::of::(); - let Some(Value::Func(func)) = fn_ty.scope().get("with") else { + let bind = fn_ty.scope().get("with")?; + let Value::Func(func) = bind.read() else { return None; }; Some(func) @@ -296,7 +297,8 @@ static WITH_FUNC: LazyLock> = LazyLock::new(|| { static WHERE_FUNC: LazyLock> = LazyLock::new(|| { let fn_ty = Type::of::(); - let Some(Value::Func(func)) = fn_ty.scope().get("where") else { + let bind = fn_ty.scope().get("where")?; + let Value::Func(func) = bind.read() else { return None; }; Some(func) @@ -313,7 +315,9 @@ fn value_to_def(value: Value, name: impl FnOnce() -> Option>) -> O let decl = Decl::func(s.cast().unwrap()); Definition::new(decl.into(), Some(val)) } - Value::Module(module) => Definition::new_var(module.name().into(), val), + Value::Module(module) => { + Definition::new_var(Interned::new_str(module.name().unwrap()), val) + } _v => Definition::new_var(name()?, val), }) } diff --git a/crates/tinymist-query/src/analysis/signature.rs b/crates/tinymist-query/src/analysis/signature.rs index 458391c7..ea7382dd 100644 --- a/crates/tinymist-query/src/analysis/signature.rs +++ b/crates/tinymist-query/src/analysis/signature.rs @@ -538,7 +538,7 @@ pub fn func_signature(func: Func) -> Signature { analyze_closure_signature(closure.clone(), &mut add_param); None } - Repr::Element(..) | Repr::Native(..) => { + Repr::Element(..) | Repr::Native(..) | Repr::Plugin(..) => { for param in func.params().unwrap() { add_param(Interned::new(ParamTy { name: param.name.into(), diff --git a/crates/tinymist-query/src/analysis/track_values.rs b/crates/tinymist-query/src/analysis/track_values.rs index bb1a8c6e..e923de27 100644 --- a/crates/tinymist-query/src/analysis/track_values.rs +++ b/crates/tinymist-query/src/analysis/track_values.rs @@ -2,14 +2,14 @@ use comemo::Track; use ecow::*; -use tinymist_std::typst::TypstDocument; +use tinymist_std::typst::{TypstDocument, TypstPagedDocument}; use typst::engine::{Engine, Route, Sink, Traced}; -use typst::eval::Vm; use typst::foundations::{Context, Label, Scopes, Styles, Value}; use typst::introspection::Introspector; use typst::model::BibliographyElem; use typst::syntax::{ast, LinkedNode, Span, SyntaxKind, SyntaxNode}; use typst::World; +use typst_shim::eval::Vm; /// Try to determine a set of possible values for an expression. pub fn analyze_expr(world: &dyn World, node: &LinkedNode) -> EcoVec<(Value, Option)> { @@ -43,7 +43,7 @@ pub fn analyze_expr_(world: &dyn World, node: &SyntaxNode) -> EcoVec<(Value, Opt } } - return typst::trace(world, node.span()); + return typst::trace::(world, node.span()); } }; @@ -64,6 +64,7 @@ pub fn analyze_import_(world: &dyn World, source: &SyntaxNode) -> (Option let traced = Traced::default(); let mut sink = Sink::new(); let engine = Engine { + routines: &typst::ROUTINES, world: world.track(), route: Route::default(), introspector: introspector.track(), @@ -78,9 +79,13 @@ pub fn analyze_import_(world: &dyn World, source: &SyntaxNode) -> (Option Scopes::new(Some(world.library())), Span::detached(), ); - let module = typst_shim::eval::import(&mut vm, source.clone(), source_span, true) - .ok() - .map(Value::Module); + let module = match source.clone() { + Value::Str(path) => typst_shim::eval::import(&mut vm.engine, &path, source_span) + .ok() + .map(Value::Module), + Value::Module(module) => Some(Value::Module(module)), + _ => None, + }; (Some(source), module) } @@ -137,9 +142,9 @@ pub fn analyze_labels(document: &TypstDocument) -> (Vec, usize) { let split = output.len(); // Bibliography keys. - for (key, detail) in BibliographyElem::keys(document.introspector().track()) { + for (label, detail) in BibliographyElem::keys(document.introspector().track()) { output.push(DynLabel { - label: Label::new(key.as_str()), + label, label_desc: detail.clone(), detail: detail.clone(), bib_title: detail, diff --git a/crates/tinymist-query/src/analysis/tyck/convert.rs b/crates/tinymist-query/src/analysis/tyck/convert.rs index 70c9f559..8692cce9 100644 --- a/crates/tinymist-query/src/analysis/tyck/convert.rs +++ b/crates/tinymist-query/src/analysis/tyck/convert.rs @@ -19,7 +19,7 @@ pub fn is_plain_value(value: &Value) -> bool { | Value::Fraction(..) | Value::Color(..) | Value::Gradient(..) - | Value::Pattern(..) + | Value::Tiling(..) | Value::Symbol(..) | Value::Version(..) | Value::Str(..) @@ -44,14 +44,6 @@ pub fn term_value(value: &Value) -> Ty { } // todo: term arguments Value::Args(..) => Ty::Builtin(BuiltinTy::Args), - Value::Plugin(plugin) => { - // todo: create infer variables for plugin functions - let values = plugin - .iter() - .map(|method| (method.as_str().into(), Ty::Func(SigTy::any()))) - .collect(); - Ty::Dict(RecordTy::new(values)) - } Value::Dict(dict) => { let values = dict .iter() @@ -63,7 +55,7 @@ pub fn term_value(value: &Value) -> Ty { let values = module .scope() .iter() - .map(|(k, v, s)| (k.into(), term_value_rec(v, s))) + .map(|(k, b)| (k.into(), term_value_rec(b.read(), b.span()))) .collect(); Ty::Dict(RecordTy::new(values)) } @@ -83,7 +75,6 @@ pub fn term_value_rec(value: &Value, s: Span) -> Ty { | Value::Auto | Value::Array(..) | Value::Args(..) - | Value::Plugin(..) | Value::Dict(..) | Value::Module(..) | Value::Func(..) @@ -99,7 +90,7 @@ pub fn term_value_rec(value: &Value, s: Span) -> Ty { | Value::Fraction(..) | Value::Color(..) | Value::Gradient(..) - | Value::Pattern(..) + | Value::Tiling(..) | Value::Symbol(..) | Value::Version(..) | Value::Str(..) diff --git a/crates/tinymist-query/src/code_context.rs b/crates/tinymist-query/src/code_context.rs index 610895c6..d4e7d2a3 100644 --- a/crates/tinymist-query/src/code_context.rs +++ b/crates/tinymist-query/src/code_context.rs @@ -104,7 +104,7 @@ impl SemanticRequest for InteractCodeContextRequest { let _ = world.map_shadow_by_id( mapped_source.id(), - Bytes::from(mapped_source.text().as_bytes()), + Bytes::new(mapped_source.text().as_bytes().to_vec()), ); world.take_db(); @@ -130,7 +130,7 @@ impl SemanticRequest for InteractCodeContextRequest { .collect(); let _ = world.map_shadow_by_id( mapped_source.id(), - Bytes::from(source.text().as_bytes()), + Bytes::new(source.text().as_bytes().to_vec()), ); Some(InteractCodeContextResponse::StyleAt { style }) diff --git a/crates/tinymist-query/src/code_lens.rs b/crates/tinymist-query/src/code_lens.rs index 1f2dff2b..01ca0f4c 100644 --- a/crates/tinymist-query/src/code_lens.rs +++ b/crates/tinymist-query/src/code_lens.rs @@ -33,7 +33,15 @@ impl SemanticRequest for CodeLensRequest { res.push(doc_lens("Profile", vec!["profile".into()])); res.push(doc_lens("Preview", vec!["preview".into()])); - res.push(doc_lens("Export PDF", vec!["export-pdf".into()])); + + let is_html = ctx.world.library.features.is_enabled(typst::Feature::Html); + + if is_html { + res.push(doc_lens("Export HTML", vec!["export-html".into()])); + } else { + res.push(doc_lens("Export PDF", vec!["export-pdf".into()])); + } + res.push(doc_lens("More ..", vec!["more".into()])); Some(res) diff --git a/crates/tinymist-query/src/docs/convert.rs b/crates/tinymist-query/src/docs/convert.rs index a203e7fe..686748a6 100644 --- a/crates/tinymist-query/src/docs/convert.rs +++ b/crates/tinymist-query/src/docs/convert.rs @@ -23,7 +23,7 @@ pub(crate) fn convert_docs(ctx: &SharedContext, content: &str) -> StrResult { Some(()) } + _ => None, } } diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@bracket_strong.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@bracket_strong.typ.snap index d24a6501..f50a4da0 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@bracket_strong.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@bracket_strong.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on t (63..64) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/bracket_strong.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(content, delta: int) => strong" }, - "sortText": "180", + "sortText": "182", "textEdit": { "newText": "strong(${1:})", "range": { @@ -36,7 +35,7 @@ snapshot_kind: text "labelDetails": { "description": "(content, delta: int) => strong" }, - "sortText": "181", + "sortText": "183", "textEdit": { "newText": "strong[${1:}]", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@builtin_shadow.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@builtin_shadow.typ.snap index 5386de5b..601156b7 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@builtin_shadow.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@builtin_shadow.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on ( (71..72) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/builtin_shadow.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(fill-rest: false) => none" }, - "sortText": "126", + "sortText": "127", "textEdit": { "newText": "pagebreak()${1:}", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@builtin_shadow_existing.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@builtin_shadow_existing.typ.snap index df5041c6..72811d9e 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@builtin_shadow_existing.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@builtin_shadow_existing.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on ( (70..71) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/builtin_shadow_existing.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(fill-rest: false) => none" }, - "sortText": "127", + "sortText": "128", "textEdit": { "newText": "pagebreak()${1:}", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_markup.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_markup.typ.snap index 46d137f0..27ba2264 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_markup.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_markup.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on (23..24) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/colon_markup.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(content, b: content | none, bl: content | none, br: content | none, t: content | none, tl: content | none, tr: content | none) => attach" }, - "sortText": "075", + "sortText": "076", "textEdit": { "newText": " attach(${1:})", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_math.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_math.typ.snap index 96ea07e5..01a31fb3 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_math.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@colon_math.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on (23..24) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/colon_math.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(content, b: content | none, bl: content | none, br: content | none, t: content | none, tl: content | none, tr: content | none) => attach" }, - "sortText": "075", + "sortText": "076", "textEdit": { "newText": " attach(${1:})", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@context_code_init.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@context_code_init.typ.snap index 868f99ab..8cc77600 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@context_code_init.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@context_code_init.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (36..37) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/context_code_init.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(to: \"even\" | \"odd\" | none, weak: bool) => pagebreak" }, - "sortText": "127", + "sortText": "128", "textEdit": { "newText": "pagebreak()${1:}", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@context_init.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@context_init.typ.snap index 605200ca..cb352cd9 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@context_init.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@context_init.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (34..35) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/context_init.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(to: \"even\" | \"odd\" | none, weak: bool) => pagebreak" }, - "sortText": "127", + "sortText": "128", "textEdit": { "newText": "pagebreak()${1:}", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@hash.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@hash.typ.snap index 73c6ebf6..93f599da 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@hash.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@hash.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (26..27) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/hash.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(to: \"even\" | \"odd\" | none, weak: bool) => pagebreak" }, - "sortText": "126", + "sortText": "127", "textEdit": { "newText": "pagebreak()${1:}", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@hash_ident.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@hash_ident.typ.snap index 6ffed87c..d59fa561 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@hash_ident.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@hash_ident.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (28..29) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/hash_ident.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(to: \"even\" | \"odd\" | none, weak: bool) => pagebreak" }, - "sortText": "126", + "sortText": "127", "textEdit": { "newText": "pagebreak()${1:}", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@hash_math.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@hash_math.typ.snap index 841bbc12..1c8101e1 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@hash_math.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@hash_math.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (28..29) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/hash_math.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(to: \"even\" | \"odd\" | none, weak: bool) => pagebreak" }, - "sortText": "126", + "sortText": "127", "textEdit": { "newText": "pagebreak()${1:}", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@hash_math_ident.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@hash_math_ident.typ.snap index 36198ac2..cc9da42c 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@hash_math_ident.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@hash_math_ident.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (30..31) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/hash_math_ident.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(to: \"even\" | \"odd\" | none, weak: bool) => pagebreak" }, - "sortText": "126", + "sortText": "127", "textEdit": { "newText": "pagebreak()${1:}", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@let_closure_init.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@let_closure_init.typ.snap index e241d0f7..af6c976a 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@let_closure_init.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@let_closure_init.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (40..41) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/let_closure_init.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(to: \"even\" | \"odd\" | none, weak: bool) => pagebreak" }, - "sortText": "128", + "sortText": "129", "textEdit": { "newText": "pagebreak()${1:}", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@let_fn_init.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@let_fn_init.typ.snap index aa22cd59..07ecadcd 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@let_fn_init.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@let_fn_init.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (37..38) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/let_fn_init.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(to: \"even\" | \"odd\" | none, weak: bool) => pagebreak" }, - "sortText": "128", + "sortText": "129", "textEdit": { "newText": "pagebreak()${1:}", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@let_init.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@let_init.typ.snap index d3a91dfd..83db4d0b 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@let_init.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@let_init.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (34..35) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/let_init.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(to: \"even\" | \"odd\" | none, weak: bool) => pagebreak" }, - "sortText": "128", + "sortText": "129", "textEdit": { "newText": "pagebreak()${1:}", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@math_bold2.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@math_bold2.typ.snap index aa71cef2..7abfa055 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@math_bold2.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@math_bold2.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (30..31) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/math_bold2.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(content) => content" }, - "sortText": "086", + "sortText": "087", "textEdit": { "newText": "bold(${1:})", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@math_call_in_call.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@math_call_in_call.typ.snap index 88e7de9d..b65b51b0 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@math_call_in_call.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@math_call_in_call.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (28..29) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/math_call_in_call.typ -snapshot_kind: text --- [ { @@ -31,7 +30,7 @@ snapshot_kind: text "kind": 3, "label": "abs", "labelDetails": { - "description": "(content, size: auto | relative) => content" + "description": "(content, size: relative) => content" }, "sortText": "058", "textEdit": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@math_call_in_call2.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@math_call_in_call2.typ.snap index fce3a784..627361a2 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@math_call_in_call2.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@math_call_in_call2.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (27..28) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/math_call_in_call2.typ -snapshot_kind: text --- [ { @@ -13,7 +12,7 @@ snapshot_kind: text "kind": 3, "label": "abs", "labelDetails": { - "description": "(content, size: auto | relative) => content" + "description": "(content, size: relative) => content" }, "sortText": "057", "textEdit": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@math_call_in_call3.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@math_call_in_call3.typ.snap index 189ad2e0..f68b7829 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@math_call_in_call3.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@math_call_in_call3.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (28..29) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/math_call_in_call3.typ -snapshot_kind: text --- [ { @@ -13,7 +12,7 @@ snapshot_kind: text "kind": 3, "label": "abs", "labelDetails": { - "description": "(content, size: auto | relative) => content" + "description": "(content, size: relative) => content" }, "sortText": "055", "textEdit": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@math_call_in_call4.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@math_call_in_call4.typ.snap index e5edf1a3..eabfd9a1 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@math_call_in_call4.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@math_call_in_call4.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (29..30) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/math_call_in_call4.typ -snapshot_kind: text --- [ { @@ -13,7 +12,7 @@ snapshot_kind: text "kind": 3, "label": "abs", "labelDetails": { - "description": "(content, size: auto | relative) => content" + "description": "(content, size: relative) => content" }, "sortText": "055", "textEdit": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@math_ident_in_call.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@math_ident_in_call.typ.snap index b07ba8e0..ccb93018 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@math_ident_in_call.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@math_ident_in_call.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (27..28) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/math_ident_in_call.typ -snapshot_kind: text --- [ { @@ -13,9 +12,9 @@ snapshot_kind: text "kind": 3, "label": "norm", "labelDetails": { - "description": "(content, size: auto | relative) => content" + "description": "(content, size: relative) => content" }, - "sortText": "221", + "sortText": "229", "textEdit": { "newText": "norm(${1:})", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@math_ident_in_call2.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@math_ident_in_call2.typ.snap index 5406c4ec..87e4a4fb 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@math_ident_in_call2.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@math_ident_in_call2.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (28..29) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/math_ident_in_call2.typ -snapshot_kind: text --- [ { @@ -13,9 +12,9 @@ snapshot_kind: text "kind": 3, "label": "norm", "labelDetails": { - "description": "(content, size: auto | relative) => content" + "description": "(content, size: relative) => content" }, - "sortText": "223", + "sortText": "231", "textEdit": { "newText": "norm(${1:})", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@set.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@set.typ.snap index 977f4ec0..ec726cee 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@set.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@set.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (92..93) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/set.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(str, align: alignment, block: bool, lang: none | str, syntaxes: [syntax], tab-size: int, theme: [theme]) => raw" }, - "sortText": "052", + "sortText": "053", "textEdit": { "newText": "raw(${1:})", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@set2.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@set2.typ.snap index 1d7ae5d4..1d12815d 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@set2.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@set2.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (86..87) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/set2.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(str, align: alignment, block: bool, lang: none | str, syntaxes: [syntax], tab-size: int, theme: [theme]) => raw" }, - "sortText": "052", + "sortText": "053", "textEdit": { "newText": "raw(${1:})", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@set_in_show.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@set_in_show.typ.snap index 33c6e919..9874b19a 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@set_in_show.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@set_in_show.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (114..115) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/set_in_show.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(str, align: alignment, block: bool, lang: none | str, syntaxes: [syntax], tab-size: int, theme: [theme]) => raw" }, - "sortText": "053", + "sortText": "054", "textEdit": { "newText": "raw(${1:})", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@show.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@show.typ.snap index a76cabda..44968c95 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@show.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@show.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (109..110) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/show.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(str, align: alignment, block: bool, lang: none | str, syntaxes: [syntax], tab-size: int, theme: [theme]) => raw" }, - "sortText": "094", + "sortText": "096", "textEdit": { "newText": "raw: ${1:}", "range": { @@ -36,7 +35,7 @@ snapshot_kind: text "labelDetails": { "description": "(str, align: alignment, block: bool, lang: none | str, syntaxes: [syntax], tab-size: int, theme: [theme]) => raw" }, - "sortText": "095", + "sortText": "097", "textEdit": { "newText": "raw.where(${1:}): ${2:}", "range": { @@ -54,7 +53,7 @@ snapshot_kind: text { "kind": 15, "label": "regex selector", - "sortText": "100", + "sortText": "102", "textEdit": { "newText": "regex(\"${1:regex}\"): ${2:}", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@show2.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@show2.typ.snap index 578c9700..e3b367f2 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@show2.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@show2.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (108..109) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/show2.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(str, align: alignment, block: bool, lang: none | str, syntaxes: [syntax], tab-size: int, theme: [theme]) => raw" }, - "sortText": "094", + "sortText": "096", "textEdit": { "newText": "raw: ${1:}", "range": { @@ -36,7 +35,7 @@ snapshot_kind: text "labelDetails": { "description": "(str, align: alignment, block: bool, lang: none | str, syntaxes: [syntax], tab-size: int, theme: [theme]) => raw" }, - "sortText": "095", + "sortText": "097", "textEdit": { "newText": "raw.where(${1:}): ${2:}", "range": { @@ -54,7 +53,7 @@ snapshot_kind: text { "kind": 15, "label": "regex selector", - "sortText": "100", + "sortText": "102", "textEdit": { "newText": "regex(\"${1:regex}\"): ${2:}", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@show3.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@show3.typ.snap index 5c667a78..154168ba 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@show3.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@show3.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (103..104) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/show3.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(str, align: alignment, block: bool, lang: none | str, syntaxes: [syntax], tab-size: int, theme: [theme]) => raw" }, - "sortText": "094", + "sortText": "096", "textEdit": { "newText": "raw: ${1:}", "range": { @@ -36,7 +35,7 @@ snapshot_kind: text "labelDetails": { "description": "(str, align: alignment, block: bool, lang: none | str, syntaxes: [syntax], tab-size: int, theme: [theme]) => raw" }, - "sortText": "095", + "sortText": "097", "textEdit": { "newText": "raw.where(${1:}): ${2:}", "range": { @@ -54,7 +53,7 @@ snapshot_kind: text { "kind": 15, "label": "regex selector", - "sortText": "100", + "sortText": "102", "textEdit": { "newText": "regex(\"${1:regex}\"): ${2:}", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@show_transform.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@show_transform.typ.snap index c2cb3800..a6a33579 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@show_transform.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@show_transform.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (93..94) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/show_transform.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(str, align: alignment, block: bool, lang: none | str, syntaxes: [syntax], tab-size: int, theme: [theme]) => raw" }, - "sortText": "173", + "sortText": "175", "textEdit": { "newText": "raw(${1:})", "range": { @@ -36,7 +35,7 @@ snapshot_kind: text "labelDetails": { "description": "(str, align: alignment, block: bool, lang: none | str, syntaxes: [syntax], tab-size: int, theme: [theme]) => raw" }, - "sortText": "174", + "sortText": "176", "textEdit": { "newText": "raw.with(${1:})", "range": { @@ -57,7 +56,7 @@ snapshot_kind: text "labelDetails": { "description": "([any], encoding: \"utf8\" | none) => bytes | str" }, - "sortText": "175", + "sortText": "177", "textEdit": { "newText": "read(${1:})", "range": { @@ -78,7 +77,7 @@ snapshot_kind: text "labelDetails": { "description": "([any], encoding: \"utf8\" | none) => bytes | str" }, - "sortText": "176", + "sortText": "178", "textEdit": { "newText": "read.with(${1:})", "range": { @@ -96,7 +95,7 @@ snapshot_kind: text { "kind": 15, "label": "replacement", - "sortText": "187", + "sortText": "189", "textEdit": { "newText": "[${1:content}]", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@show_transform2.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@show_transform2.typ.snap index 6f927358..e4d1ff8a 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@show_transform2.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@show_transform2.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (92..93) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/show_transform2.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(str, align: alignment, block: bool, lang: none | str, syntaxes: [syntax], tab-size: int, theme: [theme]) => raw" }, - "sortText": "173", + "sortText": "175", "textEdit": { "newText": "raw(${1:})", "range": { @@ -36,7 +35,7 @@ snapshot_kind: text "labelDetails": { "description": "(str, align: alignment, block: bool, lang: none | str, syntaxes: [syntax], tab-size: int, theme: [theme]) => raw" }, - "sortText": "174", + "sortText": "176", "textEdit": { "newText": "raw.with(${1:})", "range": { @@ -57,7 +56,7 @@ snapshot_kind: text "labelDetails": { "description": "([any], encoding: \"utf8\" | none) => bytes | str" }, - "sortText": "175", + "sortText": "177", "textEdit": { "newText": "read(${1:})", "range": { @@ -78,7 +77,7 @@ snapshot_kind: text "labelDetails": { "description": "([any], encoding: \"utf8\" | none) => bytes | str" }, - "sortText": "176", + "sortText": "178", "textEdit": { "newText": "read.with(${1:})", "range": { @@ -96,7 +95,7 @@ snapshot_kind: text { "kind": 15, "label": "replacement", - "sortText": "187", + "sortText": "189", "textEdit": { "newText": "[${1:content}]", "range": { diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@show_transform3.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@show_transform3.typ.snap index 0f211796..c6a24bf2 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@show_transform3.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@show_transform3.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (91..92) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/completion/show_transform3.typ -snapshot_kind: text --- [ { @@ -15,7 +14,7 @@ snapshot_kind: text "labelDetails": { "description": "(str, align: alignment, block: bool, lang: none | str, syntaxes: [syntax], tab-size: int, theme: [theme]) => raw" }, - "sortText": "173", + "sortText": "175", "textEdit": { "newText": " raw(${1:})", "range": { @@ -36,7 +35,7 @@ snapshot_kind: text "labelDetails": { "description": "(str, align: alignment, block: bool, lang: none | str, syntaxes: [syntax], tab-size: int, theme: [theme]) => raw" }, - "sortText": "174", + "sortText": "176", "textEdit": { "newText": " raw.with(${1:})", "range": { @@ -57,7 +56,7 @@ snapshot_kind: text "labelDetails": { "description": "([any], encoding: \"utf8\" | none) => bytes | str" }, - "sortText": "175", + "sortText": "177", "textEdit": { "newText": " read(${1:})", "range": { @@ -78,7 +77,7 @@ snapshot_kind: text "labelDetails": { "description": "([any], encoding: \"utf8\" | none) => bytes | str" }, - "sortText": "176", + "sortText": "178", "textEdit": { "newText": " read.with(${1:})", "range": { @@ -96,7 +95,7 @@ snapshot_kind: text { "kind": 15, "label": "replacement", - "sortText": "187", + "sortText": "189", "textEdit": { "newText": " [${1:content}]", "range": { diff --git a/crates/tinymist-query/src/fixtures/pkgs/snaps/test@touying-core-slides.typ.snap b/crates/tinymist-query/src/fixtures/pkgs/snaps/test@touying-core-slides.typ.snap index 17979a10..66ecccfc 100644 --- a/crates/tinymist-query/src/fixtures/pkgs/snaps/test@touying-core-slides.typ.snap +++ b/crates/tinymist-query/src/fixtures/pkgs/snaps/test@touying-core-slides.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (57..58) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/pkgs/touying-core-slides.typ -snapshot_kind: text --- [ { @@ -57,7 +56,7 @@ snapshot_kind: text "labelDetails": { "description": "(content, gap: length, justify: bool) => repeat" }, - "sortText": "159", + "sortText": "161", "textEdit": { "newText": "repeat(${1:})", "range": { diff --git a/crates/tinymist-query/src/fixtures/pkgs/snaps/test@touying-utils-cover-with-rect.typ.snap b/crates/tinymist-query/src/fixtures/pkgs/snaps/test@touying-utils-cover-with-rect.typ.snap index 2e30942a..68c6e98a 100644 --- a/crates/tinymist-query/src/fixtures/pkgs/snaps/test@touying-utils-cover-with-rect.typ.snap +++ b/crates/tinymist-query/src/fixtures/pkgs/snaps/test@touying-utils-cover-with-rect.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (71..72) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/pkgs/touying-utils-cover-with-rect.typ -snapshot_kind: text --- [ { @@ -36,7 +35,7 @@ snapshot_kind: text "labelDetails": { "description": "type" }, - "sortText": "186", + "sortText": "188", "textEdit": { "newText": "stroke(${1:})", "range": { diff --git a/crates/tinymist-query/src/fixtures/pkgs/snaps/test@touying-utils-current-heading.typ-2.snap b/crates/tinymist-query/src/fixtures/pkgs/snaps/test@touying-utils-current-heading.typ-2.snap index 1f3f81bf..29c5a91a 100644 --- a/crates/tinymist-query/src/fixtures/pkgs/snaps/test@touying-utils-current-heading.typ-2.snap +++ b/crates/tinymist-query/src/fixtures/pkgs/snaps/test@touying-utils-current-heading.typ-2.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (63..64) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/pkgs/touying-utils-current-heading.typ -snapshot_kind: text --- [ { @@ -33,7 +32,7 @@ snapshot_kind: text "labelDetails": { "description": "type" }, - "sortText": "089", + "sortText": "090", "textEdit": { "newText": "int(${1:})", "range": { diff --git a/crates/tinymist-query/src/fixtures/pkgs/snaps/test@touying-utils-markup-text.typ.snap b/crates/tinymist-query/src/fixtures/pkgs/snaps/test@touying-utils-markup-text.typ.snap index 33cc1eb7..46f17773 100644 --- a/crates/tinymist-query/src/fixtures/pkgs/snaps/test@touying-utils-markup-text.typ.snap +++ b/crates/tinymist-query/src/fixtures/pkgs/snaps/test@touying-utils-markup-text.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/completion.rs description: Completion on / (71..72) expression: "JsonRepr::new_pure(results)" input_file: crates/tinymist-query/src/fixtures/pkgs/touying-utils-markup-text.typ -snapshot_kind: text --- [ { @@ -69,7 +68,7 @@ snapshot_kind: text "labelDetails": { "description": "type" }, - "sortText": "180", + "sortText": "182", "textEdit": { "newText": "str(${1:})", "range": { diff --git a/crates/tinymist-query/src/fixtures/semantic_tokens/snaps/test@tinymist_issue_601.typ.snap b/crates/tinymist-query/src/fixtures/semantic_tokens/snaps/test@tinymist_issue_601.typ.snap index 110e1568..41a25f90 100644 --- a/crates/tinymist-query/src/fixtures/semantic_tokens/snaps/test@tinymist_issue_601.typ.snap +++ b/crates/tinymist-query/src/fixtures/semantic_tokens/snaps/test@tinymist_issue_601.typ.snap @@ -2,6 +2,5 @@ source: crates/tinymist-query/src/semantic_tokens_full.rs expression: "serde_json::to_string(&result).unwrap()" input_file: crates/tinymist-query/src/fixtures/semantic_tokens/tinymist_issue_601.typ -snapshot_kind: text --- -{"data":[0,0,1,21,4,0,1,3,5,4,0,3,1,10,4,0,1,1,22,4,0,1,1,3,4,0,1,1,22,4,0,1,1,22,4,1,0,1,22,4,0,1,1,22,4,1,0,1,22,4]} +{"data":[0,0,1,21,4,0,1,3,5,4,0,3,1,21,4,0,1,1,22,4,0,1,1,3,4,0,1,1,22,4,0,1,1,22,4,1,0,1,22,4,0,1,1,22,0,1,0,1,22,0]} diff --git a/crates/tinymist-query/src/hover.rs b/crates/tinymist-query/src/hover.rs index 64355079..b84102f9 100644 --- a/crates/tinymist-query/src/hover.rs +++ b/crates/tinymist-query/src/hover.rs @@ -325,7 +325,7 @@ impl ExternalDocLink { Repr::With(w) => { func = &w.0; } - Repr::Closure(..) => { + Repr::Closure(..) | Repr::Plugin(..) => { return None; } } diff --git a/crates/tinymist-query/src/jump.rs b/crates/tinymist-query/src/jump.rs index 8d170996..5aa805dc 100644 --- a/crates/tinymist-query/src/jump.rs +++ b/crates/tinymist-query/src/jump.rs @@ -49,6 +49,7 @@ pub fn jump_from_cursor( point, }) } + _ => None, } } @@ -68,7 +69,12 @@ fn find_in_frame(frame: &Frame, span: Span, min_dis: &mut u64, res: &mut Point) return Some(pos); } if glyph.span.0.id() == span.id() { - let dis = glyph.span.0.number().abs_diff(span.number()); + let dis = glyph + .span + .0 + .into_raw() + .get() + .abs_diff(span.into_raw().get()); if dis < *min_dis { *min_dis = dis; *res = pos; diff --git a/crates/tinymist-query/src/prepare_rename.rs b/crates/tinymist-query/src/prepare_rename.rs index eb09c44b..cd712531 100644 --- a/crates/tinymist-query/src/prepare_rename.rs +++ b/crates/tinymist-query/src/prepare_rename.rs @@ -122,7 +122,7 @@ fn validate_fn_renaming(def: &Definition) -> Option<()> { match func.inner() { // todo: rename with site Repr::With(w) => func = &w.0, - Repr::Closure(..) => return Some(()), + Repr::Closure(..) | Repr::Plugin(..) => return Some(()), // native functions can't be renamed Repr::Native(..) | Repr::Element(..) => return None, } diff --git a/crates/tinymist-query/src/syntax/def.rs b/crates/tinymist-query/src/syntax/def.rs index c163b47a..73f73be1 100644 --- a/crates/tinymist-query/src/syntax/def.rs +++ b/crates/tinymist-query/src/syntax/def.rs @@ -137,7 +137,11 @@ impl ExprScope { // ref_expr.of = of.clone(); // ref_expr.val = val.map(|v| Ty::Value(InsTy::new(v.clone()))); // return ref_expr; - (of, val.cloned().map(|val| Ty::Value(InsTy::new(val)))) + ( + of, + val.cloned() + .map(|val| Ty::Value(InsTy::new(val.read().to_owned()))), + ) } pub fn merge_into(&self, exports: &mut LexicalScope) { @@ -150,7 +154,7 @@ impl ExprScope { ExprScope::Module(module) => { crate::log_debug_ct!("imported: {module:?}"); let v = Interned::new(Ty::Value(InsTy::new(Value::Module(module.clone())))); - for (name, _, _) in module.scope().iter() { + for (name, _) in module.scope().iter() { let name: Interned = name.into(); exports.insert_mut(name.clone(), select_of(v.clone(), name)); } @@ -158,7 +162,7 @@ impl ExprScope { ExprScope::Func(func) => { if let Some(scope) = func.scope() { let v = Interned::new(Ty::Value(InsTy::new(Value::Func(func.clone())))); - for (name, _, _) in scope.iter() { + for (name, _) in scope.iter() { let name: Interned = name.into(); exports.insert_mut(name.clone(), select_of(v.clone(), name)); } @@ -166,7 +170,7 @@ impl ExprScope { } ExprScope::Type(ty) => { let v = Interned::new(Ty::Value(InsTy::new(Value::Type(*ty)))); - for (name, _, _) in ty.scope().iter() { + for (name, _) in ty.scope().iter() { let name: Interned = name.into(); exports.insert_mut(name.clone(), select_of(v.clone(), name)); } @@ -460,13 +464,62 @@ impl Ord for Decl { (Self::Generated(l), Self::Generated(r)) => l.0 .0.cmp(&r.0 .0), (Self::Module(l), Self::Module(r)) => l.fid.cmp(&r.fid), (Self::Docs(l), Self::Docs(r)) => l.var.cmp(&r.var).then_with(|| l.base.cmp(&r.base)), - _ => self.span().number().cmp(&other.span().number()), + _ => self.span().into_raw().cmp(&other.span().into_raw()), }; base.then_with(|| self.name().cmp(other.name())) } } +trait StrictCmp { + /// Low-performance comparison but it is free from the concurrency issue. + /// This is only used for making stable test snapshots. + fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering; +} + +impl Decl { + pub fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering { + let base = match (self, other) { + (Self::Generated(l), Self::Generated(r)) => l.0 .0.cmp(&r.0 .0), + (Self::Module(l), Self::Module(r)) => l.fid.strict_cmp(&r.fid), + (Self::Docs(l), Self::Docs(r)) => l + .var + .strict_cmp(&r.var) + .then_with(|| l.base.strict_cmp(&r.base)), + _ => self.span().strict_cmp(&other.span()), + }; + + base.then_with(|| self.name().cmp(other.name())) + } +} + +impl StrictCmp for TypstFileId { + fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering { + self.package() + .map(ToString::to_string) + .cmp(&other.package().map(ToString::to_string)) + .then_with(|| self.vpath().cmp(other.vpath())) + } +} +impl StrictCmp for Option { + fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering { + match (self, other) { + (Some(l), Some(r)) => l.strict_cmp(r), + (Some(_), None) => std::cmp::Ordering::Greater, + (None, Some(_)) => std::cmp::Ordering::Less, + (None, None) => std::cmp::Ordering::Equal, + } + } +} + +impl StrictCmp for Span { + fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering { + self.id() + .strict_cmp(&other.id()) + .then_with(|| self.into_raw().cmp(&other.into_raw())) + } +} + impl PartialOrd for Decl { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) diff --git a/crates/tinymist-query/src/syntax/docs.rs b/crates/tinymist-query/src/syntax/docs.rs index 58fbc362..c3a2c141 100644 --- a/crates/tinymist-query/src/syntax/docs.rs +++ b/crates/tinymist-query/src/syntax/docs.rs @@ -239,8 +239,8 @@ impl DocsChecker<'_> { .chain([ Type::of::(), Type::of::(), - Type::of::(), - Type::of::(), + Type::of::(), + Type::of::(), Type::of::(), Type::of::(), Type::of::(), @@ -287,9 +287,9 @@ impl DocsChecker<'_> { let val = module.scope().get(name)?; crate::log_debug_ct!("check doc type annotation: {name:?}"); - if let Value::Content(raw) = val { + if let Value::Content(raw) = val.read() { let annotated = raw.clone().unpack::().ok()?; - let annotated = annotated.text().clone().into_value().cast::().ok()?; + let annotated = annotated.text.clone().into_value().cast::().ok()?; let code = typst::syntax::parse_code(&annotated.as_str().replace('\'', "θ")); let mut exprs = code.cast::()?.exprs(); let term = self.check_type_expr(module, exprs.next()?); diff --git a/crates/tinymist-query/src/syntax/expr.rs b/crates/tinymist-query/src/syntax/expr.rs index 165a8dff..e42ec2f1 100644 --- a/crates/tinymist-query/src/syntax/expr.rs +++ b/crates/tinymist-query/src/syntax/expr.rs @@ -8,7 +8,7 @@ use tinymist_std::hash::hash128; use typst::{ foundations::{Element, NativeElement, Value}, model::{EmphElem, EnumElem, HeadingElem, ListElem, StrongElem, TermsElem}, - syntax::{Span, SyntaxNode}, + syntax::{ast::MathTextKind, Span, SyntaxNode}, utils::LazyHash, }; @@ -424,6 +424,12 @@ impl ExprWorker<'_> { self.check_math([num, denom].iter()) } MathRoot(root) => self.check(root.radicand()), + MathText(mathtext) => { + Expr::Type(Ty::Value(InsTy::new(Value::Str(match mathtext.get() { + MathTextKind::Character(c) => c.into(), + MathTextKind::Number(n) => n.to_string().into(), + })))) + } } } @@ -742,7 +748,9 @@ impl ExprWorker<'_> { (_, Some(Value::Module(m))) => { // todo: dyn resolve src_expr match m.file_id() { - Some(fid) => Some(Expr::Decl(Decl::module(m.name().into(), fid).into())), + Some(fid) => Some(Expr::Decl( + Decl::module(m.name().unwrap().into(), fid).into(), + )), None => Some(Expr::Type(Ty::Value(InsTy::new(Value::Module(m))))), } } @@ -1162,7 +1170,7 @@ impl ExprWorker<'_> { // v.select(field.name()).ok() match v { Ty::Value(val) => { - Some(Ty::Value(InsTy::new(val.val.field(field.name()).ok()?))) + Some(Ty::Value(InsTy::new(val.val.field(field.name(), ()).ok()?))) } _ => None, } @@ -1202,7 +1210,7 @@ impl ExprWorker<'_> { let val = scope .get(name) .cloned() - .map(|val| Ty::Value(InsTy::new(val))); + .map(|val| Ty::Value(InsTy::new(val.read().clone()))); (None, val) } diff --git a/crates/tinymist-query/src/tests.rs b/crates/tinymist-query/src/tests.rs index a187268f..991e92cd 100644 --- a/crates/tinymist-query/src/tests.rs +++ b/crates/tinymist-query/src/tests.rs @@ -10,7 +10,7 @@ use std::{ use once_cell::sync::Lazy; use serde_json::{ser::PrettyFormatter, Serializer, Value}; -use tinymist_project::CompileFontArgs; +use tinymist_project::{CompileFontArgs, ExportTarget}; use tinymist_std::typst::TypstDocument; use tinymist_world::package::PackageSpec; use tinymist_world::vfs::WorkspaceResolver; @@ -155,6 +155,7 @@ pub fn run_with_sources(source: &str, f: impl FnOnce(&mut LspUniverse, PathBu }; let mut verse = LspUniverseBuilder::build( EntryState::new_rooted(root.as_path().into(), None), + ExportTarget::Paged, Default::default(), Arc::new( LspUniverseBuilder::resolve_fonts(CompileFontArgs { @@ -187,7 +188,7 @@ pub fn run_with_sources(source: &str, f: impl FnOnce(&mut LspUniverse, PathBu let pw = root.join(Path::new(&path)); verse - .map_shadow(&pw, Bytes::from(source.as_bytes())) + .map_shadow(&pw, Bytes::from_string(source.to_owned())) .unwrap(); last_pw = Some(pw); } diff --git a/crates/tinymist-query/src/ty/builtin.rs b/crates/tinymist-query/src/ty/builtin.rs index 1cb77865..78ab6fdd 100644 --- a/crates/tinymist-query/src/ty/builtin.rs +++ b/crates/tinymist-query/src/ty/builtin.rs @@ -121,7 +121,7 @@ impl Ty { pub(crate) fn from_param_site(func: &Func, param: &ParamInfo) -> Ty { use typst::foundations::func::Repr; match func.inner() { - Repr::Element(..) | Repr::Native(..) => { + Repr::Element(..) | Repr::Native(..) | Repr::Plugin(..) => { if let Some(ty) = param_mapping(func, param) { return ty; } @@ -137,7 +137,7 @@ impl Ty { use typst::foundations::func::Repr; match func.inner() { Repr::Element(elem) => return Ty::Builtin(BuiltinTy::Element(*elem)), - Repr::Closure(_) => {} + Repr::Closure(_) | Repr::Plugin(_) => {} Repr::With(w) => return Ty::from_return_site(&w.0, ty), Repr::Native(_) => {} }; diff --git a/crates/tinymist-query/src/ty/def.rs b/crates/tinymist-query/src/ty/def.rs index 558f0ab5..ef890483 100644 --- a/crates/tinymist-query/src/ty/def.rs +++ b/crates/tinymist-query/src/ty/def.rs @@ -183,7 +183,7 @@ impl Ty { Ty::Builtin(BuiltinTy::Module(m)) => m.name().clone(), ty => ty .value() - .and_then(|v| Some(Interned::new_str(v.name()?))) + .map(|_| Interned::new_str(&self.name())) .unwrap_or_default(), } } @@ -253,7 +253,7 @@ impl Ty { } fn is_content_builtin_type(ty: &Type) -> bool { - *ty == Type::of::() || *ty == Type::of::() + *ty == Type::of::() || *ty == Type::of::() } /// A function parameter type @@ -646,6 +646,14 @@ impl Ord for TypeVar { } } +impl TypeVar { + /// Low-performance comparison but it is free from the concurrency issue. + /// This is only used for making stable test snapshots. + pub fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering { + self.def.strict_cmp(&other.def) + } +} + impl PartialOrd for TypeVar { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) diff --git a/crates/tinymist-query/src/ty/describe.rs b/crates/tinymist-query/src/ty/describe.rs index c4d42019..000af796 100644 --- a/crates/tinymist-query/src/ty/describe.rs +++ b/crates/tinymist-query/src/ty/describe.rs @@ -196,7 +196,7 @@ impl TypeDescriber { let Value::Module(m) = &v.val else { return "module".into(); }; - return eco_format!("module({})", m.name()); + return eco_format!("module({})", m.name().unwrap()); } Ty::Value(v) if !is_plain_value(&v.val) => return self.describe(&term_value(&v.val)), Ty::Value(v) if self.value => return truncated_repr_::<181>(&v.val), diff --git a/crates/tinymist-query/src/ty/iface.rs b/crates/tinymist-query/src/ty/iface.rs index c3e5ffc2..e3b92152 100644 --- a/crates/tinymist-query/src/ty/iface.rs +++ b/crates/tinymist-query/src/ty/iface.rs @@ -1,8 +1,5 @@ +use typst::foundations::{Dict, Module, Scope, Type}; use typst::syntax::FileId; -use typst::{ - foundations::{Dict, Module, Scope, Type}, - syntax::Span, -}; use super::BoundChecker; use crate::{syntax::Decl, ty::prelude::*}; @@ -56,8 +53,8 @@ impl Iface<'_> { fn select_scope(scope: Option<&Scope>, key: &str) -> Option { let scope = scope?; let sub = scope.get(key)?; - let sub_span = scope.get_span(key).unwrap_or_else(Span::detached); - Some(Ty::Value(InsTy::new_at(sub.clone(), sub_span))) + let sub_span = sub.span(); + Some(Ty::Value(InsTy::new_at(sub.read().clone(), sub_span))) } pub trait IfaceChecker: TyCtx { diff --git a/crates/tinymist-query/src/upstream/mod.rs b/crates/tinymist-query/src/upstream/mod.rs index 8bdf9b21..886459e2 100644 --- a/crates/tinymist-query/src/upstream/mod.rs +++ b/crates/tinymist-query/src/upstream/mod.rs @@ -7,7 +7,7 @@ use serde::Deserialize; use serde_yaml as yaml; use typst::{ diag::{bail, StrResult}, - foundations::{Content, Func, Module, Type, Value}, + foundations::{Binding, Content, Func, Module, Type, Value}, introspection::MetadataElem, syntax::Span, text::{FontInfo, FontStyle}, @@ -113,8 +113,8 @@ static GROUPS: Lazy> = Lazy::new(|| { .module() .scope() .iter() - .filter(|(_, v, _)| matches!(v, Value::Func(_))) - .map(|(k, _, _)| k.clone()) + .filter(|(_, v)| matches!(v.read(), Value::Func(_))) + .map(|(k, _)| k.clone()) .collect(); } } @@ -175,7 +175,7 @@ static LIBRARY: Lazy = Lazy::new(Library::default); /// Extract a module from another module. #[track_caller] fn get_module<'a>(parent: &'a Module, name: &str) -> StrResult<&'a Module> { - match parent.scope().get(name) { + match parent.scope().get(name).map(|x| x.read()) { Some(Value::Module(module)) => Ok(module), _ => bail!("module doesn't contain module `{name}`"), } @@ -189,7 +189,7 @@ fn resolve_definition(head: &str, base: &str) -> StrResult { while let Some(name) = parts.peek() { if category.is_none() { - category = focus.scope().get_category(name); + category = focus.scope().get(name).and_then(Binding::category); } let Ok(module) = get_module(focus, name) else { break; @@ -203,7 +203,7 @@ fn resolve_definition(head: &str, base: &str) -> StrResult { }; let name = parts.next().ok_or("link is missing first part")?; - let value = focus.field(name)?; + let value = focus.field(name, ())?; // Handle grouped functions. if let Some(group) = GROUPS.iter().find(|group| { @@ -222,7 +222,7 @@ fn resolve_definition(head: &str, base: &str) -> StrResult { let mut route = format!("{}reference/{}/{name}", base, category.name()); if let Some(next) = parts.next() { - if let Ok(field) = value.field(next) { + if let Ok(field) = value.field(next, ()) { route.push_str("/#definitions-"); route.push_str(next); if let Some(next) = parts.next() { @@ -283,10 +283,10 @@ static ROUTE_MAPS: Lazy> = Lazy::new(|| { (LIBRARY.math.scope(), None, None), ]; while let Some((scope, parent_name, cat)) = scope_to_finds.pop() { - for (name, value, _) in scope.iter() { - let cat = cat.or_else(|| scope.get_category(name)); + for (name, bind) in scope.iter() { + let cat = cat.or_else(|| bind.category()); let name = urlify(name); - match value { + match bind.read() { Value::Func(func) => { if let Some(cat) = cat { let Some(name) = func.name() else { @@ -437,6 +437,7 @@ pub fn with_vm( let traced = Traced::default(); let mut sink = Sink::new(); let engine = Engine { + routines: &typst::ROUTINES, world, route: Route::default(), introspector: introspector.track(), diff --git a/crates/tinymist-query/src/upstream/tooltip.rs b/crates/tinymist-query/src/upstream/tooltip.rs index f2e72124..57219ecc 100644 --- a/crates/tinymist-query/src/upstream/tooltip.rs +++ b/crates/tinymist-query/src/upstream/tooltip.rs @@ -148,7 +148,7 @@ fn closure_tooltip(leaf: &LinkedNode) -> Option { let captures = visitor.finish(); let mut names: Vec<_> = captures .iter() - .map(|(name, _, _)| eco_format!("`{name}`")) + .map(|(name, _)| eco_format!("`{name}`")) .collect(); if names.is_empty() { return None; @@ -193,7 +193,7 @@ fn named_param_tooltip(world: &dyn World, leaf: &LinkedNode) -> Option }; // Find metadata about the function. - if let Some(Value::Func(func)) = world.library().global.scope().get(&callee); + if let Some(Value::Func(func)) = world.library().global.scope().get(&callee).map(|x| x.read()); then { (func, named) } else { return None; } }; diff --git a/crates/tinymist-render/src/lib.rs b/crates/tinymist-render/src/lib.rs index b63306e6..baa3c946 100644 --- a/crates/tinymist-render/src/lib.rs +++ b/crates/tinymist-render/src/lib.rs @@ -127,6 +127,7 @@ impl PeriscopeRenderer { Some((SvgText::join(svg_text), width, height)) } + _ => None, } } } diff --git a/crates/tinymist-std/src/concepts/typst.rs b/crates/tinymist-std/src/concepts/typst.rs index 8ac1dbac..fcfdae66 100644 --- a/crates/tinymist-std/src/concepts/typst.rs +++ b/crates/tinymist-std/src/concepts/typst.rs @@ -12,7 +12,11 @@ pub(crate) mod well_known { pub use typst::layout::Abs as TypstAbs; - pub use typst::model::Document as TypstPagedDocument; + pub use typst::Document as TypstDocumentTrait; + + pub use typst::layout::PagedDocument as TypstPagedDocument; + + pub use typst::html::HtmlDocument as TypstHtmlDocument; pub use typst::text::Font as TypstFont; @@ -28,13 +32,24 @@ pub(crate) mod well_known { pub enum TypstDocument { /// The document compiled with `paged` target. Paged(Arc), + /// The document compiled with `html` target. + Html(Arc), } impl TypstDocument { + /// Gets the number of pages in the document. + pub fn num_of_pages(&self) -> u32 { + match self { + Self::Paged(doc) => doc.pages.len() as u32, + Self::Html(_doc) => 1u32, + } + } + /// Gets details about the document. pub fn info(&self) -> &typst::model::DocumentInfo { match self { Self::Paged(doc) => &doc.info, + Self::Html(doc) => &doc.info, } } @@ -43,6 +58,45 @@ impl TypstDocument { pub fn introspector(&self) -> &typst::introspection::Introspector { match self { Self::Paged(doc) => &doc.introspector, + Self::Html(doc) => &doc.introspector, + } + } +} + +impl From> for TypstDocument { + fn from(doc: Arc) -> Self { + Self::Paged(doc) + } +} + +impl From> for TypstDocument { + fn from(doc: Arc) -> Self { + Self::Html(doc) + } +} + +impl<'a> TryFrom<&'a TypstDocument> for &'a Arc { + type Error = crate::Error; + + fn try_from(doc: &'a TypstDocument) -> Result { + match doc { + TypstDocument::Paged(doc) => Ok(doc), + TypstDocument::Html(_doc) => { + crate::bail!("The document is compiled with `html` target, not `paged`.") + } + } + } +} + +impl<'a> TryFrom<&'a TypstDocument> for &'a Arc { + type Error = crate::Error; + + fn try_from(doc: &'a TypstDocument) -> Result { + match doc { + TypstDocument::Paged(_doc) => { + crate::bail!("The document is compiled with `paged` target, not `html`.") + } + TypstDocument::Html(doc) => Ok(doc), } } } diff --git a/crates/tinymist-task/Cargo.toml b/crates/tinymist-task/Cargo.toml index 62b4aae2..89e463fa 100644 --- a/crates/tinymist-task/Cargo.toml +++ b/crates/tinymist-task/Cargo.toml @@ -34,10 +34,12 @@ tinymist-derive.workspace = true toml.workspace = true typst.workspace = true typst-assets.workspace = true +typst-eval.workspace = true +typst-html.workspace = true typst-pdf.workspace = true +typst-render.workspace = true typst-shim.workspace = true typst-svg.workspace = true -typst-render.workspace = true notify.workspace = true [features] diff --git a/crates/tinymist-task/README.md b/crates/tinymist-task/README.md new file mode 100644 index 00000000..b75a30af --- /dev/null +++ b/crates/tinymist-task/README.md @@ -0,0 +1,3 @@ +# tinymist-task + +Task model of typst for tinymist. diff --git a/crates/tinymist-task/src/compute.rs b/crates/tinymist-task/src/compute.rs index bd28902e..32a55db8 100644 --- a/crates/tinymist-task/src/compute.rs +++ b/crates/tinymist-task/src/compute.rs @@ -6,21 +6,18 @@ use std::sync::Arc; use comemo::Track; use ecow::EcoString; use tinymist_std::error::prelude::*; -use tinymist_std::typst::TypstPagedDocument; -use tinymist_world::{ - args::convert_source_date_epoch, CompileSnapshot, CompilerFeat, ExportComputation, - WorldComputeGraph, -}; +use tinymist_std::typst::{TypstDocument, TypstHtmlDocument, TypstPagedDocument}; +use tinymist_world::{CompileSnapshot, CompilerFeat, ExportComputation, WorldComputeGraph}; use typst::diag::{SourceResult, StrResult}; -use typst::foundations::{Bytes, Content, Datetime, IntoValue, LocatableSelector, Scope, Value}; +use typst::foundations::{Bytes, Content, IntoValue, LocatableSelector, Scope, Value}; use typst::layout::Abs; +use typst::routines::EvalMode; use typst::syntax::{ast, Span, SyntaxNode}; use typst::visualize::Color; use typst::World; -use typst_pdf::PdfOptions; -use typst_shim::eval::EvalMode; +use typst_eval::eval_string; -use crate::model::{ExportPdfTask, ExportPngTask, ExportSvgTask}; +use crate::model::{ExportHtmlTask, ExportPngTask, ExportSvgTask}; use crate::primitives::TaskWhen; use crate::{ExportTransform, Pages, QueryTask}; @@ -40,10 +37,10 @@ pub struct HtmlFlag; pub struct ExportTimings; impl ExportTimings { - pub fn needs_run( + pub fn needs_run( snap: &CompileSnapshot, timing: Option, - docs: Option<&TypstPagedDocument>, + docs: Option<&D>, ) -> Option { let s = snap.signal; let when = timing.unwrap_or(TaskWhen::Never); @@ -56,7 +53,7 @@ impl ExportTimings { TaskWhen::OnType => Some(s.by_mem_events), TaskWhen::OnSave => Some(s.by_fs_events), TaskWhen::OnDocumentHasTitle if s.by_fs_events => { - docs.map(|doc| doc.info.title.is_some()) + docs.map(|doc| doc.info().title.is_some()) } TaskWhen::OnDocumentHasTitle => Some(false), } @@ -135,7 +132,7 @@ impl ExportComputation for PngExport { pixmap .encode_png() - .map(Bytes::from) + .map(Bytes::new) .context_ut("failed to encode PNG") } } @@ -148,6 +145,21 @@ impl ExportComputation for PngExport { // } // } +pub struct HtmlExport; + +impl ExportComputation for HtmlExport { + type Output = String; + type Config = ExportHtmlTask; + + fn run( + _graph: &Arc>, + doc: &Arc, + _config: &ExportHtmlTask, + ) -> Result { + Ok(typst_html::html(doc)?) + } +} + // impl WorldComputable for HtmlExport { // type Output = Option; @@ -161,12 +173,13 @@ pub struct DocumentQuery; impl DocumentQuery { // todo: query exporter /// Retrieve the matches for the selector. - pub fn retrieve( + pub fn retrieve( world: &dyn World, selector: &str, - document: &TypstPagedDocument, + document: &D, ) -> StrResult> { - let selector = typst_shim::eval::eval_string( + let selector = eval_string( + &typst::ROUTINES, world.track(), selector, Span::detached(), @@ -185,15 +198,15 @@ impl DocumentQuery { .map_err(|e| EcoString::from(format!("failed to cast: {}", e.message())))?; Ok(document - .introspector + .introspector() .query(&selector.0) .into_iter() .collect::>()) } - fn run_inner( + fn run_inner( g: &Arc>, - doc: &Arc, + doc: &Arc, config: &QueryTask, ) -> Result> { let selector = &config.selector; @@ -212,9 +225,20 @@ impl DocumentQuery { .collect()) } - pub fn get_as_value( + pub fn doc_get_as_value( g: &Arc>, - doc: &Arc, + doc: &TypstDocument, + config: &QueryTask, + ) -> Result { + match doc { + TypstDocument::Paged(doc) => Self::get_as_value(g, doc, config), + TypstDocument::Html(doc) => Self::get_as_value(g, doc, config), + } + } + + pub fn get_as_value( + g: &Arc>, + doc: &Arc, config: &QueryTask, ) -> Result { let mapped = Self::run_inner(g, doc, config)?; @@ -232,13 +256,13 @@ impl DocumentQuery { } } -impl ExportComputation for DocumentQuery { +impl ExportComputation for DocumentQuery { type Output = SourceResult; type Config = QueryTask; fn run( g: &Arc>, - doc: &Arc, + doc: &Arc, config: &QueryTask, ) -> Result> { let pretty = false; @@ -356,19 +380,6 @@ fn parse_color(fill: String) -> anyhow::Result { } } -/// Convert [`chrono::DateTime`] to [`Datetime`] -fn convert_datetime(date_time: chrono::DateTime) -> Option { - use chrono::{Datelike, Timelike}; - Datetime::from_ymd_hms( - date_time.year(), - date_time.month().try_into().ok()?, - date_time.day().try_into().ok()?, - date_time.hour().try_into().ok()?, - date_time.minute().try_into().ok()?, - date_time.second().try_into().ok()?, - ) -} - #[cfg(test)] mod tests { diff --git a/crates/tinymist-task/src/compute/pdf.rs b/crates/tinymist-task/src/compute/pdf.rs index cb65e1b0..e9106797 100644 --- a/crates/tinymist-task/src/compute/pdf.rs +++ b/crates/tinymist-task/src/compute/pdf.rs @@ -1,7 +1,12 @@ use super::*; +use crate::model::ExportPdfTask; +use tinymist_world::args::convert_source_date_epoch; +use typst::foundations::Datetime; pub use typst_pdf::pdf; +use typst_pdf::PdfOptions; pub use typst_pdf::PdfStandard as TypstPdfStandard; +use typst_pdf::Timestamp; pub struct PdfExport; impl ExportComputation for PdfExport { @@ -23,7 +28,7 @@ impl ExportComputation for PdfExport { // todo: Some(pdf_uri.as_str()) - Ok(Bytes::from(typst_pdf::pdf( + Ok(Bytes::new(typst_pdf::pdf( doc, &PdfOptions { timestamp: convert_datetime(creation_timestamp), @@ -33,6 +38,19 @@ impl ExportComputation for PdfExport { } } +/// Convert [`chrono::DateTime`] to [`Timestamp`] +pub fn convert_datetime(date_time: chrono::DateTime) -> Option { + use chrono::{Datelike, Timelike}; + Some(Timestamp::new_utc(Datetime::from_ymd_hms( + date_time.year(), + date_time.month().try_into().ok()?, + date_time.day().try_into().ok()?, + date_time.hour().try_into().ok()?, + date_time.minute().try_into().ok()?, + date_time.second().try_into().ok()?, + )?)) +} + // impl WorldComputable for PdfExport { // type Output = Option; diff --git a/crates/tinymist-task/src/compute/text.rs b/crates/tinymist-task/src/compute/text.rs index eff56619..24e1074b 100644 --- a/crates/tinymist-task/src/compute/text.rs +++ b/crates/tinymist-task/src/compute/text.rs @@ -10,7 +10,7 @@ pub struct TextExport; impl TextExport { pub fn run_on_doc(doc: &TypstDocument) -> Result { - Ok(format!("{}", FullTextDigest(doc.clone()))) + Ok(format!("{}", FullTextDigest(doc))) } } @@ -28,9 +28,9 @@ impl ExportComputation for TextExport { } /// A full text digest of a document. -pub struct FullTextDigest(pub TypstDocument); +struct FullTextDigest<'a>(&'a TypstDocument); -impl FullTextDigest { +impl FullTextDigest<'_> { fn export_frame(f: &mut fmt::Formatter<'_>, doc: &typst::layout::Frame) -> fmt::Result { for (_, item) in doc.items() { Self::export_item(f, item)?; @@ -55,9 +55,26 @@ impl FullTextDigest { Link(..) | Tag(..) | Shape(..) | Image(..) => Ok(()), } } + + fn export_element(f: &mut fmt::Formatter<'_>, elem: &typst::html::HtmlElement) -> fmt::Result { + for child in elem.children.iter() { + Self::export_html_node(f, child)?; + } + Ok(()) + } + + fn export_html_node(f: &mut fmt::Formatter<'_>, node: &typst::html::HtmlNode) -> fmt::Result { + use typst::html::HtmlNode::*; + match node { + Tag(_) => Ok(()), + Element(elem) => Self::export_element(f, elem), + Text(t, _) => f.write_str(t.as_str()), + Frame(frame) => Self::export_frame(f, frame), + } + } } -impl fmt::Display for FullTextDigest { +impl fmt::Display for FullTextDigest<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.0 { TypstDocument::Paged(paged_doc) => { @@ -66,6 +83,10 @@ impl fmt::Display for FullTextDigest { } Ok(()) } + TypstDocument::Html(html_doc) => { + Self::export_element(f, &html_doc.root)?; + Ok(()) + } } } } diff --git a/crates/tinymist-task/src/model.rs b/crates/tinymist-task/src/model.rs index 167f0b54..926bb028 100644 --- a/crates/tinymist-task/src/model.rs +++ b/crates/tinymist-task/src/model.rs @@ -68,6 +68,8 @@ pub enum ProjectTask { ExportSvg(ExportSvgTask), /// An export HTML task. ExportHtml(ExportHtmlTask), + /// An export HTML task. + ExportSvgHtml(ExportHtmlTask), /// An export Markdown task. ExportMd(ExportMarkdownTask), /// An export Text task. @@ -88,6 +90,7 @@ impl ProjectTask { | Self::ExportPng(..) | Self::ExportSvg(..) | Self::ExportHtml(..) + | Self::ExportSvgHtml(..) | Self::ExportMd(..) | Self::ExportText(..) | Self::Query(..) => self.as_export()?.when, @@ -102,6 +105,7 @@ impl ProjectTask { Self::ExportPng(task) => &task.export, Self::ExportSvg(task) => &task.export, Self::ExportHtml(task) => &task.export, + Self::ExportSvgHtml(task) => &task.export, Self::ExportMd(task) => &task.export, Self::ExportText(task) => &task.export, Self::Query(task) => &task.export, @@ -112,7 +116,7 @@ impl ProjectTask { pub fn extension(&self) -> &str { match self { Self::ExportPdf { .. } => "pdf", - Self::Preview(..) | Self::ExportHtml { .. } => "html", + Self::Preview(..) | Self::ExportSvgHtml { .. } | Self::ExportHtml { .. } => "html", Self::ExportMd { .. } => "md", Self::ExportText { .. } => "txt", Self::ExportSvg { .. } => "svg", diff --git a/crates/tinymist-task/src/primitives.rs b/crates/tinymist-task/src/primitives.rs index bec47a17..e9408e53 100644 --- a/crates/tinymist-task/src/primitives.rs +++ b/crates/tinymist-task/src/primitives.rs @@ -236,6 +236,21 @@ impl PathPattern { } } +/// Specifies the current export target. +/// +/// The design of this configuration is not yet finalized and for this reason it +/// is guarded behind the html feature. Visit the HTML documentation page for +/// more details. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum ExportTarget { + /// The current export target is for PDF, PNG, and SVG export. + #[default] + Paged, + /// The current export target is for Html export. + Html, +} + /// A PDF standard that Typst can enforce conformance with. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, ValueEnum, Serialize, Deserialize)] #[allow(non_camel_case_types)] diff --git a/crates/tinymist-vfs/src/browser.rs b/crates/tinymist-vfs/src/browser.rs index 3cd5a34b..b8a620aa 100644 --- a/crates/tinymist-vfs/src/browser.rs +++ b/crates/tinymist-vfs/src/browser.rs @@ -55,7 +55,7 @@ impl PathAccessModel for ProxyAccessModel { })?; let data = if let Some(data) = data.dyn_ref::() { - Bytes::from(data.to_vec()) + Bytes::new(data.to_vec()) } else { return Err(FileError::AccessDenied); }; diff --git a/crates/tinymist-vfs/src/system.rs b/crates/tinymist-vfs/src/system.rs index ac5b9450..1f431e86 100644 --- a/crates/tinymist-vfs/src/system.rs +++ b/crates/tinymist-vfs/src/system.rs @@ -34,7 +34,7 @@ impl PathAccessModel for SystemAccessModel { .map_err(f)? .read_to_end(&mut buf) .map_err(f)?; - Ok(buf.into()) + Ok(Bytes::new(buf)) } } diff --git a/crates/tinymist-world/src/browser.rs b/crates/tinymist-world/src/browser.rs index de40a38f..eed3e53d 100644 --- a/crates/tinymist-world/src/browser.rs +++ b/crates/tinymist-world/src/browser.rs @@ -45,8 +45,10 @@ impl TypstBrowserUniverse { let vfs = tinymist_vfs::Vfs::new(resolver, access_model); + // todo: enable html Self::new_raw( EntryState::new_rooted(root_dir.into(), None), + false, inputs, vfs, registry, diff --git a/crates/tinymist-world/src/compute.rs b/crates/tinymist-world/src/compute.rs index 15e5375d..cb578f4b 100644 --- a/crates/tinymist-world/src/compute.rs +++ b/crates/tinymist-world/src/compute.rs @@ -1,13 +1,14 @@ #![allow(missing_docs)] use std::any::TypeId; +use std::borrow::Cow; use std::sync::{Arc, OnceLock}; -use ecow::EcoVec; use parking_lot::Mutex; use tinymist_std::error::prelude::*; -use tinymist_std::typst::TypstPagedDocument; +use tinymist_std::typst::{TypstHtmlDocument, TypstPagedDocument}; use typst::diag::{At, SourceResult, Warned}; +use typst::ecow::EcoVec; use typst::syntax::Span; use crate::snapshot::CompileSnapshot; @@ -126,7 +127,7 @@ impl WorldComputeGraph { ins: Result>, ) -> Result<(), Result>> { let entry = self.computed(TypeId::of::()).computed; - let initialized = entry.set(ins.map(|e| Arc::new(e) as AnyArc)); + let initialized = entry.set(ins.map(|e| e as AnyArc)); initialized.map_err(WorldComputeEntry::cast) } @@ -149,9 +150,6 @@ impl WorldComputeGraph { } } -pub trait Document {} -impl Document for TypstPagedDocument {} - pub trait ExportDetection { type Config: Send + Sync + 'static; @@ -171,6 +169,17 @@ pub trait ExportComputation { Self::run(g, doc, config) } + fn cast_run<'a>( + g: &Arc>, + doc: impl TryInto<&'a Arc, Error = tinymist_std::Error>, + config: &Self::Config, + ) -> Result + where + D: 'a, + { + Self::run(g, doc.try_into()?, config) + } + fn run( g: &Arc>, doc: &Arc, @@ -205,25 +214,51 @@ impl FlagTask { } pub type PagedCompilationTask = CompilationTask; +pub type HtmlCompilationTask = CompilationTask; pub struct CompilationTask(std::marker::PhantomData); -impl WorldComputable for CompilationTask { - type Output = Option>>>; +impl WorldComputable for CompilationTask +where + D: typst::Document + Send + Sync + 'static, +{ + type Output = Option>>>; fn compute(graph: &Arc>) -> Result { - let enabled = graph - .must_get::>>()? - .enabled; + let enabled = graph.must_get::>>()?.enabled; Ok(enabled.then(|| { - let mut world = graph.snap.world.clone(); - world.set_is_compiling(true); - let compiled = typst::compile(&world); - world.set_is_compiling(false); + // todo: create html world once + let is_paged_compilation = TypeId::of::() == TypeId::of::(); + let is_html_compilation = TypeId::of::() == TypeId::of::(); + + let mut world = if is_paged_compilation { + graph.snap.world.paged_task() + } else if is_html_compilation { + graph.snap.world.html_task() + } else { + Cow::Borrowed(&graph.snap.world) + }; + + world.to_mut().set_is_compiling(true); + let compiled = typst::compile::(world.as_ref()); + world.to_mut().set_is_compiling(false); + + let exclude_html_warnings = if !is_html_compilation { + compiled.warnings + } else if compiled.warnings.len() == 1 + && compiled.warnings[0] + .message + .starts_with("html export is under active development") + { + EcoVec::new() + } else { + compiled.warnings + }; + Warned { output: compiled.output.map(Arc::new), - warnings: compiled.warnings, + warnings: exclude_html_warnings, } })) } @@ -231,18 +266,25 @@ impl WorldComputable for CompilationTask pub struct OptionDocumentTask(std::marker::PhantomData); -impl WorldComputable for OptionDocumentTask { - type Output = Option>; +impl WorldComputable for OptionDocumentTask +where + D: typst::Document + Send + Sync + 'static, +{ + type Output = Option>; fn compute(graph: &Arc>) -> Result { - let doc = graph.compute::>()?; - let doc = doc.as_ref().as_ref(); - let compiled = doc.and_then(|warned| warned.output.clone().ok()); + let doc = graph.compute::>()?; + let compiled = doc + .as_ref() + .as_ref() + .and_then(|warned| warned.output.clone().ok()); Ok(compiled) } } +impl OptionDocumentTask where D: typst::Document + Send + Sync + 'static {} + struct CompilationDiagnostics { errors: Option>, warnings: Option>, @@ -261,6 +303,7 @@ impl CompilationDiagnostics { pub struct DiagnosticsTask { paged: CompilationDiagnostics, + html: CompilationDiagnostics, } impl WorldComputable for DiagnosticsTask { @@ -268,19 +311,33 @@ impl WorldComputable for DiagnosticsTask { fn compute(graph: &Arc>) -> Result { let paged = graph.compute::()?.clone(); + let html = graph.compute::()?.clone(); Ok(Self { - paged: CompilationDiagnostics::from_result(paged.as_ref()), + paged: CompilationDiagnostics::from_result(&paged), + html: CompilationDiagnostics::from_result(&html), }) } } impl DiagnosticsTask { + pub fn error_cnt(&self) -> usize { + self.paged.errors.as_ref().map_or(0, |e| e.len()) + + self.html.errors.as_ref().map_or(0, |e| e.len()) + } + + pub fn warning_cnt(&self) -> usize { + self.paged.warnings.as_ref().map_or(0, |e| e.len()) + + self.html.warnings.as_ref().map_or(0, |e| e.len()) + } + pub fn diagnostics(&self) -> impl Iterator { self.paged .errors .iter() .chain(self.paged.warnings.iter()) + .chain(self.html.errors.iter()) + .chain(self.html.warnings.iter()) .flatten() } } @@ -347,7 +404,7 @@ impl WorldComputeGraph { } /// Compile once from scratch. - pub fn pure_compile(&self) -> Warned>> { + pub fn pure_compile(&self) -> Warned>> { let res = self.ensure_main(); if let Err(err) = res { return Warned { @@ -356,7 +413,7 @@ impl WorldComputeGraph { }; } - let res = ::typst::compile(&self.snap.world); + let res = ::typst::compile::(&self.snap.world); // compile document Warned { output: res.output.map(Arc::new), @@ -368,4 +425,14 @@ impl WorldComputeGraph { pub fn compile(&self) -> Warned>> { self.pure_compile() } + + /// Compile to html once from scratch. + pub fn compile_html(&self) -> Warned>> { + self.pure_compile() + } + + // With **the compilation state**, query the matches for the selector. + // fn query(&mut self, selector: String, document: &TypstDocument) -> + // SourceResult> { self.pure_query(world, selector, + // document) } } diff --git a/crates/tinymist-world/src/font/loader.rs b/crates/tinymist-world/src/font/loader.rs index f6b33cdf..7fc80cf6 100644 --- a/crates/tinymist-world/src/font/loader.rs +++ b/crates/tinymist-world/src/font/loader.rs @@ -38,6 +38,6 @@ impl FontLoader for LazyBufferFontLoader { fn load(&mut self) -> Option { let mut buf = vec![]; self.read.take().unwrap().read_all(&mut buf).ok()?; - Font::new(buf.into(), self.index) + Font::new(Bytes::new(buf), self.index) } } diff --git a/crates/tinymist-world/src/font/system.rs b/crates/tinymist-world/src/font/system.rs index 1d3703eb..922ee6d7 100644 --- a/crates/tinymist-world/src/font/system.rs +++ b/crates/tinymist-world/src/font/system.rs @@ -136,8 +136,8 @@ impl SystemFontSearcher { // Source3: add the fonts in memory. for font_data in opts.with_embedded_fonts { self.add_memory_font(match font_data { - Cow::Borrowed(data) => Bytes::from_static(data), - Cow::Owned(data) => Bytes::from(data), + Cow::Borrowed(data) => Bytes::new(data), + Cow::Owned(data) => Bytes::new(data), }); } diff --git a/crates/tinymist-world/src/font/web/mod.rs b/crates/tinymist-world/src/font/web/mod.rs index 7dadf2d4..ee994fb4 100644 --- a/crates/tinymist-world/src/font/web/mod.rs +++ b/crates/tinymist-world/src/font/web/mod.rs @@ -368,7 +368,7 @@ impl FontLoader for WebFontLoader { ); // let blob = pollster::block_on(JsFuture::from(blob.array_buffer())).unwrap(); let blob = font.load()?; - let blob = Bytes::from(js_sys::Uint8Array::new(&blob).to_vec()); + let blob = Bytes::new(js_sys::Uint8Array::new(&blob).to_vec()); Font::new(blob, self.index) } @@ -406,7 +406,7 @@ impl BrowserFontSearcher { /// Add fonts that are embedded in the binary. pub fn add_embedded(&mut self) { for font_data in typst_assets::fonts() { - let buffer = Bytes::from_static(font_data); + let buffer = Bytes::new(font_data); for font in Font::iter(buffer) { self.book.push(font.info().clone()); self.fonts.push(FontSlot::with_value(Some(font))); diff --git a/crates/tinymist-world/src/system.rs b/crates/tinymist-world/src/system.rs index 008a285c..2c42ff74 100644 --- a/crates/tinymist-world/src/system.rs +++ b/crates/tinymist-world/src/system.rs @@ -40,8 +40,11 @@ impl TypstSystemUniverse { let registry: Arc = Arc::default(); let resolver = Arc::new(RegistryPathMapper::new(registry.clone())); let inputs = std::mem::take(&mut opts.inputs); + + // todo: enable html Ok(Self::new_raw( opts.entry.clone().try_into()?, + false, Some(Arc::new(LazyHash::new(inputs))), Vfs::new(resolver, SystemAccessModel {}), registry, @@ -72,8 +75,10 @@ impl SystemUniverseBuilder { let registry = Arc::new(package_registry); let resolver = Arc::new(RegistryPathMapper::new(registry.clone())); + // todo: enable html TypstSystemUniverse::new_raw( entry, + false, Some(inputs), Vfs::new(resolver, SystemAccessModel {}), registry, @@ -118,13 +123,15 @@ mod tests { #[test] fn test_args() { + use tinymist_std::typst::TypstPagedDocument; + let args = CompileOnceArgs::parse_from(["tinymist", "main.typ"]); let verse = args .resolve_system() .expect("failed to resolve system universe"); let world = verse.snapshot(); - let _res = typst::compile(&world); + let _res = typst::compile::(&world); } static FONT_COMPUTED: AtomicBool = AtomicBool::new(false); diff --git a/crates/tinymist-world/src/world.rs b/crates/tinymist-world/src/world.rs index 956c4bac..b1a7e28b 100644 --- a/crates/tinymist-world/src/world.rs +++ b/crates/tinymist-world/src/world.rs @@ -1,4 +1,5 @@ use std::{ + borrow::Cow, num::NonZeroUsize, ops::Deref, path::{Path, PathBuf}, @@ -49,6 +50,8 @@ pub struct CompilerUniverse { /// State for the *root & entry* of compilation. /// The world forbids direct access to files outside this directory. entry: EntryState, + /// Whether to enable HTML features. + enable_html: bool, /// Additional input arguments to compile the entry file. inputs: Arc>, @@ -73,6 +76,7 @@ impl CompilerUniverse { /// + See [`crate::TypstBrowserUniverse::new`] for browser environment. pub fn new_raw( entry: EntryState, + enable_html: bool, inputs: Option>>, vfs: Vfs, registry: Arc, @@ -80,6 +84,7 @@ impl CompilerUniverse { ) -> Self { Self { entry, + enable_html, inputs: inputs.unwrap_or_default(), revision: NonZeroUsize::new(1).expect("initial revision is 1"), @@ -151,8 +156,9 @@ impl CompilerUniverse { pub fn snapshot_with(&self, mutant: Option) -> CompilerWorld { let w = CompilerWorld { entry: self.entry.clone(), + enable_html: self.enable_html, inputs: self.inputs.clone(), - library: create_library(self.inputs.clone()), + library: create_library(self.inputs.clone(), self.enable_html), font_resolver: self.font_resolver.clone(), registry: self.registry.clone(), vfs: self.vfs.snapshot(), @@ -430,6 +436,8 @@ pub struct CompilerWorld { /// State for the *root & entry* of compilation. /// The world forbids direct access to files outside this directory. entry: EntryState, + /// Whether to enable HTML features. + enable_html: bool, /// Additional input arguments to compile the entry file. inputs: Arc>, @@ -467,7 +475,10 @@ impl CompilerWorld { // Fetch to avoid inconsistent state. let _ = self.today(None); - let library = mutant.inputs.clone().map(create_library); + let library = mutant + .inputs + .clone() + .map(|inputs| create_library(inputs, self.enable_html)); let root_changed = if let Some(e) = mutant.entry.as_ref() { self.entry.workspace_root() != e.workspace_root() @@ -476,6 +487,7 @@ impl CompilerWorld { }; let mut world = CompilerWorld { + enable_html: self.enable_html, inputs: mutant.inputs.unwrap_or_else(|| self.inputs.clone()), library: library.unwrap_or_else(|| self.library.clone()), entry: mutant.entry.unwrap_or_else(|| self.entry.clone()), @@ -567,6 +579,42 @@ impl CompilerWorld { .clone_source_cache() .evict(self.vfs.revision(), threshold); } + + /// A list of all available packages and optionally descriptions for them. + /// + /// This function is optional to implement. It enhances the user experience + /// by enabling autocompletion for packages. Details about packages from the + /// `@preview` namespace are available from + /// `https://packages.typst.org/preview/index.json`. + pub fn packages(&self) -> &[(PackageSpec, Option)] { + self.registry.packages() + } + + pub fn paged_task(&self) -> Cow<'_, CompilerWorld> { + let enabled_paged = !self.library.features.is_enabled(typst::Feature::Html); + + if enabled_paged { + return Cow::Borrowed(self); + } + + let mut world = self.clone(); + world.library = create_library(world.inputs.clone(), false); + + Cow::Owned(world) + } + + pub fn html_task(&self) -> Cow<'_, CompilerWorld> { + let enabled_html = self.library.features.is_enabled(typst::Feature::Html); + + if enabled_html { + return Cow::Borrowed(self); + } + + let mut world = self.clone(); + world.library = create_library(world.inputs.clone(), true); + + Cow::Owned(world) + } } impl ShadowApi for CompilerWorld { @@ -687,16 +735,6 @@ impl World for CompilerWorld { naive.day().try_into().ok()?, ) } - - /// A list of all available packages and optionally descriptions for them. - /// - /// This function is optional to implement. It enhances the user experience - /// by enabling autocompletion for packages. Details about packages from the - /// `@preview` namespace are available from - /// `https://packages.typst.org/preview/index.json`. - fn packages(&self) -> &[(PackageSpec, Option)] { - self.registry.packages() - } } impl EntryReader for CompilerWorld { @@ -774,9 +812,16 @@ impl<'a, F: CompilerFeat> codespan_reporting::files::Files<'a> for CompilerWorld } #[comemo::memoize] -fn create_library(inputs: Arc>) -> Arc> { +fn create_library(inputs: Arc>, enable_html: bool) -> Arc> { + let features = if enable_html { + typst::Features::from_iter([typst::Feature::Html]) + } else { + typst::Features::default() + }; + let lib = typst::Library::builder() .with_inputs(inputs.deref().deref().clone()) + .with_features(features) .build(); Arc::new(LazyHash::new(lib)) diff --git a/crates/tinymist/Cargo.toml b/crates/tinymist/Cargo.toml index 0f96ce43..e06c7b93 100644 --- a/crates/tinymist/Cargo.toml +++ b/crates/tinymist/Cargo.toml @@ -50,7 +50,7 @@ parking_lot.workspace = true paste.workspace = true rayon.workspace = true reflexo.workspace = true -reflexo-typst = { workspace = true, features = ["system"] } +reflexo-typst = { workspace = true, features = ["system", "svg"] } reflexo-vec2svg.workspace = true rpds.workspace = true serde.workspace = true @@ -62,9 +62,9 @@ tinymist-assets = { workspace = true } tinymist-query.workspace = true tinymist-std.workspace = true tinymist-core = { workspace = true, default-features = false, features = [] } -tinymist-project = { workspace = true, features = ["system"] } +tinymist-project = { workspace = true, features = ["lsp"] } tinymist-render.workspace = true -tokio = { workspace = true, features = ["fs"] } +tokio = { workspace = true, features = ["rt-multi-thread", "io-std"] } tokio-util.workspace = true toml.workspace = true ttf-parser.workspace = true @@ -74,6 +74,7 @@ typst-svg.workspace = true typst-pdf.workspace = true typst-render.workspace = true typst-timing.workspace = true +typst-html = { workspace = true, optional = true } typst-shim.workspace = true typst-preview = { workspace = true, optional = true } typst-ansi-hl.workspace = true @@ -84,7 +85,7 @@ unicode-script.workspace = true walkdir.workspace = true [features] -default = ["cli", "preview", "embed-fonts", "no-content-hint"] +default = ["cli", "html", "pdf", "preview", "embed-fonts", "no-content-hint"] cli = ["sync-lsp/clap", "clap/wrap_help"] @@ -98,11 +99,15 @@ dhat-heap = ["dhat"] # into the binary. embed-fonts = ["tinymist-project/fonts"] +# Enable the experimental HTML backend. +html = ["dep:typst-html"] + +pdf = ["tinymist-task/pdf"] + # Disable the default content hint. # This requires modifying typst. no-content-hint = [ - "tinymist-query/no-content-hint", - "tinymist-task/no-content-hint", + "typst-preview/no-content-hint", "reflexo-typst/no-content-hint", "reflexo-vec2svg/no-content-hint", ] @@ -111,7 +116,6 @@ preview = [ "typst-preview", "typst-preview/clap", "tinymist-assets/typst-preview", - "tinymist-project/preview", "hyper-tungstenite", ] diff --git a/crates/tinymist/src/args.rs b/crates/tinymist/src/args.rs index aff33524..a3ab2907 100644 --- a/crates/tinymist/src/args.rs +++ b/crates/tinymist/src/args.rs @@ -3,8 +3,8 @@ use std::path::Path; use sync_lsp::transport::MirrorArgs; use tinymist::{ - project::{DocCommands, TaskCommands}, - tool::project::{CompileArgs, GenerateScriptArgs}, + project::DocCommands, + tool::project::{CompileArgs, GenerateScriptArgs, TaskCommands}, CompileFontArgs, CompileOnceArgs, }; use tinymist_core::LONG_VERSION; diff --git a/crates/tinymist/src/init.rs b/crates/tinymist/src/init.rs index 8c55a28e..ab32f496 100644 --- a/crates/tinymist/src/init.rs +++ b/crates/tinymist/src/init.rs @@ -19,6 +19,7 @@ use tinymist_project::{ use tinymist_query::analysis::{Modifier, TokenType}; use tinymist_query::{CompletionFeat, PositionEncoding}; use tinymist_render::PeriscopeArgs; +use tinymist_task::ExportTarget; use typst::foundations::IntoValue; use typst_shim::utils::{Deferred, LazyHash}; @@ -27,7 +28,7 @@ use typst_shim::utils::{Deferred, LazyHash}; // textDocument.definition.linkSupport capability. use super::*; -use crate::world::ImmutDict; +use crate::project::ImmutDict; /// Capability to add valid commands to the arguments. pub trait AddCommands { @@ -279,6 +280,7 @@ const CONFIG_ITEMS: &[&str] = &[ "formatterMode", "formatterPrintWidth", "formatterIndentSize", + "exportTarget", "completion", "fontPaths", "systemFonts", @@ -311,6 +313,8 @@ pub struct Config { pub formatter_indent_size: Option, /// Whether to remove html from markup content in responses. pub support_html_in_markdown: bool, + /// Tinymist's default export target. + pub export_target: ExportTarget, /// Tinymist's completion features. pub completion: CompletionFeat, } @@ -396,6 +400,7 @@ impl Config { assign_config!(formatter_print_width := "formatterPrintWidth"?: Option); assign_config!(formatter_indent_size := "formatterIndentSize"?: Option); assign_config!(support_html_in_markdown := "supportHtmlInMarkdown"?: bool); + assign_config!(export_target := "exportTarget"?: ExportTarget); assign_config!(completion := "completion"?: CompletionFeat); assign_config!(completion.trigger_suggest := "triggerSuggest"?: bool); assign_config!(completion.trigger_parameter_hints := "triggerParameterHints"?: bool); @@ -431,13 +436,25 @@ impl Config { pub(crate) fn export(&self) -> ExportUserConfig { let compile_config = &self.compile; + let export = ExportTask { + output: Some(compile_config.output_path.clone()), + when: compile_config.export_pdf, + transform: vec![], + }; + ExportUserConfig { + export_target: self.export_target, + // todo: we only have `exportPdf` for now + // task: match self.export_target { + // ExportTarget::Paged => ProjectTask::ExportPdf(ExportPdfTask { + // export, + // pdf_standards: vec![], + // creation_timestamp: compile_config.determine_creation_timestamp(), + // }), + // ExportTarget::Html => ProjectTask::ExportHtml(ExportHtmlTask { export }), + // }, task: ProjectTask::ExportPdf(ExportPdfTask { - export: ExportTask { - output: Some(compile_config.output_path.clone()), - when: compile_config.export_pdf, - transform: vec![], - }, + export, pdf_standards: vec![], creation_timestamp: compile_config.determine_creation_timestamp(), }), @@ -703,7 +720,7 @@ impl CompileConfig { log::info!("creating SharedFontResolver with {opts:?}"); Derived(Deferred::new(|| { - crate::world::LspUniverseBuilder::resolve_fonts(opts) + crate::project::LspUniverseBuilder::resolve_fonts(opts) .map(Arc::new) .expect("failed to create font book") })) diff --git a/crates/tinymist/src/input.rs b/crates/tinymist/src/input.rs index 97c68115..879d921f 100644 --- a/crates/tinymist/src/input.rs +++ b/crates/tinymist/src/input.rs @@ -29,7 +29,7 @@ impl ServerState { self.memory_changes .insert(path.clone(), Source::detached(content.clone())); - let content: Bytes = content.as_bytes().into(); + let content = Bytes::from_string(content); // todo: is it safe to believe that the path is normalized? let files = FileChangeSet::new_inserts(vec![(path, FileResult::Ok(content).into())]); @@ -74,7 +74,7 @@ impl ServerState { } } - let snapshot = FileResult::Ok(source.text().as_bytes().into()).into(); + let snapshot = FileResult::Ok(Bytes::from_string(source.text().to_owned())).into(); let files = FileChangeSet::new_inserts(vec![(path.clone(), snapshot)]); diff --git a/crates/tinymist/src/lib.rs b/crates/tinymist/src/lib.rs index e1deb7e8..6f0192b8 100644 --- a/crates/tinymist/src/lib.rs +++ b/crates/tinymist/src/lib.rs @@ -46,6 +46,5 @@ use lsp_query::QueryFuture; use lsp_server::ResponseError; use serde_json::from_value; use sync_lsp::*; -use tinymist_std::error::Result; use utils::*; use world::*; diff --git a/crates/tinymist/src/project.rs b/crates/tinymist/src/project.rs index f2c40e49..79c9e63d 100644 --- a/crates/tinymist/src/project.rs +++ b/crates/tinymist/src/project.rs @@ -102,7 +102,7 @@ impl ServerState { self.memory_changes .iter() .map(|(path, content)| { - let content = Bytes::from(content.clone().text().as_bytes()); + let content = Bytes::from_string(content.clone().text().to_owned()); (path.clone(), FileResult::Ok(content).into()) }) .collect(), @@ -125,7 +125,8 @@ impl ServerState { entry: Option, ) -> Result { let entry = self.config.compile.entry_resolver.resolve(entry); - self.project.restart_dedicate(dedicate, entry) + let enable_html = matches!(self.config.export_target, ExportTarget::Html); + self.project.restart_dedicate(dedicate, entry, enable_html) } /// Create a fresh [`ProjectState`]. @@ -177,6 +178,7 @@ impl ServerState { notified_revision: Mutex::default(), }); + let export_target = config.export_target; let default_path = config.compile.entry_resolver.resolve_default(); let entry = config.compile.entry_resolver.resolve(default_path); let inputs = config.compile.determine_inputs(); @@ -189,7 +191,13 @@ impl ServerState { let embedded_fonts = Arc::new(LspUniverseBuilder::only_embedded_fonts().unwrap()); let package_registry = LspUniverseBuilder::resolve_package(cert_path.clone(), Some(&package)); - let verse = LspUniverseBuilder::build(entry, inputs, embedded_fonts, package_registry); + let verse = LspUniverseBuilder::build( + entry, + export_target, + inputs, + embedded_fonts, + package_registry, + ); // todo: unify filesystem watcher let (dep_tx, dep_rx) = mpsc::unbounded_channel(); @@ -340,8 +348,9 @@ impl ProjectState { &mut self, group: &str, entry: EntryState, + enable_html: bool, ) -> Result { - self.compiler.restart_dedicate(group, entry) + self.compiler.restart_dedicate(group, entry, enable_html) } } @@ -610,6 +619,8 @@ impl CompileHandler for CompileHandlerImpl if let Some(inner) = self.preview.get(&snap.id) { let snap = snap.clone(); inner.notify_compile(Arc::new(crate::tool::preview::PreviewCompileView { snap })); + } else { + log::info!("Project: no preview for {:?}", snap.id); } } } diff --git a/crates/tinymist/src/resource/mod.rs b/crates/tinymist/src/resource/mod.rs index 4024cf23..61ff627b 100644 --- a/crates/tinymist/src/resource/mod.rs +++ b/crates/tinymist/src/resource/mod.rs @@ -12,8 +12,7 @@ mod prelude { pub use serde_json::Value as JsonValue; pub use sync_lsp::*; pub use tinymist_std::error::prelude::*; - pub use typst::foundations::{Scope, Value}; - pub use typst::symbols::Symbol; + pub use typst::foundations::{Scope, Symbol, Value}; pub(crate) use crate::ServerState; diff --git a/crates/tinymist/src/resource/symbols.rs b/crates/tinymist/src/resource/symbols.rs index 0639b9bc..19fac868 100644 --- a/crates/tinymist/src/resource/symbols.rs +++ b/crates/tinymist/src/resource/symbols.rs @@ -1,9 +1,11 @@ use std::{collections::BTreeMap, path::Path, sync::Arc}; +use reflexo_typst::TypstPagedDocument; use reflexo_typst::{vector::font::GlyphId, TypstFont}; use sync_lsp::LspResult; use tinymist_project::LspCompileSnapshot; use tinymist_std::typst::TypstDocument; +use typst::foundations::Bytes; use typst::{syntax::VirtualPath, World}; use crate::world::{base::ShadowApi, EntryState, TaskInputs}; @@ -949,10 +951,23 @@ impl ServerState { /// Get the all valid symbols pub async fn get_symbol_resources(snap: LspCompileSnapshot) -> LspResult { let mut symbols = ResourceSymbolMap::new(); - use typst::symbols::{emoji, sym}; - populate_scope(sym().scope(), "sym", SymCategory::Misc, &mut symbols); + + let std = snap + .world + .library + .std + .read() + .scope() + .ok_or_else(|| internal_error("cannot get std scope"))?; + let sym = std + .get("sym") + .ok_or_else(|| internal_error("cannot get sym"))?; + + if let Some(scope) = sym.read().scope() { + populate_scope(scope, "sym", SymCategory::Misc, &mut symbols); + } // todo: disabling emoji module, as there is performant issue on emojis - let _ = emoji; + // let _ = emoji; // populate_scope(emoji().scope(), "emoji", SymCategory::Emoji, &mut symbols); const PRELUDE: &str = r#"#show math.equation: set text(font: ( @@ -984,11 +999,11 @@ impl ServerState { ..Default::default() }); forked - .map_shadow_by_id(forked.main(), math_shaping_text.into_bytes().into()) + .map_shadow_by_id(forked.main(), Bytes::from_string(math_shaping_text)) .map_err(|e| error_once!("cannot map shadow", err: e)) .map_err(internal_error)?; - let sym_doc = typst::compile(&forked) + let sym_doc = typst::compile::(&forked) .output .map_err(|e| error_once!("cannot compile symbols", err: format!("{e:?}"))) .map_err(internal_error)?; @@ -1115,10 +1130,15 @@ fn trait_symbol_fonts( impl Worker<'_> { fn work(&mut self, doc: &TypstDocument) { - let TypstDocument::Paged(paged_doc) = doc; - for (pg, s) in paged_doc.pages.iter().zip(self.symbols.iter()) { - self.active = s; - self.work_frame(&pg.frame); + match doc { + TypstDocument::Paged(paged_doc) => { + for (pg, s) in paged_doc.pages.iter().zip(self.symbols.iter()) { + self.active = s; + self.work_frame(&pg.frame); + } + } + // todo: handle html + TypstDocument::Html(..) => {} } } @@ -1134,8 +1154,6 @@ fn trait_symbol_fonts( | FrameItem::Image(_, _, _) | FrameItem::Link(_, _) | FrameItem::Tag(_) => continue, - #[cfg(not(feature = "no-content-hint"))] - FrameItem::ContentHint(_) => continue, }; let font = text.font.clone(); @@ -1192,7 +1210,7 @@ fn populate( name, ResourceSymbolItem { category, - unicode: ch.char() as u32, + unicode: ch as u32, glyphs: vec![], }, ); @@ -1205,8 +1223,8 @@ fn populate_scope( fallback_cat: SymCategory, out: &mut ResourceSymbolMap, ) { - for (k, v, _) in sym.iter() { - let Value::Symbol(sym) = v else { + for (k, b) in sym.iter() { + let Value::Symbol(sym) = b.read() else { continue; }; diff --git a/crates/tinymist/src/server.rs b/crates/tinymist/src/server.rs index 12ccd6d9..a376b320 100644 --- a/crates/tinymist/src/server.rs +++ b/crates/tinymist/src/server.rs @@ -237,6 +237,7 @@ impl ServerState { // commands .with_command_("tinymist.exportPdf", State::export_pdf) .with_command_("tinymist.exportSvg", State::export_svg) + // .with_command_("tinymist.exportSvgHtml", State::export_html) .with_command_("tinymist.exportPng", State::export_png) .with_command_("tinymist.exportText", State::export_text) .with_command_("tinymist.exportHtml", State::export_html) diff --git a/crates/tinymist/src/task/export.rs b/crates/tinymist/src/task/export.rs index 53eb8afe..b57f63a2 100644 --- a/crates/tinymist/src/task/export.rs +++ b/crates/tinymist/src/task/export.rs @@ -7,23 +7,20 @@ use crate::project::{ ApplyProjectTask, CompiledArtifact, ExportHtmlTask, ExportMarkdownTask, ExportPdfTask, ExportPngTask, ExportTextTask, TaskWhen, }; -use anyhow::bail; use reflexo::ImmutPath; -use reflexo_typst::TypstDatetime; use tinymist_project::{ convert_source_date_epoch, EntryReader, ExportSvgTask, ExportTask as ProjectExportTask, LspCompiledArtifact, ProjectTask, QueryTask, }; use tinymist_std::error::prelude::*; use tinymist_std::typst::TypstDocument; -use tinymist_task::get_page_selection; +use tinymist_task::{convert_datetime, get_page_selection, ExportTarget, TextExport}; use tokio::sync::mpsc; use typlite::Typlite; use typst::foundations::IntoValue; use typst::visualize::Color; use typst_pdf::PdfOptions; -use crate::tool::text::FullTextDigest; use crate::{actor::editor::EditorRequest, tool::word_count}; use super::{FutureFolder, SyncTaskFactory}; @@ -136,7 +133,7 @@ impl ExportTask { task: ProjectTask, artifact: LspCompiledArtifact, lock_dir: Option, - ) -> anyhow::Result> { + ) -> Result> { use reflexo_vec2svg::DefaultExportFeature; use ProjectTask::*; @@ -179,16 +176,32 @@ impl ExportTask { }); // Prepare the document. - let doc = doc.map_err(|_| anyhow::anyhow!("no document"))?; + let doc = doc.ok().context("no document")?; // Prepare data. let kind2 = task.clone(); - let data = FutureFolder::compute(move |_| -> anyhow::Result> { + let data = FutureFolder::compute(move |_| -> Result> { let doc = &doc; // static BLANK: Lazy = Lazy::new(Page::default); - let TypstDocument::Paged(paged_doc) = &doc; - let first_page = paged_doc.pages.first().unwrap(); + let html_doc = || { + Ok(match &doc { + TypstDocument::Html(html_doc) => html_doc, + TypstDocument::Paged(_) => bail!("expected html document, found Paged"), + }) + }; + let paged_doc = || { + Ok(match &doc { + TypstDocument::Paged(paged_doc) => paged_doc, + TypstDocument::Html(_) => bail!("expected paged document, found HTML"), + }) + }; + let first_page = || { + paged_doc()? + .pages + .first() + .context("no first page to export") + }; Ok(match kind2 { Preview(..) => vec![], // todo: more pdf flags @@ -204,7 +217,7 @@ impl ExportTask { // todo: Some(pdf_uri.as_str()) typst_pdf::pdf( - paged_doc, + paged_doc()?, &PdfOptions { timestamp: convert_datetime(creation_timestamp), ..Default::default() @@ -221,9 +234,8 @@ impl ExportTask { one, }) => { let pretty = false; - let elements = - reflexo_typst::query::retrieve(&snap.world, &selector, paged_doc) - .map_err(|e| anyhow::anyhow!("failed to retrieve: {e}"))?; + let elements = reflexo_typst::query::retrieve(&snap.world, &selector, doc) + .map_err(|e| anyhow::anyhow!("failed to retrieve: {e}"))?; if one && elements.len() != 1 { bail!("expected exactly one element, found {}", elements.len()); } @@ -245,11 +257,16 @@ impl ExportTask { serialize(&mapped, &format, pretty).map(String::into_bytes)? } } - ExportHtml(ExportHtmlTask { export: _ }) => { - reflexo_vec2svg::render_svg_html::(paged_doc).into_bytes() + ExportHtml(ExportHtmlTask { export: _ }) => typst_html::html(html_doc()?) + .map_err(|e| format!("export error: {e:?}")) + .context_ut("failed to export to html")? + .into_bytes(), + ExportSvgHtml(ExportHtmlTask { export: _ }) => { + reflexo_vec2svg::render_svg_html::(paged_doc()?) + .into_bytes() } ExportText(ExportTextTask { export: _ }) => { - format!("{}", FullTextDigest(doc.clone())).into_bytes() + TextExport::run_on_doc(doc)?.into_bytes() } ExportMd(ExportMarkdownTask { export: _ }) => { let conv = Typlite::new(Arc::new(snap.world)) @@ -262,9 +279,9 @@ impl ExportTask { let (is_first, merged_gap) = get_page_selection(&export)?; if is_first { - typst_svg::svg(first_page).into_bytes() + typst_svg::svg(first_page()?).into_bytes() } else { - typst_svg::svg_merged(paged_doc, merged_gap).into_bytes() + typst_svg::svg_merged(paged_doc()?, merged_gap).into_bytes() } } ExportPng(ExportPngTask { export, ppi, fill }) => { @@ -282,9 +299,9 @@ impl ExportTask { let (is_first, merged_gap) = get_page_selection(&export)?; let pixmap = if is_first { - typst_render::render(first_page, ppi / 72.) + typst_render::render(first_page()?, ppi / 72.) } else { - typst_render::render_merged(paged_doc, ppi / 72., merged_gap, Some(fill)) + typst_render::render_merged(paged_doc()?, ppi / 72., merged_gap, Some(fill)) }; pixmap @@ -306,6 +323,8 @@ impl ExportTask { /// User configuration for export. #[derive(Clone, PartialEq, Eq)] pub struct ExportUserConfig { + /// Tinymist's default export target. + pub export_target: ExportTarget, pub task: ProjectTask, pub count_words: bool, } @@ -313,6 +332,7 @@ pub struct ExportUserConfig { impl Default for ExportUserConfig { fn default() -> Self { Self { + export_target: ExportTarget::default(), task: ProjectTask::ExportPdf(ExportPdfTask { export: ProjectExportTask { when: TaskWhen::Never, @@ -327,7 +347,7 @@ impl Default for ExportUserConfig { } } -fn parse_color(fill: String) -> anyhow::Result { +fn parse_color(fill: String) -> Result { match fill.as_str() { "black" => Ok(Color::BLACK), "white" => Ok(Color::WHITE), @@ -335,13 +355,13 @@ fn parse_color(fill: String) -> anyhow::Result { "green" => Ok(Color::GREEN), "blue" => Ok(Color::BLUE), hex if hex.starts_with('#') => { - Color::from_str(&hex[1..]).map_err(|e| anyhow::anyhow!("failed to parse color: {e}")) + Color::from_str(&hex[1..]).context_ut("failed to parse color") } _ => bail!("invalid color: {fill}"), } } -fn log_err(artifact: anyhow::Result) -> Option { +fn log_err(artifact: Result) -> Option { match artifact { Ok(v) => Some(v), Err(err) => { @@ -351,28 +371,15 @@ fn log_err(artifact: anyhow::Result) -> Option { } } -/// Convert [`chrono::DateTime`] to [`TypstDatetime`] -fn convert_datetime(date_time: chrono::DateTime) -> Option { - use chrono::{Datelike, Timelike}; - TypstDatetime::from_ymd_hms( - date_time.year(), - date_time.month().try_into().ok()?, - date_time.day().try_into().ok()?, - date_time.hour().try_into().ok()?, - date_time.minute().try_into().ok()?, - date_time.second().try_into().ok()?, - ) -} - /// Serialize data to the output format. -fn serialize(data: &impl serde::Serialize, format: &str, pretty: bool) -> anyhow::Result { +fn serialize(data: &impl serde::Serialize, format: &str, pretty: bool) -> Result { Ok(match format { - "json" if pretty => serde_json::to_string_pretty(data)?, - "json" => serde_json::to_string(data)?, - "yaml" => serde_yaml::to_string(&data)?, + "json" if pretty => serde_json::to_string_pretty(data).context("serialize to json")?, + "json" => serde_json::to_string(data).context("serialize to json")?, + "yaml" => serde_yaml::to_string(&data).context_ut("serialize to yaml")?, "txt" => { use serde_json::Value::*; - let value = serde_json::to_value(data)?; + let value = serde_json::to_value(data).context("serialize to json value")?; match value { String(s) => s, _ => { diff --git a/crates/tinymist/src/task/export2.rs b/crates/tinymist/src/task/export2.rs index 7f79b73b..003ee59f 100644 --- a/crates/tinymist/src/task/export2.rs +++ b/crates/tinymist/src/task/export2.rs @@ -2,19 +2,19 @@ use std::sync::Arc; -use reflexo_typst::Bytes; -use tinymist_project::{EntryReader, LspCompilerFeat, PdfExport, PngExport, SvgExport, TaskWhen}; +use reflexo_typst::{Bytes, CompilerFeat, EntryReader, ExportWebSvgHtmlTask, WebSvgHtmlExport}; +use reflexo_vec2svg::DefaultExportFeature; +use tinymist_project::{HtmlExport, LspCompilerFeat, PdfExport, PngExport, SvgExport, TaskWhen}; use tinymist_std::error::prelude::*; -use tinymist_std::typst::{TypstDocument, TypstPagedDocument}; -use tinymist_task::ExportTimings; +use tinymist_std::typst::TypstPagedDocument; +use tinymist_task::{ExportTimings, TextExport}; use typlite::Typlite; use typst::diag::SourceResult; -use crate::project::{ExportMarkdownTask, ExportTextTask, ProjectTask}; -use crate::tool::text::FullTextDigest; +use crate::project::{ExportMarkdownTask, ProjectTask}; use crate::world::base::{ - CompilerFeat, ConfigTask, DiagnosticsTask, ExportComputation, FlagTask, OptionDocumentTask, - PagedCompilationTask, WorldComputable, WorldComputeGraph, + ConfigTask, DiagnosticsTask, ExportComputation, FlagTask, HtmlCompilationTask, + OptionDocumentTask, PagedCompilationTask, WorldComputable, WorldComputeGraph, }; #[derive(Clone, Copy, Default)] @@ -24,6 +24,7 @@ impl ProjectCompilation { pub fn preconfig_timings(graph: &Arc>) -> Result { // todo: configure run_diagnostics! let paged_diag = Some(TaskWhen::OnType); + let html_diag = Some(TaskWhen::Never); let pdf: Option = graph .get::>::Config>>() @@ -37,6 +38,10 @@ impl ProjectCompilation { .get::>::Config>>() .transpose()? .map(|config| config.export.when); + let html: Option = graph + .get::>::Config>>() + .transpose()? + .map(|config| config.export.when); let md: Option = graph .get::>() .transpose()? @@ -50,10 +55,12 @@ impl ProjectCompilation { let check = |timing| ExportTimings::needs_run(&graph.snap, timing, doc).unwrap_or(true); let compile_paged = [paged_diag, pdf, svg, png, text, md].into_iter().any(check); + let compile_html = [html_diag, html].into_iter().any(check); let _ = graph.provide::>(Ok(FlagTask::flag(compile_paged))); + let _ = graph.provide::>(Ok(FlagTask::flag(compile_html))); - Ok(compile_paged) + Ok(compile_paged || compile_html) } } @@ -70,12 +77,15 @@ impl WorldComputable for ProjectCompilation { pub struct ProjectExport; impl ProjectExport { - fn export_bytes>( + fn export_bytes< + D: typst::Document + Send + Sync + 'static, + T: ExportComputation, + >( graph: &Arc>, when: Option, config: &T::Config, ) -> Result> { - let doc = graph.compute::>()?; + let doc = graph.compute::>()?; let doc = doc.as_ref(); let n = ExportTimings::needs_run(&graph.snap, when, doc.as_deref()).unwrap_or(true); if !n { @@ -86,12 +96,15 @@ impl ProjectExport { res.transpose() } - fn export_string>( + fn export_string< + D: typst::Document + Send + Sync + 'static, + T: ExportComputation, + >( graph: &Arc>, when: Option, config: &T::Config, ) -> Result> { - let doc = graph.compute::>()?; + let doc = graph.compute::>()?; let doc = doc.as_ref(); let n = ExportTimings::needs_run(&graph.snap, when, doc.as_deref()).unwrap_or(true); if !n { @@ -99,11 +112,7 @@ impl ProjectExport { } let doc = doc.as_ref(); - let res = doc.map(|doc| { - T::run(graph, doc, config) - .map(String::into_bytes) - .map(Bytes::from) - }); + let res = doc.map(|doc| T::run(graph, doc, config).map(Bytes::from_string)); res.transpose() } } @@ -124,10 +133,17 @@ impl WorldComputable for ProjectExport { use ProjectTask::*; match config.as_ref() { Preview(..) => todo!(), - ExportPdf(config) => Self::export_bytes::(graph, when, config), - ExportPng(config) => Self::export_bytes::(graph, when, config), - ExportSvg(config) => Self::export_string::(graph, when, config), - ExportHtml(_config) => todo!(), + ExportPdf(config) => Self::export_bytes::<_, PdfExport>(graph, when, config), + ExportPng(config) => Self::export_bytes::<_, PngExport>(graph, when, config), + ExportSvg(config) => Self::export_string::<_, SvgExport>(graph, when, config), + ExportHtml(config) => Self::export_string::<_, HtmlExport>(graph, when, config), + // todo: configuration + ExportSvgHtml(_config) => Self::export_string::< + _, + WebSvgHtmlExport, + >( + graph, when, &ExportWebSvgHtmlTask::default() + ), ExportMd(_config) => { let doc = graph.compute::>()?; let doc = doc.as_ref(); @@ -137,11 +153,9 @@ impl WorldComputable for ProjectExport { return Ok(None); } - Ok(TypliteMdExport::run(graph)? - .map(String::into_bytes) - .map(Bytes::from)) + Ok(TypliteMdExport::run(graph)?.map(Bytes::from_string)) } - ExportText(config) => Self::export_string::(graph, when, config), + ExportText(config) => Self::export_string::<_, TextExport>(graph, when, config), Query(..) => todo!(), } }; @@ -177,21 +191,3 @@ impl WorldComputable for TypliteMdExport { Self::run(graph) } } - -pub struct TextExport; - -impl ExportComputation for TextExport { - type Output = String; - type Config = ExportTextTask; - - fn run( - _g: &Arc>, - doc: &Arc, - _config: &ExportTextTask, - ) -> Result { - Ok(format!( - "{}", - FullTextDigest(TypstDocument::Paged(doc.clone())) - )) - } -} diff --git a/crates/tinymist/src/task/mod.rs b/crates/tinymist/src/task/mod.rs index 3f89720b..0c9cf8c8 100644 --- a/crates/tinymist/src/task/mod.rs +++ b/crates/tinymist/src/task/mod.rs @@ -16,6 +16,7 @@ use futures::Future; use parking_lot::Mutex; use rayon::Scope; use reflexo::TakeAs; +use tinymist_std::error::prelude::*; /// Please uses this if you believe all mutations are fast #[derive(Clone, Default)] @@ -54,13 +55,13 @@ struct FutureFolder { } impl FutureFolder { - async fn compute<'scope, OP, R: Send + 'static>(op: OP) -> anyhow::Result + async fn compute<'scope, OP, R: Send + 'static>(op: OP) -> Result where OP: FnOnce(&Scope<'scope>) -> R + Send + 'static, { tokio::task::spawn_blocking(move || -> R { rayon::in_place_scope(op) }) .await - .map_err(|e| anyhow::anyhow!("compute error: {e:?}")) + .context_ut("compute error") } #[must_use] diff --git a/crates/tinymist/src/task/user_action.rs b/crates/tinymist/src/task/user_action.rs index df182b2e..b84329a4 100644 --- a/crates/tinymist/src/task/user_action.rs +++ b/crates/tinymist/src/task/user_action.rs @@ -7,7 +7,7 @@ use base64::Engine; use hyper::service::service_fn; use hyper_util::{rt::TokioIo, server::graceful::GracefulShutdown}; use lsp_server::RequestId; -use reflexo_typst::TypstDict; +use reflexo_typst::{TypstDict, TypstPagedDocument}; use serde::{Deserialize, Serialize}; use serde_json::Value as JsonValue; use sync_lsp::{just_future, LspClient, SchedulableResponse}; @@ -165,14 +165,14 @@ async fn trace_main( req_id: RequestId, ) -> ! { typst_timing::enable(); - let res = typst::compile(w); + let res = typst::compile::(w); let diags = match &res.output { Ok(_res) => res.warnings, Err(errors) => errors.clone(), }; let mut writer = std::io::BufWriter::new(Vec::new()); let _ = typst_timing::export_json(&mut writer, |span| { - resolve_span(w, span).unwrap_or_else(|| ("unknown".to_string(), 0)) + resolve_span(w, Span::from_raw(span)).unwrap_or_else(|| ("unknown".to_string(), 0)) }); let timings = writer.into_inner().unwrap(); diff --git a/crates/tinymist/src/tool/mod.rs b/crates/tinymist/src/tool/mod.rs index c55aa2a5..cdac9a0e 100644 --- a/crates/tinymist/src/tool/mod.rs +++ b/crates/tinymist/src/tool/mod.rs @@ -2,7 +2,6 @@ pub mod package; pub mod project; -pub mod text; pub mod word_count; #[cfg(feature = "preview")] diff --git a/crates/tinymist/src/tool/package/init.rs b/crates/tinymist/src/tool/package/init.rs index eaf7c1b5..86019368 100644 --- a/crates/tinymist/src/tool/package/init.rs +++ b/crates/tinymist/src/tool/package/init.rs @@ -10,7 +10,7 @@ use typst::syntax::package::{PackageSpec, TemplateInfo}; use typst::syntax::VirtualPath; use typst::World; -use crate::world::LspWorld; +use crate::project::LspWorld; /// The source of a template. #[derive(Debug, Clone)] diff --git a/crates/tinymist/src/tool/preview.rs b/crates/tinymist/src/tool/preview.rs index 330fa588..a6743786 100644 --- a/crates/tinymist/src/tool/preview.rs +++ b/crates/tinymist/src/tool/preview.rs @@ -16,12 +16,13 @@ use lsp_types::notification::Notification; use lsp_types::Url; use parking_lot::Mutex; use reflexo_typst::debug_loc::SourceSpanOffset; +use reflexo_typst::Bytes; use reflexo_typst::{error::prelude::*, Error}; use serde::Serialize; use serde_json::Value as JsonValue; use sync_lsp::just_ok; use tinymist_assets::TYPST_PREVIEW_HTML; -use tinymist_project::ProjectInsId; +use tinymist_project::{ProjectInsId, WorldProvider}; use tinymist_std::error::IgnoreLogging; use tinymist_std::typst::TypstDocument; use tokio::sync::{mpsc, oneshot}; @@ -77,7 +78,8 @@ impl typst_preview::CompileView for PreviewCompileView { let source_id = world.id_for_path(Path::new(&loc.filepath))?; let source = world.source(source_id).ok()?; - let cursor = source.line_column_to_byte(loc.pos.line, loc.pos.column)?; + let cursor = + source.line_column_to_byte(loc.pos.line as usize, loc.pos.character as usize)?; let node = LinkedNode::new(source.root()).leaf_at_compat(cursor)?; if node.kind() != SyntaxKind::Text { @@ -94,8 +96,8 @@ impl typst_preview::CompileView for PreviewCompileView { let world = &self.snap.world; let Location::Src(src_loc) = loc; - let line = src_loc.pos.line; - let column = src_loc.pos.column; + let line = src_loc.pos.line as usize; + let column = src_loc.pos.character as usize; let doc = self.snap.success_doc(); let Some(doc) = doc.as_ref() else { @@ -280,7 +282,7 @@ impl EditorServer for PreviewProjectHandler { .into_iter() .map(|(path, content)| { // todo: cloning PathBuf -> Arc - (path.into(), Ok(content.as_bytes().into()).into()) + (path.into(), Ok(Bytes::from_string(content)).into()) }) .collect(), ); @@ -843,6 +845,7 @@ fn jump_from_cursor(document: &TypstDocument, source: &Source, cursor: usize) -> positions } + _ => vec![], } } @@ -862,7 +865,12 @@ fn find_in_frame(frame: &Frame, span: Span, min_dis: &mut u64, p: &mut Point) -> return Some(pos); } if glyph.span.0.id() == span.id() { - let dis = glyph.span.0.number().abs_diff(span.number()); + let dis = glyph + .span + .0 + .into_raw() + .get() + .abs_diff(span.into_raw().get()); if dis < *min_dis { *min_dis = dis; *p = pos; diff --git a/crates/tinymist/src/tool/project.rs b/crates/tinymist/src/tool/project.rs index 0c22bb93..f98b931e 100644 --- a/crates/tinymist/src/tool/project.rs +++ b/crates/tinymist/src/tool/project.rs @@ -40,6 +40,43 @@ pub struct GenerateScriptArgs { pub output: Option, } +#[cfg(feature = "preview")] +pub use typst_preview::{PreviewArgs, PreviewMode}; + +/// Project task commands. +#[derive(Debug, Clone, clap::Subcommand)] +#[clap(rename_all = "kebab-case")] +pub enum TaskCommands { + /// Declare a preview task. + #[cfg(feature = "preview")] + Preview(TaskPreviewArgs), +} + +/// Declare an lsp task. +#[derive(Debug, Clone, clap::Parser)] +#[cfg(feature = "preview")] +pub struct TaskPreviewArgs { + /// Argument to identify a project. + #[clap(flatten)] + pub declare: DocNewArgs, + + /// Name a task. + #[clap(long = "task")] + pub name: Option, + + /// When to run the task + #[arg(long = "when")] + pub when: Option, + + /// Preview arguments + #[clap(flatten)] + pub preview: PreviewArgs, + + /// Preview mode + #[clap(long = "preview-mode", default_value = "document", value_name = "MODE")] + pub preview_mode: PreviewMode, +} + #[cfg(feature = "preview")] trait LockFileExt { fn preview(&mut self, doc_id: Id, args: &TaskPreviewArgs) -> Result; @@ -278,6 +315,9 @@ fn shell_build_script(shell: Shell) -> Result { ProjectTask::ExportSvg(..) => { cmd.push("--format=svg"); } + ProjectTask::ExportSvgHtml(..) => { + cmd.push("--format=svg_html"); + } ProjectTask::ExportMd(..) => { cmd.push("--format=md"); } diff --git a/crates/tinymist/src/tool/text.rs b/crates/tinymist/src/tool/text.rs deleted file mode 100644 index ba6461e5..00000000 --- a/crates/tinymist/src/tool/text.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! Text export utilities. - -use core::fmt; -use tinymist_std::typst::TypstDocument; - -/// A full text digest of a document. -pub struct FullTextDigest(pub TypstDocument); - -impl FullTextDigest { - fn export_frame(f: &mut fmt::Formatter<'_>, doc: &typst::layout::Frame) -> fmt::Result { - for (_, item) in doc.items() { - Self::export_item(f, item)?; - } - #[cfg(not(feature = "no-content-hint"))] - { - use std::fmt::Write; - let c = doc.content_hint(); - if c != '\0' { - f.write_char(c)?; - } - } - - Ok(()) - } - - fn export_item(f: &mut fmt::Formatter<'_>, item: &typst::layout::FrameItem) -> fmt::Result { - use typst::layout::FrameItem::*; - match item { - Group(g) => Self::export_frame(f, &g.frame), - Text(t) => f.write_str(t.text.as_str()), - Link(..) | Tag(..) | Shape(..) | Image(..) => Ok(()), - } - } -} - -impl fmt::Display for FullTextDigest { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.0 { - TypstDocument::Paged(paged_doc) => { - for page in paged_doc.pages.iter() { - Self::export_frame(f, &page.frame)?; - } - Ok(()) - } - } - } -} diff --git a/crates/tinymist/src/tool/word_count.rs b/crates/tinymist/src/tool/word_count.rs index dad83deb..c941d2f6 100644 --- a/crates/tinymist/src/tool/word_count.rs +++ b/crates/tinymist/src/tool/word_count.rs @@ -154,6 +154,8 @@ impl SpanMapper { self.frame(&page.frame); } } + // todo: handle html + TypstDocument::Html(..) => {} } } diff --git a/crates/typlite/Cargo.toml b/crates/typlite/Cargo.toml index f5707cd4..101a7b32 100644 --- a/crates/typlite/Cargo.toml +++ b/crates/typlite/Cargo.toml @@ -27,7 +27,7 @@ comemo.workspace = true ecow.workspace = true tinymist-analysis.workspace = true tinymist-std.workspace = true -tinymist-project = { workspace = true, features = ["system"] } +tinymist-project = { workspace = true, features = ["lsp"] } typst.workspace = true typst-svg.workspace = true typst-syntax.workspace = true @@ -37,7 +37,7 @@ insta.workspace = true regex.workspace = true [features] -default = ["cli", "embed-fonts", "no-content-hint"] +default = ["cli", "embed-fonts"] clap = ["dep:clap"] cli = ["clap", "clap/wrap_help"] @@ -49,9 +49,5 @@ cli = ["clap", "clap/wrap_help"] # into the binary. embed-fonts = ["tinymist-project/fonts"] -# Disable the default content hint. -# This requires modifying typst. -no-content-hint = ["tinymist-project/no-content-hint"] - # [lints] # workspace = true diff --git a/crates/typlite/src/lib.rs b/crates/typlite/src/lib.rs index 65fe0666..0ff7b079 100644 --- a/crates/typlite/src/lib.rs +++ b/crates/typlite/src/lib.rs @@ -159,7 +159,7 @@ impl TypliteWorker { RawLang | RawDelim | RawTrimmed => Err("converting clause")?, Math | MathIdent | MathAlignPoint | MathDelimited | MathAttach | MathPrimes - | MathFrac | MathRoot | MathShorthand => Err("converting math node")?, + | MathFrac | MathRoot | MathShorthand | MathText => Err("converting math node")?, // Error nodes Error => Err(node.clone().into_text().to_string())?, @@ -316,6 +316,7 @@ impl TypliteWorker { // Ignored comments LineComment => Ok(Value::None), BlockComment => Ok(Value::None), + Shebang => Ok(Value::None), }; if res.clone()? == Value::None && !matches!( @@ -591,7 +592,7 @@ impl TypliteWorker { #set text(fill: rgb("#c0caf5")) if sys.inputs.at("x-color-theme", default: none) == "dark"; {code}"## ); - let main = Bytes::from(code.as_bytes().to_owned()); + let main = Bytes::new(code.as_bytes().to_owned()); // let world = LiteWorld::new(main); let path = Path::new("__render__.typ"); @@ -738,7 +739,8 @@ impl TypliteWorker { let mut s = EcoString::new(); let raw = node.cast::().unwrap(); - // Raw codes with typlite language will not be treated as a code block but directly output into the Markdown result. + // Raw codes with typlite language will not be treated as a code block but + // directly output into the Markdown result. if let Some(lang) = raw.lang() { if &EcoString::from("typlite") == lang.get() { for line in raw.lines() { diff --git a/crates/typlite/src/tests.rs b/crates/typlite/src/tests.rs index 9ea2776c..255446f0 100644 --- a/crates/typlite/src/tests.rs +++ b/crates/typlite/src/tests.rs @@ -5,7 +5,7 @@ use std::sync::{LazyLock, OnceLock}; use regex::Regex; use tinymist_project::{ - font::TinymistFontResolver, CompileFontArgs, EntryState, LspUniverseBuilder, + font::TinymistFontResolver, CompileFontArgs, EntryState, ExportTarget, LspUniverseBuilder, }; use typst_syntax::Source; @@ -23,13 +23,14 @@ fn conv_(s: &str, for_docs: bool) -> EcoString { let main = Source::detached(s); let mut universe = LspUniverseBuilder::build( EntryState::new_rooted_by_id(cwd.as_path().into(), main.id()), + ExportTarget::Paged, Default::default(), FONT_RESOLVER.clone(), Default::default(), ); let main_id = universe.main_id().unwrap(); universe - .map_shadow_by_id(main_id, Bytes::from(main.text().as_bytes().to_owned())) + .map_shadow_by_id(main_id, Bytes::from_string(main.text().to_owned())) .unwrap(); let world = universe.snapshot(); diff --git a/crates/typst-preview/src/actor/render.rs b/crates/typst-preview/src/actor/render.rs index 78ca002c..45871b81 100644 --- a/crates/typst-preview/src/actor/render.rs +++ b/crates/typst-preview/src/actor/render.rs @@ -2,7 +2,7 @@ use std::ops::Range; use std::sync::Arc; use reflexo_typst::debug_loc::{ - CharPosition, DocumentPosition, ElementPoint, SourceLocation, SourceSpanOffset, + DocumentPosition, ElementPoint, LspPosition, SourceLocation, SourceSpanOffset, }; use reflexo_vec2svg::IncrSvgDocServer; use tinymist_std::typst::TypstDocument; @@ -139,16 +139,14 @@ impl RenderActor { continue; }; - let TypstDocument::Paged(document) = document; - let data = if has_full_render { if let Some(data) = self.renderer.pack_current() { data } else { - self.renderer.pack_delta(document) + self.renderer.pack_delta(&document) } } else { - self.renderer.pack_delta(document) + self.renderer.pack_delta(&document) }; let Ok(_) = self.svg_sender.send(data) else { log::info!("RenderActor: svg_sender is dropped"); @@ -241,9 +239,9 @@ impl RenderActor { .view()? .resolve_source_span(crate::Location::Src(SourceLocation { filepath: req.filepath.to_string_lossy().to_string(), - pos: CharPosition { + pos: LspPosition { line: req.line, - column: req.character, + character: req.character, }, }))?; log::info!("RenderActor: changing cursor position: {span:?}"); @@ -263,9 +261,9 @@ impl RenderActor { .view()? .resolve_document_position(crate::Location::Src(SourceLocation { filepath: req.filepath.to_string_lossy().to_string(), - pos: CharPosition { + pos: LspPosition { line: req.line, - column: req.character, + character: req.character, }, })); diff --git a/crates/typst-preview/src/lib.rs b/crates/typst-preview/src/lib.rs index 27d4d59e..ee858fd1 100644 --- a/crates/typst-preview/src/lib.rs +++ b/crates/typst-preview/src/lib.rs @@ -322,24 +322,24 @@ pub struct DocToSrcJumpInfo { #[derive(Debug, Clone, Deserialize)] pub struct ChangeCursorPositionRequest { filepath: PathBuf, - line: usize, + line: u32, /// fixme: character is 0-based, UTF-16 code unit. /// We treat it as UTF-8 now. - character: usize, + character: u32, } #[derive(Debug, Clone, Deserialize)] pub struct ResolveSourceLocRequest { filepath: PathBuf, - line: usize, + line: u32, /// fixme: character is 0-based, UTF-16 code unit. /// We treat it as UTF-8 now. - character: usize, + character: u32, } impl ResolveSourceLocRequest { pub fn to_byte_offset(&self, src: &typst::syntax::Source) -> Option { - src.line_column_to_byte(self.line, self.character) + src.line_column_to_byte(self.line as usize, self.character as usize) } } @@ -401,6 +401,13 @@ impl CompileWatcher { } pub fn notify_compile(&self, view: Arc) { + log::info!( + "Preview({:?}): received notification: signal({:?}, {:?}), refresh style {:?}", + self.task_id, + view.is_by_entry_update(), + view.is_on_saved(), + self.refresh_style + ); if !view.is_by_entry_update() && (self.refresh_style == RefreshStyle::OnSave && !view.is_on_saved()) { diff --git a/crates/typst-shim/Cargo.toml b/crates/typst-shim/Cargo.toml index 876fb18d..17b183ad 100644 --- a/crates/typst-shim/Cargo.toml +++ b/crates/typst-shim/Cargo.toml @@ -4,18 +4,18 @@ description = "A compatibility layer for Typst release and mainline versions." authors = ["The Typst Project Developers"] version.workspace = true edition.workspace = true -readme.workspace = true license.workspace = true homepage.workspace = true repository.workspace = true rust-version.workspace = true [features] -default = [] +default = ["nightly"] nightly = [] [dependencies] typst-syntax.workspace = true +typst-eval.workspace = true typst.workspace = true cfg-if.workspace = true comemo.workspace = true diff --git a/crates/typst-shim/README.md b/crates/typst-shim/README.md new file mode 100644 index 00000000..aac05651 --- /dev/null +++ b/crates/typst-shim/README.md @@ -0,0 +1,3 @@ +# typst-shim + +A compatibility layer for Typst release and mainline versions. diff --git a/crates/typst-shim/src/nightly/eval.rs b/crates/typst-shim/src/nightly/eval.rs index 0150abad..4c9ba862 100644 --- a/crates/typst-shim/src/nightly/eval.rs +++ b/crates/typst-shim/src/nightly/eval.rs @@ -5,6 +5,8 @@ use typst::engine::{Route, Sink, Traced}; use typst::foundations::Module; use typst::World; +pub use typst_eval::*; + /// Evaluates a source file and return the resulting module. pub fn eval_compat( world: &dyn World, diff --git a/crates/typst-shim/src/stable/eval.rs b/crates/typst-shim/src/stable/eval.rs index 7230f378..4c9ba862 100644 --- a/crates/typst-shim/src/stable/eval.rs +++ b/crates/typst-shim/src/stable/eval.rs @@ -5,7 +5,7 @@ use typst::engine::{Route, Sink, Traced}; use typst::foundations::Module; use typst::World; -pub use typst::eval::*; +pub use typst_eval::*; /// Evaluates a source file and return the resulting module. pub fn eval_compat( @@ -16,7 +16,8 @@ pub fn eval_compat( let traced = Traced::default(); let mut sink = Sink::default(); - typst::eval::eval( + typst_eval::eval( + &typst::ROUTINES, world.track(), traced.track(), sink.track_mut(), diff --git a/editors/neovim/Configuration.md b/editors/neovim/Configuration.md index a239b521..ac30025f 100644 --- a/editors/neovim/Configuration.md +++ b/editors/neovim/Configuration.md @@ -16,6 +16,16 @@ The path pattern to store Typst artifacts, you can use `$root` or `$dir` or `$na - **Type**: `string` +## `exportTarget` + +The target to export the document to. Defaults to `paged`. Note: you can still export pdf when it is set to `html`. This configuration only affects how the language server completes your code. + +- **Type**: `string` +- **Enum**: + - `paged`: The current export target is for PDF, PNG, and SVG export. + - `html`: The current export target is for HTML export. +- **Default**: `"paged"` + ## `exportPdf` The extension can export PDFs of your Typst files. This setting controls whether this feature is enabled and how often it runs. diff --git a/editors/vscode/CHANGELOG.md b/editors/vscode/CHANGELOG.md index 0b51f03d..d84cb884 100644 --- a/editors/vscode/CHANGELOG.md +++ b/editors/vscode/CHANGELOG.md @@ -6,6 +6,12 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how The changelog lines unspecified with authors are all written by the @Myriad-Dreamin. +## v0.12.21 - [2025-02-20] + +Nightly Release at [feat: split tinymist-task (#1277)](https://github.com/Myriad-Dreamin/tinymist/commit/3799db6dd4b3a6504fe295ff74d6e82cc57d16bf), using [ParaN3xus/typst tinymist-nightly-v0.12.21-content-hint](https://github.com/ParaN3xus/typst/tree/tinymist-nightly-v0.12.21-content-hint), a.k.a. [typst/typst 0.13 changelog (#5801)](https://github.com/typst/typst/commit/4a9a5d2716fc91f60734769eb001aef32fe15403). + +**Full Changelog**: https://github.com/Myriad-Dreamin/tinymist/compare/v0.12.19...v0.12.21 + ## v0.12.20 - [2025-02-21] We massively changed the internal world implementation. This unblocks many new features: @@ -122,6 +128,12 @@ For `tinymist.lock` feature, please check the [tinymist.projectResolution = "loc **Full Changelog**: https://github.com/Myriad-Dreamin/tinymist/compare/v0.12.18...v0.12.20 +## v0.12.19 - [2025-02-03] + +Nightly Release at [feat: generate declarative project lock file (#1133)](https://github.com/Myriad-Dreamin/tinymist/commit/bdfc1ed648f040b1c552d43f8ee7c9e9c882544e), using [ParaN3xus/typst tinymist-nightly-v0.12.19-rc2-content-hint](https://github.com/ParaN3xus/typst/tree/tinymist-nightly-v0.12.19-rc2-content-hint), a.k.a. [typst/typst Support first-line-indent for every paragraph (#5768)](https://github.com/typst/typst/commit/85d177897468165b93056947a80086b2f84d815d). + +**Full Changelog**: https://github.com/Myriad-Dreamin/tinymist/compare/v0.12.18...v0.12.19 + ## v0.12.18 - [2025-01-09] We have added maintainers to GitHub since 2025-01-09: diff --git a/editors/vscode/Configuration.md b/editors/vscode/Configuration.md index 1165f907..72e9621e 100644 --- a/editors/vscode/Configuration.md +++ b/editors/vscode/Configuration.md @@ -16,6 +16,16 @@ The path pattern to store Typst artifacts, you can use `$root` or `$dir` or `$na - **Type**: `string` +## `tinymist.exportTarget` + +The target to export the document to. Defaults to `paged`. Note: you can still export pdf when it is set to `html`. This configuration only affects how the language server completes your code. + +- **Type**: `string` +- **Enum**: + - `paged`: The current export target is for PDF, PNG, and SVG export. + - `html`: The current export target is for HTML export. +- **Default**: `"paged"` + ## `tinymist.exportPdf` The extension can export PDFs of your Typst files. This setting controls whether this feature is enabled and how often it runs. diff --git a/editors/vscode/package.json b/editors/vscode/package.json index 8196f495..1989b048 100644 --- a/editors/vscode/package.json +++ b/editors/vscode/package.json @@ -1,6 +1,6 @@ { "name": "tinymist", - "version": "0.12.20", + "version": "0.12.21", "description": "An integrated language service for Typst", "keywords": [ "typst", @@ -295,6 +295,20 @@ "type": "string", "default": "" }, + "tinymist.exportTarget": { + "title": "Export target", + "description": "The target to export the document to. Defaults to `paged`. Note: you can still export pdf when it is set to `html`. This configuration only affects how the language server completes your code.", + "type": "string", + "default": "paged", + "enum": [ + "paged", + "html" + ], + "enumDescriptions": [ + "The current export target is for PDF, PNG, and SVG export.", + "The current export target is for HTML export." + ] + }, "tinymist.exportPdf": { "title": "Export PDF", "description": "The extension can export PDFs of your Typst files. This setting controls whether this feature is enabled and how often it runs.", diff --git a/editors/vscode/src/extension.ts b/editors/vscode/src/extension.ts index bad75ea6..e3721db0 100644 --- a/editors/vscode/src/extension.ts +++ b/editors/vscode/src/extension.ts @@ -237,7 +237,7 @@ async function openExternal(target: string): Promise { } async function commandExport( - mode: "Pdf" | "Svg" | "Png", + mode: "Pdf" | "Html" | "Svg" | "Png", extraOpts?: any, ): Promise { const activeEditor = window.activeTextEditor; @@ -298,7 +298,7 @@ async function commandCopyAnsiHighlight(): Promise { * Implements the functionality for the 'Show PDF' button shown in the editor title * if a `.typ` file is opened. */ -async function commandShow(kind: "Pdf" | "Svg" | "Png", extraOpts?: any): Promise { +async function commandShow(kind: "Pdf" | "Html" | "Svg" | "Png", extraOpts?: any): Promise { const activeEditor = window.activeTextEditor; if (activeEditor === undefined) { return; @@ -541,6 +541,10 @@ async function commandRunCodeLens(...args: string[]): Promise { void vscode.commands.executeCommand(`typst-preview.preview`); return; } + case "export-html": { + await commandShow("Html"); + break; + } case "export-pdf": { await commandShow("Pdf"); return; diff --git a/syntaxes/textmate/package.json b/syntaxes/textmate/package.json index 9815c558..e5525561 100644 --- a/syntaxes/textmate/package.json +++ b/syntaxes/textmate/package.json @@ -1,6 +1,6 @@ { "name": "typst-textmate", - "version": "0.12.20", + "version": "0.12.21", "private": true, "scripts": { "compile": "npx tsc && node ./dist/main.mjs", diff --git a/tests/e2e/main.rs b/tests/e2e/main.rs index ae9a0164..39bd5914 100644 --- a/tests/e2e/main.rs +++ b/tests/e2e/main.rs @@ -374,7 +374,7 @@ fn e2e() { }); let hash = replay_log(&tinymist_binary, &root.join("neovim")); - insta::assert_snapshot!(hash, @"siphash128_13:b3c34687da7a40cef9adc4f56a1f4ba8"); + insta::assert_snapshot!(hash, @"siphash128_13:4eac03a63490eece9188d477f427de10"); } { @@ -385,7 +385,7 @@ fn e2e() { }); let hash = replay_log(&tinymist_binary, &root.join("vscode")); - insta::assert_snapshot!(hash, @"siphash128_13:28c1e597e2082585f5042d8ca3eb1a88"); + insta::assert_snapshot!(hash, @"siphash128_13:5b6780811ad069bada68fe0b170867e8"); } }