mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-12-23 08:47:50 +00:00
feat: add command to profile the entire server (#1438)
* feat: declare and bind tinymist.profileServer command * feat: editor bridge with the frontend * feat: start and stop server profiling * feat: add profile-server prototype (#1440) * Add profile-server prototype * fix: use branch --------- Co-authored-by: Myriad-Dreamin <camiyoru@gmail.com> * feat: make it good * build: update cargo.lock * dev: ls profile impl and hook * test: update snapshot --------- Co-authored-by: Derived Cat <hooyuser@outlook.com>
This commit is contained in:
parent
890ecd93a5
commit
d6d3766b6f
43 changed files with 858 additions and 298 deletions
301
Cargo.lock
generated
301
Cargo.lock
generated
|
|
@ -23,7 +23,7 @@ version = "0.7.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
|
||||
dependencies = [
|
||||
"getrandom 0.2.15",
|
||||
"getrandom 0.2.16",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
|
@ -185,7 +185,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -208,9 +208,9 @@ checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973"
|
|||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.74"
|
||||
version = "0.3.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
||||
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
|
|
@ -346,9 +346,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.22.0"
|
||||
version = "1.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540"
|
||||
checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
|
|
@ -408,9 +408,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.19"
|
||||
version = "1.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362"
|
||||
checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
|
@ -447,9 +447,9 @@ checksum = "7588475145507237ded760e52bf2f1085495245502033756d28ea72ade0e498b"
|
|||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.40"
|
||||
version = "0.4.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
|
||||
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
|
|
@ -499,9 +499,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.36"
|
||||
version = "4.5.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04"
|
||||
checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
|
|
@ -509,9 +509,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.36"
|
||||
version = "4.5.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5"
|
||||
checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
|
@ -524,9 +524,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.5.47"
|
||||
version = "4.5.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c06f5378ea264ad4f82bbc826628b5aad714a75abf6ece087e923010eb937fb6"
|
||||
checksum = "c91d3baa3bcd889d60e6ef28874126a0b384fd225ab83aa6d8a801c519194ce1"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
|
|
@ -560,7 +560,7 @@ dependencies = [
|
|||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -596,7 +596,7 @@ checksum = "01be201bdfedabd81a11363ab87c1bc92baedf90039437bede5eee0ea9ece8d5"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -653,7 +653,7 @@ checksum = "c8936e42f9b4f5bdfaf23700609ac1f11cb03ad4c1ec128a4ee4fd0903e228db"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -881,7 +881,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -892,7 +892,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
|
|||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1006,7 +1006,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1031,7 +1031,7 @@ checksum = "9556bc800956545d6420a640173e5ba7dfa82f38d3ea5a167eb555bc69ac3323"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1055,9 +1055,9 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
|
|||
|
||||
[[package]]
|
||||
name = "ecow"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef5eeffa816451f3a6a4cce9cd796e3e5ba6018638d3ce5cc7f87b73bababf60"
|
||||
checksum = "b92b481eb5d59fd8e80e92ff11d057d1ca8d144b2cd8c66cc8d5bd177a3c0dc5"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
|
@ -1121,7 +1121,7 @@ checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1337,7 +1337,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1391,9 +1391,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
|
|
@ -1447,9 +1447,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.4.9"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633"
|
||||
checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
|
|
@ -1494,9 +1494,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
version = "0.15.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
|
||||
dependencies = [
|
||||
"foldhash",
|
||||
]
|
||||
|
|
@ -1539,9 +1539,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e"
|
||||
checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
|
|
@ -1631,7 +1631,7 @@ dependencies = [
|
|||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
"webpki-roots",
|
||||
"webpki-roots 0.26.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1845,7 +1845,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1954,7 +1954,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.15.2",
|
||||
"hashbrown 0.15.3",
|
||||
"serde",
|
||||
]
|
||||
|
||||
|
|
@ -1980,15 +1980,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.42.2"
|
||||
version = "1.43.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50259abbaa67d11d2bcafc7ba1d094ed7a0c70e3ce893f0d0997f73558cb3084"
|
||||
checksum = "154934ea70c58054b556dd430b99a98c2a7ff5309ac9891597e339b5c28f4371"
|
||||
dependencies = [
|
||||
"console",
|
||||
"globset",
|
||||
"linked-hash-map",
|
||||
"once_cell",
|
||||
"pin-project",
|
||||
"similar",
|
||||
"walkdir",
|
||||
]
|
||||
|
|
@ -2070,9 +2068,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
|||
|
||||
[[package]]
|
||||
name = "jiff"
|
||||
version = "0.2.8"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5ad87c89110f55e4cd4dc2893a9790820206729eaf221555f742d540b0724a0"
|
||||
checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806"
|
||||
dependencies = [
|
||||
"jiff-static",
|
||||
"log",
|
||||
|
|
@ -2083,13 +2081,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "jiff-static"
|
||||
version = "0.2.8"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d076d5b64a7e2fe6f0743f02c43ca4a6725c0f904203bfe276a5b3e793103605"
|
||||
checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2113,9 +2111,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "kqueue"
|
||||
version = "1.0.8"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c"
|
||||
checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a"
|
||||
dependencies = [
|
||||
"kqueue-sys",
|
||||
"libc",
|
||||
|
|
@ -2133,9 +2131,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "kurbo"
|
||||
version = "0.11.1"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89234b2cc610a7dd927ebde6b41dd1a5d4214cffaef4cf1fb2195d592f92518f"
|
||||
checksum = "1077d333efea6170d9ccb96d3c3026f300ca0773da4938cc4c811daa6df68b0c"
|
||||
dependencies = [
|
||||
"arrayvec 0.7.6",
|
||||
"smallvec",
|
||||
|
|
@ -2161,9 +2159,9 @@ checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
|||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.11"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
|
||||
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
|
|
@ -2279,9 +2277,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "mintex"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bec4598fddb13cc7b528819e697852653252b760f1228b7642679bf2ff2cd07"
|
||||
checksum = "c505b3e17ed6b70a7ed2e67fbb2c560ee327353556120d6e72f5232b6880d536"
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
|
|
@ -2451,7 +2449,7 @@ dependencies = [
|
|||
"by_address",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2543,7 +2541,7 @@ dependencies = [
|
|||
"phf_shared",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2561,26 +2559,6 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
|
|
@ -2695,7 +2673,7 @@ version = "0.2.21"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"zerocopy 0.8.24",
|
||||
"zerocopy 0.8.25",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2734,9 +2712,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "psm"
|
||||
version = "0.1.25"
|
||||
version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f58e5423e24c18cc840e1c98370b3993c6649cd1678b4d24318bcf0a083cbe88"
|
||||
checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
|
@ -2814,13 +2792,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "quinn-proto"
|
||||
version = "0.11.10"
|
||||
version = "0.11.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc"
|
||||
checksum = "bcbafbbdbb0f638fe3f35f3c56739f77a8a1d070cb25603226c83339b391472b"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"getrandom 0.3.2",
|
||||
"rand 0.9.0",
|
||||
"rand 0.9.1",
|
||||
"ring",
|
||||
"rustc-hash 2.1.1",
|
||||
"rustls",
|
||||
|
|
@ -2834,9 +2812,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "quinn-udp"
|
||||
version = "0.5.11"
|
||||
version = "0.5.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "541d0f57c6ec747a90738a52741d3221f7960e8ac2f0ff4b1a63680e033b4ab5"
|
||||
checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842"
|
||||
dependencies = [
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
|
|
@ -2880,13 +2858,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
|
||||
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.3",
|
||||
"zerocopy 0.8.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2915,7 +2892,7 @@ version = "0.6.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom 0.2.15",
|
||||
"getrandom 0.2.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2949,9 +2926,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.11"
|
||||
version = "0.5.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3"
|
||||
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
]
|
||||
|
|
@ -2962,7 +2939,7 @@ version = "0.4.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
||||
dependencies = [
|
||||
"getrandom 0.2.15",
|
||||
"getrandom 0.2.16",
|
||||
"libredox",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
|
@ -2973,7 +2950,7 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
|
||||
dependencies = [
|
||||
"getrandom 0.2.15",
|
||||
"getrandom 0.2.16",
|
||||
"libredox",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
|
@ -3164,7 +3141,7 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"webpki-roots",
|
||||
"webpki-roots 0.26.11",
|
||||
"windows-registry",
|
||||
]
|
||||
|
||||
|
|
@ -3202,7 +3179,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
|
|||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"getrandom 0.2.15",
|
||||
"getrandom 0.2.16",
|
||||
"libc",
|
||||
"untrusted",
|
||||
"windows-sys 0.52.0",
|
||||
|
|
@ -3321,9 +3298,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.5"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf"
|
||||
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"errno",
|
||||
|
|
@ -3334,9 +3311,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.26"
|
||||
version = "0.23.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0"
|
||||
checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"ring",
|
||||
|
|
@ -3366,9 +3343,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.1"
|
||||
version = "0.103.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03"
|
||||
checksum = "7149975849f1abb3832b246010ef62ccc80d3a76169517ada7188252b9cfb437"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
|
|
@ -3463,7 +3440,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3486,7 +3463,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3537,7 +3514,7 @@ dependencies = [
|
|||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3566,9 +3543,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.8"
|
||||
version = "0.10.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
|
|
@ -3583,9 +3560,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
|||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.2"
|
||||
version = "1.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
|
||||
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
|
@ -3671,9 +3648,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
|||
|
||||
[[package]]
|
||||
name = "stacker"
|
||||
version = "0.1.20"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "601f9201feb9b09c00266478bf459952b9ef9a6b94edb2f21eba14ab681a60a9"
|
||||
checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
|
|
@ -3697,7 +3674,7 @@ version = "0.18.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a3275464d7a9f2d4cac57c89c2ef96a8524dba2864c8d6f82e3980baf136f9b"
|
||||
dependencies = [
|
||||
"hashbrown 0.15.2",
|
||||
"hashbrown 0.15.3",
|
||||
"serde",
|
||||
]
|
||||
|
||||
|
|
@ -3726,7 +3703,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3787,9 +3764,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.100"
|
||||
version = "2.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
|
||||
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -3825,13 +3802,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.1"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3969,7 +3946,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3980,7 +3957,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4149,7 +4126,9 @@ dependencies = [
|
|||
"toml",
|
||||
"triomphe",
|
||||
"typst",
|
||||
"typst-macros",
|
||||
"typst-shim",
|
||||
"typst-timing",
|
||||
"unscanny",
|
||||
]
|
||||
|
||||
|
|
@ -4225,7 +4204,7 @@ name = "tinymist-derive"
|
|||
version = "0.13.12"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4357,7 +4336,9 @@ dependencies = [
|
|||
"typlite",
|
||||
"typst",
|
||||
"typst-assets",
|
||||
"typst-macros",
|
||||
"typst-shim",
|
||||
"typst-timing",
|
||||
"unscanny",
|
||||
"walkdir",
|
||||
"yaml-rust2",
|
||||
|
|
@ -4553,9 +4534,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.44.2"
|
||||
version = "1.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48"
|
||||
checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
|
|
@ -4577,7 +4558,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4604,9 +4585,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.14"
|
||||
version = "0.7.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034"
|
||||
checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
|
|
@ -4618,9 +4599,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.20"
|
||||
version = "0.8.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
|
||||
checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
|
|
@ -4630,26 +4611,33 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.8"
|
||||
version = "0.6.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||
checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.24"
|
||||
version = "0.22.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
|
||||
checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
|
||||
dependencies = [
|
||||
"indexmap 2.9.0",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_write",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_write"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076"
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.5.2"
|
||||
|
|
@ -4696,7 +4684,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4966,7 +4954,7 @@ dependencies = [
|
|||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5016,6 +5004,8 @@ dependencies = [
|
|||
"tokio",
|
||||
"typst",
|
||||
"typst-assets",
|
||||
"typst-macros",
|
||||
"typst-timing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5414,7 +5404,7 @@ dependencies = [
|
|||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
|
|
@ -5449,7 +5439,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
|
@ -5539,9 +5529,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.26.8"
|
||||
version = "0.26.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9"
|
||||
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
|
||||
dependencies = [
|
||||
"webpki-roots 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
|
@ -5604,7 +5603,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5615,7 +5614,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5876,9 +5875,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
|||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.6"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10"
|
||||
checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
|
@ -5987,7 +5986,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
|
@ -6002,11 +6001,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.24"
|
||||
version = "0.8.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
|
||||
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
|
||||
dependencies = [
|
||||
"zerocopy-derive 0.8.24",
|
||||
"zerocopy-derive 0.8.25",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6017,18 +6016,18 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.24"
|
||||
version = "0.8.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be"
|
||||
checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6048,7 +6047,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
|
@ -6090,7 +6089,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ reflexo-vec2svg = { version = "=0.6.0-rc1" }
|
|||
typst = "0.13.1"
|
||||
typst-html = "0.13.1"
|
||||
typst-library = "0.13.1"
|
||||
typst-macros = "0.13.1"
|
||||
typst-timing = "0.13.1"
|
||||
typst-svg = "0.13.1"
|
||||
typst-render = "0.13.1"
|
||||
|
|
@ -266,6 +267,7 @@ extend-exclude = ["/.git", "fixtures"]
|
|||
#
|
||||
# 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.13.10" }
|
||||
typst-macros = { git = "https://github.com/Myriad-Dreamin/typst.git", tag = "tinymist/v0.13.10" }
|
||||
typst-library = { git = "https://github.com/Myriad-Dreamin/typst.git", tag = "tinymist/v0.13.10" }
|
||||
typst-html = { git = "https://github.com/Myriad-Dreamin/typst.git", tag = "tinymist/v0.13.10" }
|
||||
typst-timing = { git = "https://github.com/Myriad-Dreamin/typst.git", tag = "tinymist/v0.13.10" }
|
||||
|
|
|
|||
|
|
@ -199,10 +199,18 @@ impl LspClientRoot {
|
|||
msg_kind: M::get_message_kind(),
|
||||
sender: Arc::downgrade(&_strong),
|
||||
req_queue: Arc::new(Mutex::new(ReqQueue::default())),
|
||||
|
||||
hook: Arc::new(()),
|
||||
};
|
||||
Self { weak, _strong }
|
||||
}
|
||||
|
||||
/// Sets the hook for the language server host.
|
||||
pub fn with_hook(mut self, hook: Arc<dyn LsHook>) -> Self {
|
||||
self.weak.hook = hook;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the weak reference to the language server host.
|
||||
pub fn weak(&self) -> LspClient {
|
||||
self.weak.clone()
|
||||
|
|
@ -221,6 +229,8 @@ pub struct LspClient {
|
|||
pub(crate) msg_kind: MessageKind,
|
||||
pub(crate) sender: Weak<ConnectionTx>,
|
||||
pub(crate) req_queue: Arc<Mutex<ReqQueue>>,
|
||||
|
||||
pub(crate) hook: Arc<dyn LsHook>,
|
||||
}
|
||||
|
||||
impl LspClient {
|
||||
|
|
@ -290,7 +300,7 @@ impl LspClient {
|
|||
/// Registers an client2server request in the request queue.
|
||||
pub fn register_request(&self, method: &str, id: &RequestId, received_at: Instant) {
|
||||
let mut req_queue = self.req_queue.lock();
|
||||
self.start_request(id, method);
|
||||
self.hook.start_request(id, method);
|
||||
req_queue
|
||||
.incoming
|
||||
.register(id.clone(), (method.to_owned(), received_at));
|
||||
|
|
@ -327,7 +337,7 @@ impl LspClient {
|
|||
return;
|
||||
};
|
||||
|
||||
self.stop_request(&id, &method, received_at);
|
||||
self.hook.stop_request(&id, &method, received_at);
|
||||
|
||||
let Some(sender) = self.sender.upgrade() else {
|
||||
log::warn!("failed to send response ({method}, {id}): connection closed");
|
||||
|
|
@ -380,7 +390,19 @@ impl LspClient {
|
|||
}
|
||||
}
|
||||
|
||||
impl LspClient {
|
||||
/// A trait that defines the hook for the language server.
|
||||
pub trait LsHook: fmt::Debug + Send + Sync {
|
||||
/// Starts a request.
|
||||
fn start_request(&self, req_id: &RequestId, method: &str);
|
||||
/// Stops a request.
|
||||
fn stop_request(&self, req_id: &RequestId, method: &str, received_at: Instant);
|
||||
/// Starts a notification.
|
||||
fn start_notification(&self, method: &str);
|
||||
/// Stops a notification.
|
||||
fn stop_notification(&self, method: &str, received_at: Instant, result: LspResult<()>);
|
||||
}
|
||||
|
||||
impl LsHook for () {
|
||||
fn start_request(&self, req_id: &RequestId, method: &str) {
|
||||
log::info!("handling {method} - ({req_id})");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ where
|
|||
|
||||
/// Handles an incoming event.
|
||||
fn on_event(&mut self, received_at: Instant, not: dap::Event) -> anyhow::Result<()> {
|
||||
self.client.start_notification(¬.event);
|
||||
self.client.hook.start_notification(¬.event);
|
||||
let handle = |s,
|
||||
dap::Event {
|
||||
seq: _,
|
||||
|
|
@ -273,7 +273,9 @@ where
|
|||
};
|
||||
|
||||
let result = handler(s, body);
|
||||
self.client.stop_notification(&event, received_at, result);
|
||||
self.client
|
||||
.hook
|
||||
.stop_notification(&event, received_at, result);
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
|
|
|||
|
|
@ -354,7 +354,7 @@ where
|
|||
|
||||
/// Handles an incoming notification.
|
||||
fn on_notification(&mut self, received_at: Instant, not: Notification) -> anyhow::Result<()> {
|
||||
self.client.start_notification(¬.method);
|
||||
self.client.hook.start_notification(¬.method);
|
||||
let handle = |s, Notification { method, params }: Notification| {
|
||||
let Some(handler) = self.notifications.get(method.as_str()) else {
|
||||
log::warn!("unhandled notification: {method}");
|
||||
|
|
@ -362,7 +362,9 @@ where
|
|||
};
|
||||
|
||||
let result = handler(s, params);
|
||||
self.client.stop_notification(&method, received_at, result);
|
||||
self.client
|
||||
.hook
|
||||
.stop_notification(&method, received_at, result);
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
|
|
|||
|
|
@ -35,7 +35,9 @@ tinymist-world.workspace = true
|
|||
toml.workspace = true
|
||||
triomphe.workspace = true
|
||||
typst.workspace = true
|
||||
typst-macros.workspace = true
|
||||
typst-shim.workspace = true
|
||||
typst-timing.workspace = true
|
||||
unscanny.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ pub fn analyze_expr(world: &dyn World, node: &LinkedNode) -> EcoVec<(Value, Opti
|
|||
}
|
||||
|
||||
/// Try to determine a set of possible values for an expression.
|
||||
#[typst_macros::time(span = node.span())]
|
||||
pub fn analyze_expr_(world: &dyn World, node: &SyntaxNode) -> EcoVec<(Value, Option<Styles>)> {
|
||||
let Some(expr) = node.cast::<ast::Expr>() else {
|
||||
return eco_vec![];
|
||||
|
|
@ -51,6 +52,7 @@ pub fn analyze_expr_(world: &dyn World, node: &SyntaxNode) -> EcoVec<(Value, Opt
|
|||
}
|
||||
|
||||
/// Try to load a module from the current source file.
|
||||
#[typst_macros::time(span = source.span())]
|
||||
pub fn analyze_import_(world: &dyn World, source: &SyntaxNode) -> (Option<Value>, Option<Value>) {
|
||||
let source_span = source.span();
|
||||
let Some((source, _)) = analyze_expr_(world, source).into_iter().next() else {
|
||||
|
|
@ -109,6 +111,7 @@ pub struct DynLabel {
|
|||
/// - All labels and descriptions for them, if available
|
||||
/// - A split offset: All labels before this offset belong to nodes, all after
|
||||
/// belong to a bibliography.
|
||||
#[typst_macros::time]
|
||||
pub fn analyze_labels(document: &TypstDocument) -> (Vec<DynLabel>, usize) {
|
||||
let mut output = vec![];
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,9 @@ tinymist-std.workspace = true
|
|||
tinymist-l10n.workspace = true
|
||||
tinymist-lint.workspace = true
|
||||
typst.workspace = true
|
||||
typst-macros.workspace = true
|
||||
typst-shim.workspace = true
|
||||
typst-timing.workspace = true
|
||||
unscanny.workspace = true
|
||||
walkdir.workspace = true
|
||||
yaml-rust2.workspace = true
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ pub struct CallInfo {
|
|||
|
||||
// todo: cache call
|
||||
/// Analyzes a function call.
|
||||
#[typst_macros::time(span = node.span())]
|
||||
pub fn analyze_call(
|
||||
ctx: &mut LocalContext,
|
||||
source: Source,
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ impl CompletionPair<'_, '_, '_> {
|
|||
self.def_completions(defines, parens);
|
||||
}
|
||||
|
||||
#[typst_macros::time]
|
||||
pub fn scope_defs(&mut self) -> Option<Defines> {
|
||||
let mut defines = Defines {
|
||||
types: self.worker.ctx.type_check(&self.cursor.source),
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ impl HasNameRange for Decl {
|
|||
|
||||
// todo: field definition
|
||||
/// Finds the definition of a symbol.
|
||||
#[typst_macros::time(span = syntax.node().span())]
|
||||
pub fn definition(
|
||||
ctx: &Arc<SharedContext>,
|
||||
source: &Source,
|
||||
|
|
|
|||
|
|
@ -779,6 +779,7 @@ impl SharedContext {
|
|||
}
|
||||
|
||||
/// Get the lint result of a source file.
|
||||
#[typst_macros::time(span = source.root().span())]
|
||||
pub(crate) fn lint(self: &Arc<Self>, source: &Source) -> LintInfo {
|
||||
let ei = self.expr_stage(source);
|
||||
let ti = self.type_check(source);
|
||||
|
|
@ -1296,6 +1297,7 @@ fn ceil_char_boundary(text: &str, mut cursor: usize) -> usize {
|
|||
cursor.min(text.len())
|
||||
}
|
||||
|
||||
#[typst_macros::time]
|
||||
#[comemo::memoize]
|
||||
fn analyze_bib(
|
||||
world: Tracked<dyn World + '_>,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use tinymist_world::package::PackageSpec;
|
|||
use super::prelude::*;
|
||||
|
||||
/// Get link expressions from a source.
|
||||
#[typst_macros::time(span = src.root().span())]
|
||||
#[comemo::memoize]
|
||||
pub fn get_link_exprs(src: &Source) -> Arc<LinkInfo> {
|
||||
let root = LinkedNode::new(src.root());
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use crate::ty::BuiltinTy;
|
|||
|
||||
/// With given type information, check the type of a literal expression again by
|
||||
/// touching the possible related nodes.
|
||||
#[typst_macros::time(span = node.span())]
|
||||
pub(crate) fn post_type_check(
|
||||
ctx: Arc<SharedContext>,
|
||||
ti: &TypeInfo,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ use crate::{
|
|||
pub type SemanticTokens = Arc<Vec<SemanticToken>>;
|
||||
|
||||
/// Get the semantic tokens for a source.
|
||||
#[typst_macros::time(span = source.root().span())]
|
||||
pub(crate) fn get_semantic_tokens(ctx: &mut LocalContext, source: &Source) -> SemanticTokens {
|
||||
let mut tokenizer = Tokenizer::new(
|
||||
source.clone(),
|
||||
|
|
|
|||
|
|
@ -30,6 +30,18 @@ pub enum SignatureTarget {
|
|||
Convert(Func),
|
||||
}
|
||||
|
||||
impl SignatureTarget {
|
||||
/// Returns the span of the callee node.
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
SignatureTarget::Def(_, def) => def.decl.span(),
|
||||
SignatureTarget::SyntaxFast(_, span) | SignatureTarget::Syntax(_, span) => *span,
|
||||
SignatureTarget::Runtime(func) | SignatureTarget::Convert(func) => func.span(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[typst_macros::time(span = callee_node.span())]
|
||||
pub(crate) fn analyze_signature(
|
||||
ctx: &Arc<SharedContext>,
|
||||
callee_node: SignatureTarget,
|
||||
|
|
@ -41,6 +53,7 @@ pub(crate) fn analyze_signature(
|
|||
})
|
||||
}
|
||||
|
||||
#[typst_macros::time(span = callee_node.span())]
|
||||
fn analyze_type_signature(
|
||||
ctx: &Arc<SharedContext>,
|
||||
callee_node: &SignatureTarget,
|
||||
|
|
@ -315,6 +328,7 @@ impl BoundChecker for AliasStackChecker<'_, '_> {
|
|||
}
|
||||
}
|
||||
|
||||
#[typst_macros::time(span = callee_node.span())]
|
||||
fn analyze_dyn_signature(
|
||||
ctx: &Arc<SharedContext>,
|
||||
callee_node: &SignatureTarget,
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ pub struct TypeEnv {
|
|||
}
|
||||
|
||||
/// Type checking at the source unit level.
|
||||
#[typst_macros::time(span = ei.source.root().span())]
|
||||
pub(crate) fn type_check(
|
||||
ctx: Arc<SharedContext>,
|
||||
ei: ExprInfo,
|
||||
|
|
@ -228,14 +229,14 @@ impl TypeChecker<'_> {
|
|||
&mut self,
|
||||
sig: &Interned<SigTy>,
|
||||
args: &Interned<SigTy>,
|
||||
withs: Option<&Vec<Interned<SigTy>>>,
|
||||
with: Option<&Vec<Interned<SigTy>>>,
|
||||
) {
|
||||
let call_desc = (sig.clone(), args.clone(), withs.cloned());
|
||||
let call_desc = (sig.clone(), args.clone(), with.cloned());
|
||||
if !self.call_cache.insert(call_desc) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (arg_recv, arg_ins) in sig.matches(args, withs) {
|
||||
for (arg_recv, arg_ins) in sig.matches(args, with) {
|
||||
self.constrain(arg_ins, arg_recv);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ static EMPTY_DOCSTRING: LazyLock<DocString> = LazyLock::new(DocString::default);
|
|||
static EMPTY_VAR_DOC: LazyLock<VarDoc> = LazyLock::new(VarDoc::default);
|
||||
|
||||
impl TypeChecker<'_> {
|
||||
#[typst_macros::time(span = expr.span())]
|
||||
pub(crate) fn check_syntax(&mut self, expr: &Expr) -> Option<Ty> {
|
||||
Some(match expr {
|
||||
Expr::Block(exprs) => self.check_block(exprs),
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ use super::{compute_docstring, def::*, DocCommentMatcher, InterpretMode};
|
|||
|
||||
pub type ExprRoute = FxHashMap<TypstFileId, Option<Arc<LazyHash<LexicalScope>>>>;
|
||||
|
||||
#[typst_macros::time(span = source.root().span())]
|
||||
pub(crate) fn expr_of(
|
||||
ctx: Arc<SharedContext>,
|
||||
source: Source,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ pub struct IndexInfo {
|
|||
pub(crate) identifiers: FxHashSet<Interned<str>>,
|
||||
}
|
||||
|
||||
#[typst_macros::time(span = src.root().span())]
|
||||
#[comemo::memoize]
|
||||
pub fn get_index_info(src: &Source) -> Arc<IndexInfo> {
|
||||
let root = src.root();
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use typst_shim::utils::LazyHash;
|
|||
|
||||
use super::{is_mark, CommentGroupMatcher};
|
||||
|
||||
#[typst_macros::time(span = source.root().span())]
|
||||
pub(crate) fn get_lexical_hierarchy(
|
||||
source: &Source,
|
||||
scope_kind: LexicalScopeKind,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ pub struct ModuleDependency {
|
|||
/// It will scan all the files in the context, using
|
||||
/// [`LocalContext::source_files`], and find the dependencies and dependents
|
||||
/// of each file.
|
||||
#[typst_macros::time]
|
||||
pub fn construct_module_dependencies(
|
||||
ctx: &mut LocalContext,
|
||||
) -> HashMap<TypstFileId, ModuleDependency> {
|
||||
|
|
|
|||
|
|
@ -318,6 +318,22 @@ impl<T, E: std::fmt::Display> IgnoreLogging<T> for Result<T, E> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> IgnoreLogging<T> for Option<T> {
|
||||
fn log_error(self, msg: &str) -> Option<T> {
|
||||
self.or_else(|| {
|
||||
log::error!("{msg}");
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
fn log_error_with(self, f: impl FnOnce() -> String) -> Option<T> {
|
||||
self.or_else(|| {
|
||||
log::error!("{}", f());
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait to add context to a result.
|
||||
pub trait WithContext<T>: Sized {
|
||||
/// Add a context to the result.
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@ use rkyv::{Archive, Deserialize as rDeser, Serialize as rSer};
|
|||
|
||||
use crate::error::prelude::Result;
|
||||
|
||||
pub(crate) type FxBuildHasher = std::hash::BuildHasherDefault<FxHasher>;
|
||||
pub use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
|
||||
pub use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet, FxHasher};
|
||||
// pub type FxIndexSet<K> = indexmap::IndexSet<K, FxHasher>;
|
||||
// pub type FxIndexMap<K, V> = indexmap::IndexMap<K, V, FxHasher>;
|
||||
/// A dashmap that uses the FxHasher as the underlying hasher.
|
||||
|
|
@ -34,7 +33,8 @@ pub type FxDashMap<K, V> = dashmap::DashMap<K, V, FxBuildHasher>;
|
|||
/// > is a probability of 1 in 36,890,000,000,000 of a `StableCrateId`
|
||||
/// > collision.
|
||||
///
|
||||
/// This stores the 16-bytes data in a pair of `u64` instead of single `u128` to avoid heavily affecting `align` of the embedding structs.
|
||||
/// This stores the 16-bytes data in a pair of `u64` instead of single `u128` to
|
||||
/// avoid heavily affecting `align` of the embedding structs.
|
||||
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))]
|
||||
#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
|
||||
|
|
|
|||
|
|
@ -281,6 +281,33 @@ impl fmt::Debug for Resolving {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Resolving {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use WorkspaceResolution::*;
|
||||
let Some(id) = self.id else {
|
||||
return write!(f, "unresolved-path");
|
||||
};
|
||||
|
||||
let path = match WorkspaceResolver::resolve(id) {
|
||||
Ok(Workspace(workspace)) => id.vpath().resolve(&workspace.path()),
|
||||
Ok(UntitledRooted(..)) => Some(id.vpath().as_rootless_path().to_owned()),
|
||||
Ok(Rootless | Package) | Err(_) => None,
|
||||
};
|
||||
|
||||
if let Some(path) = path {
|
||||
write!(f, "{}", path.display())
|
||||
} else {
|
||||
let pkg = id.package();
|
||||
match pkg {
|
||||
Some(pkg) => {
|
||||
write!(f, "{pkg}{}", id.vpath().as_rooted_path().display())
|
||||
}
|
||||
None => write!(f, "{}", id.vpath().as_rooted_path().display()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
|
|
|
|||
|
|
@ -523,7 +523,7 @@ impl ServerState {
|
|||
)
|
||||
.map_err(internal_error)?;
|
||||
|
||||
let task = user_action.trace(TraceParams {
|
||||
let task = user_action.trace_document(TraceParams {
|
||||
compiler_program: self_path,
|
||||
root: root.as_ref().to_owned(),
|
||||
main,
|
||||
|
|
@ -538,6 +538,51 @@ impl ServerState {
|
|||
})
|
||||
}
|
||||
|
||||
/// Start to get the trace data of the server.
|
||||
pub fn start_server_trace(&mut self, _args: Vec<JsonValue>) -> AnySchedulableResponse {
|
||||
let task_cell = &mut self.server_trace;
|
||||
if task_cell
|
||||
.as_ref()
|
||||
.is_some_and(|task| task.stop_tx.is_closed())
|
||||
{
|
||||
*task_cell = None;
|
||||
}
|
||||
|
||||
if task_cell.is_some() {
|
||||
return Err(internal_error("server trace is already started"));
|
||||
}
|
||||
|
||||
let (task, resp) = self.user_action.trace_server();
|
||||
*task_cell = Some(task);
|
||||
|
||||
log::info!("server trace started");
|
||||
|
||||
resp
|
||||
}
|
||||
|
||||
/// Stop getting the trace data of the server.
|
||||
pub fn stop_server_trace(&mut self, _args: Vec<JsonValue>) -> AnySchedulableResponse {
|
||||
let task_cell = &mut self.server_trace;
|
||||
if task_cell
|
||||
.as_ref()
|
||||
.is_some_and(|task| task.stop_tx.is_closed())
|
||||
{
|
||||
log::info!("server trace is dropped");
|
||||
*task_cell = None;
|
||||
}
|
||||
|
||||
let Some(task) = task_cell.take() else {
|
||||
return Err(internal_error("server trace is not started or stopped"));
|
||||
};
|
||||
|
||||
if task.stop_tx.send(()).is_err() {
|
||||
return Err(internal_error("cannot send stop signal to server trace"));
|
||||
}
|
||||
|
||||
log::info!("server trace stopping");
|
||||
just_future(async move { task.resp_rx.await.map_err(internal_error)? })
|
||||
}
|
||||
|
||||
/// Get the metrics of the document.
|
||||
pub fn get_document_metrics(
|
||||
&mut self,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ impl ServerState {
|
|||
|
||||
/// Creates a new source file.
|
||||
pub fn create_source(&mut self, path: ImmutPath, content: String) -> Result<()> {
|
||||
let _scope = typst_timing::TimingScope::new("create_source");
|
||||
log::trace!("create source: {path:?}");
|
||||
self.memory_changes
|
||||
.insert(path.clone(), Source::detached(content.clone()));
|
||||
|
|
@ -39,6 +40,7 @@ impl ServerState {
|
|||
|
||||
/// Removes a source file.
|
||||
pub fn remove_source(&mut self, path: ImmutPath) -> Result<()> {
|
||||
let _scope = typst_timing::TimingScope::new("remove_source");
|
||||
self.memory_changes.remove(&path);
|
||||
log::trace!("remove source: {path:?}");
|
||||
|
||||
|
|
@ -55,6 +57,7 @@ impl ServerState {
|
|||
content: Vec<TextDocumentContentChangeEvent>,
|
||||
position_encoding: PositionEncoding,
|
||||
) -> Result<()> {
|
||||
let _scope = typst_timing::TimingScope::new("edit_source");
|
||||
let source = self
|
||||
.memory_changes
|
||||
.get_mut(&path)
|
||||
|
|
|
|||
|
|
@ -2,21 +2,24 @@
|
|||
|
||||
mod args;
|
||||
|
||||
use core::fmt;
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::sync::LazyLock;
|
||||
use std::sync::{Arc, LazyLock};
|
||||
|
||||
use clap::Parser;
|
||||
use clap_builder::CommandFactory;
|
||||
use clap_complete::generate;
|
||||
use futures::future::MaybeDone;
|
||||
use parking_lot::Mutex;
|
||||
use reflexo::ImmutPath;
|
||||
use reflexo_typst::package::PackageSpec;
|
||||
use sync_ls::transport::{with_stdio_transport, MirrorArgs};
|
||||
use sync_ls::{
|
||||
internal_error, DapBuilder, DapMessage, LspBuilder, LspClientRoot, LspMessage, LspResult,
|
||||
RequestId,
|
||||
internal_error, DapBuilder, DapMessage, GetMessageKind, LsHook, LspBuilder, LspClientRoot,
|
||||
LspMessage, LspResult, Message, RequestId, TConnectionTx,
|
||||
};
|
||||
use tinymist::tool::project::{compile_main, generate_script_main, project_main, task_main};
|
||||
use tinymist::tool::testing::{coverage_main, test_main};
|
||||
|
|
@ -25,10 +28,12 @@ use tinymist::{Config, DapRegularInit, RegularInit, ServerState, SuperInit, User
|
|||
use tinymist_core::LONG_VERSION;
|
||||
use tinymist_project::EntryResolver;
|
||||
use tinymist_query::package::PackageInfo;
|
||||
use tinymist_std::hash::{FxBuildHasher, FxHashMap};
|
||||
use tinymist_std::{bail, error::prelude::*};
|
||||
|
||||
#[cfg(feature = "l10n")]
|
||||
use tinymist_l10n::{load_translations, set_translations};
|
||||
use typst::ecow::EcoString;
|
||||
|
||||
use crate::args::*;
|
||||
|
||||
|
|
@ -140,7 +145,7 @@ pub fn lsp_main(args: LspArgs) -> Result<()> {
|
|||
|
||||
let is_replay = !args.mirror.replay.is_empty();
|
||||
with_stdio_transport::<LspMessage>(args.mirror.clone(), |conn| {
|
||||
let client = LspClientRoot::new(RUNTIMES.tokio_runtime.handle().clone(), conn.sender);
|
||||
let client = client_root(conn.sender);
|
||||
ServerState::install_lsp(LspBuilder::new(
|
||||
RegularInit {
|
||||
client: client.weak().to_typed(),
|
||||
|
|
@ -168,7 +173,7 @@ pub fn dap_main(args: DapArgs) -> Result<()> {
|
|||
|
||||
let is_replay = !args.mirror.replay.is_empty();
|
||||
with_stdio_transport::<DapMessage>(args.mirror.clone(), |conn| {
|
||||
let client = LspClientRoot::new(RUNTIMES.tokio_runtime.handle().clone(), conn.sender);
|
||||
let client = client_root(conn.sender);
|
||||
ServerState::install_dap(DapBuilder::new(
|
||||
DapRegularInit {
|
||||
client: client.weak().to_typed(),
|
||||
|
|
@ -204,7 +209,7 @@ pub fn trace_lsp_main(args: TraceLspArgs) -> Result<()> {
|
|||
}
|
||||
|
||||
with_stdio_transport::<LspMessage>(args.mirror.clone(), |conn| {
|
||||
let client_root = LspClientRoot::new(RUNTIMES.tokio_runtime.handle().clone(), conn.sender);
|
||||
let client_root = client_root(conn.sender);
|
||||
let client = client_root.weak();
|
||||
let roots = vec![ImmutPath::from(root_path)];
|
||||
let config = Config {
|
||||
|
|
@ -265,7 +270,7 @@ pub fn query_main(cmds: QueryCommands) -> Result<()> {
|
|||
use tinymist_project::package::PackageRegistry;
|
||||
|
||||
with_stdio_transport::<LspMessage>(MirrorArgs::default(), |conn| {
|
||||
let client_root = LspClientRoot::new(RUNTIMES.tokio_runtime.handle().clone(), conn.sender);
|
||||
let client_root = client_root(conn.sender);
|
||||
let client = client_root.weak();
|
||||
|
||||
// todo: roots, inputs, font_opts
|
||||
|
|
@ -336,3 +341,66 @@ pub fn query_main(cmds: QueryCommands) -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Creates a new language server host.
|
||||
fn client_root<M: TryFrom<Message, Error = anyhow::Error> + GetMessageKind>(
|
||||
sender: TConnectionTx<M>,
|
||||
) -> LspClientRoot {
|
||||
LspClientRoot::new(RUNTIMES.tokio_runtime.handle().clone(), sender)
|
||||
.with_hook(Arc::new(TypstLsHook::default()))
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct TypstLsHook(Mutex<FxHashMap<RequestId, typst_timing::TimingScope>>);
|
||||
|
||||
impl fmt::Debug for TypstLsHook {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("TypstLsHook").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl LsHook for TypstLsHook {
|
||||
fn start_request(&self, req_id: &RequestId, method: &str) {
|
||||
().start_request(req_id, method);
|
||||
|
||||
if let Some(scope) = typst_timing::TimingScope::new(static_str(method)) {
|
||||
let mut map = self.0.lock();
|
||||
map.insert(req_id.clone(), scope);
|
||||
}
|
||||
}
|
||||
|
||||
fn stop_request(&self, req_id: &RequestId, method: &str, received_at: std::time::Instant) {
|
||||
().stop_request(req_id, method, received_at);
|
||||
|
||||
if let Some(scope) = self.0.lock().remove(req_id) {
|
||||
let _ = scope;
|
||||
}
|
||||
}
|
||||
|
||||
fn start_notification(&self, method: &str) {
|
||||
().start_notification(method);
|
||||
}
|
||||
|
||||
fn stop_notification(
|
||||
&self,
|
||||
method: &str,
|
||||
received_at: std::time::Instant,
|
||||
result: LspResult<()>,
|
||||
) {
|
||||
().stop_notification(method, received_at, result);
|
||||
}
|
||||
}
|
||||
|
||||
fn static_str(s: &str) -> &'static str {
|
||||
static STRS: Mutex<FxHashMap<EcoString, &'static str>> =
|
||||
Mutex::new(HashMap::with_hasher(FxBuildHasher));
|
||||
|
||||
let mut strs = STRS.lock();
|
||||
if let Some(&s) = strs.get(s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
let static_ref: &'static str = String::from(s).leak();
|
||||
strs.insert(static_ref.into(), static_ref);
|
||||
static_ref
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ use crate::project::{
|
|||
ProjectState, PROJECT_ROUTE_USER_ACTION_PRIORITY,
|
||||
};
|
||||
use crate::route::ProjectRouteState;
|
||||
use crate::task::{ExportTask, FormatTask, UserActionTask};
|
||||
use crate::task::{ExportTask, FormatTask, ServerTraceTask, UserActionTask};
|
||||
use crate::world::TaskInputs;
|
||||
use crate::{lsp::init::*, *};
|
||||
|
||||
|
|
@ -82,6 +82,8 @@ pub struct ServerState {
|
|||
pub ever_focusing_by_activities: bool,
|
||||
/// The client ever sent manual focusing request.
|
||||
pub ever_manual_focusing: bool,
|
||||
/// The running server trace.
|
||||
pub server_trace: Option<ServerTraceTask>,
|
||||
|
||||
// Configurations
|
||||
/// User configuration from the editor.
|
||||
|
|
@ -130,6 +132,7 @@ impl ServerState {
|
|||
ever_manual_focusing: false,
|
||||
sema_tokens_registered: false,
|
||||
formatter_registered: false,
|
||||
server_trace: None,
|
||||
config,
|
||||
|
||||
pinning_by_user: false,
|
||||
|
|
@ -277,6 +280,8 @@ impl ServerState {
|
|||
.with_command("tinymist.doGetTemplateEntry", State::get_template_entry)
|
||||
.with_command_("tinymist.interactCodeContext", State::interact_code_context)
|
||||
.with_command("tinymist.getDocumentTrace", State::get_document_trace)
|
||||
.with_command("tinymist.startServerProfiling", State::start_server_trace)
|
||||
.with_command("tinymist.stopServerProfiling", State::stop_server_trace)
|
||||
.with_command_("tinymist.getDocumentMetrics", State::get_document_metrics)
|
||||
.with_command_("tinymist.getWorkspaceLabels", State::get_workspace_labels)
|
||||
.with_command_("tinymist.getServerInfo", State::get_server_info)
|
||||
|
|
|
|||
|
|
@ -1,20 +1,28 @@
|
|||
//! The actor that runs user actions.
|
||||
|
||||
use std::future::Future;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::AtomicU64;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::bail;
|
||||
use base64::Engine;
|
||||
use futures::FutureExt;
|
||||
use hyper::body::Bytes;
|
||||
use hyper::service::service_fn;
|
||||
use hyper_util::{rt::TokioIo, server::graceful::GracefulShutdown};
|
||||
use reflexo_typst::vfs::WorkspaceResolver;
|
||||
use reflexo_typst::{TypstDict, TypstPagedDocument};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value as JsonValue;
|
||||
use sync_ls::{just_future, LspClient, RequestId, SchedulableResponse};
|
||||
use serde_json::{json, Value as JsonValue};
|
||||
use sync_ls::{just_future, LspClient, LspResult, RequestId, SchedulableResponse};
|
||||
use tinymist_std::error::IgnoreLogging;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use typst::{syntax::Span, World};
|
||||
|
||||
use crate::project::LspWorld;
|
||||
use crate::{internal_error, ServerState};
|
||||
use crate::{internal_error, AliveLock, ConnWithCancel, ServerState};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
|
@ -32,8 +40,8 @@ pub struct TraceParams {
|
|||
pub struct UserActionTask;
|
||||
|
||||
impl UserActionTask {
|
||||
/// Run a trace.
|
||||
pub fn trace(&self, params: TraceParams) -> SchedulableResponse<JsonValue> {
|
||||
/// Traces a specific document.
|
||||
pub fn trace_document(&self, params: TraceParams) -> SchedulableResponse<JsonValue> {
|
||||
just_future(async move {
|
||||
run_trace_program(params)
|
||||
.await
|
||||
|
|
@ -41,6 +49,66 @@ impl UserActionTask {
|
|||
})
|
||||
}
|
||||
|
||||
/// Traces the entire server.
|
||||
pub fn trace_server(&self) -> (ServerTraceTask, SchedulableResponse<JsonValue>) {
|
||||
let (stop_tx, mut stop_rx) = mpsc::unbounded_channel();
|
||||
let (resp_tx, resp_rx) = oneshot::channel();
|
||||
let (addr_tx, addr_rx) = oneshot::channel();
|
||||
|
||||
let stop_tx2 = stop_tx.clone();
|
||||
let task = ServerTraceTask { stop_tx, resp_rx };
|
||||
|
||||
typst_timing::enable();
|
||||
// Empty trace array is not legal, so we add a root scope.
|
||||
let _scope = typst_timing::TimingScope::new("server_trace");
|
||||
let timings = async move {
|
||||
log::info!("before generate timings");
|
||||
|
||||
stop_rx.recv().await;
|
||||
drop(_scope);
|
||||
typst_timing::disable();
|
||||
|
||||
let mut writer = std::io::BufWriter::new(Vec::new());
|
||||
let res = typst_timing::export_json(&mut writer, |span| {
|
||||
// todo: resolve line correctly
|
||||
let file_id = Span::from_raw(span).id();
|
||||
(WorkspaceResolver::display(file_id).to_string(), 0)
|
||||
});
|
||||
|
||||
let timings = writer.into_inner().unwrap();
|
||||
log::info!("after generate timings {res:?}");
|
||||
log::info!("timings: {:?}", std::str::from_utf8(&timings));
|
||||
|
||||
resp_tx
|
||||
.send(Ok(json!({})))
|
||||
.ok()
|
||||
.log_error("failed to send response");
|
||||
|
||||
Bytes::from_owner(timings)
|
||||
};
|
||||
|
||||
log::info!("now make http server");
|
||||
let resp = just_future(async move {
|
||||
let static_file_addr = "127.0.0.1:0".to_owned();
|
||||
tokio::spawn(async move {
|
||||
make_http_server(timings, static_file_addr, addr_tx).await;
|
||||
stop_tx2.send(()).ok();
|
||||
});
|
||||
|
||||
let addr = addr_rx.await.map_err(|err| {
|
||||
log::error!("failed to get address of trace server: {err:?}");
|
||||
internal_error("failed to get address of trace server")
|
||||
})?;
|
||||
|
||||
log::info!("trace server has started at {addr}");
|
||||
Ok(serde_json::json!({
|
||||
"tracingUrl": format!("http://{addr}"), // not used
|
||||
}))
|
||||
});
|
||||
|
||||
(task, resp)
|
||||
}
|
||||
|
||||
/// Run a trace request in subprocess.
|
||||
pub async fn trace_main(
|
||||
client: LspClient,
|
||||
|
|
@ -100,7 +168,7 @@ async fn run_trace_program(params: TraceParams) -> anyhow::Result<JsonValue> {
|
|||
|
||||
let stdout = child.stdout.take().expect("stdout missing");
|
||||
|
||||
let (msg_tx, msg_rx) = tokio::sync::oneshot::channel();
|
||||
let (msg_tx, msg_rx) = oneshot::channel();
|
||||
std::thread::spawn(move || {
|
||||
let mut input_chan = std::io::BufReader::new(stdout);
|
||||
let mut has_response = false;
|
||||
|
|
@ -197,10 +265,12 @@ async fn trace_main(
|
|||
});
|
||||
}
|
||||
"http" => {
|
||||
let (addr_tx, addr_rx) = tokio::sync::oneshot::channel();
|
||||
let (addr_tx, addr_rx) = oneshot::channel();
|
||||
let t = tokio::spawn(async move {
|
||||
let static_file_addr = "127.0.0.1:0".to_owned();
|
||||
let timings = async { Bytes::from_owner(timings) };
|
||||
make_http_server(timings, static_file_addr, addr_tx).await;
|
||||
std::process::exit(0);
|
||||
});
|
||||
|
||||
let addr = addr_rx.await.unwrap();
|
||||
|
|
@ -223,26 +293,43 @@ async fn trace_main(
|
|||
std::process::exit(0);
|
||||
}
|
||||
|
||||
/// The server trace task.
|
||||
pub struct ServerTraceTask {
|
||||
/// The sender to stop the trace.
|
||||
pub stop_tx: mpsc::UnboundedSender<()>,
|
||||
/// The receiver to get the trace result.
|
||||
pub resp_rx: oneshot::Receiver<LspResult<JsonValue>>,
|
||||
}
|
||||
|
||||
// todo: reuse code from tools preview
|
||||
/// Create a http server for the trace program.
|
||||
pub async fn make_http_server(
|
||||
timings: Vec<u8>,
|
||||
async fn make_http_server(
|
||||
timings: impl Future<Output = Bytes> + Send + Sync + 'static,
|
||||
static_file_addr: String,
|
||||
addr_tx: tokio::sync::oneshot::Sender<std::net::SocketAddr>,
|
||||
) -> ! {
|
||||
addr_tx: oneshot::Sender<std::net::SocketAddr>,
|
||||
) {
|
||||
use http_body_util::Full;
|
||||
use hyper::body::{Bytes, Incoming};
|
||||
type Server = hyper_util::server::conn::auto::Builder<hyper_util::rt::TokioExecutor>;
|
||||
|
||||
let alive_cnt = Arc::<AtomicU64>::default();
|
||||
|
||||
let (alive_tx, mut alive_rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let timings = hyper::body::Bytes::from(timings);
|
||||
let make_service = move || {
|
||||
let timings = timings.shared();
|
||||
|
||||
let alive_cnt2 = alive_cnt.clone();
|
||||
let make_service = move |cancel: CancellationToken| {
|
||||
let alive_cnt = alive_cnt2.clone();
|
||||
let timings = timings.clone();
|
||||
let alive_tx = alive_tx.clone();
|
||||
|
||||
service_fn(move |req: hyper::Request<Incoming>| {
|
||||
let cancel = cancel.clone();
|
||||
let alive_cnt = alive_cnt.clone();
|
||||
let timings = timings.clone();
|
||||
let _ = alive_tx.send(());
|
||||
async move {
|
||||
let _alive_cnt = AliveLock::hold(alive_cnt);
|
||||
// Make sure VSCode can connect to this http server but no malicious website a
|
||||
// user might open in a browser. We recognize VSCode by an `Origin` header that
|
||||
// starts with `vscode-webview://`. Malicious websites can (hopefully) not trick
|
||||
|
|
@ -263,6 +350,14 @@ pub async fn make_http_server(
|
|||
let b = hyper::Response::builder()
|
||||
.header(hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN, allowed_origin);
|
||||
if req.uri().path() == "/" {
|
||||
let timings = tokio::select! {
|
||||
_ = cancel.cancelled() => {
|
||||
log::info!("client connection is dropped, exiting loop");
|
||||
anyhow::bail!("client connection is dropped")
|
||||
},
|
||||
timings = timings => timings,
|
||||
};
|
||||
|
||||
let res = if req.method() == hyper::Method::HEAD {
|
||||
b.body(Full::<Bytes>::default()).unwrap()
|
||||
} else {
|
||||
|
|
@ -291,7 +386,7 @@ pub async fn make_http_server(
|
|||
let addr = listener.local_addr().unwrap();
|
||||
log::info!("trace server listening on http://{addr}");
|
||||
|
||||
let (final_tx, final_rx) = tokio::sync::oneshot::channel();
|
||||
let (final_tx, final_rx) = oneshot::channel();
|
||||
|
||||
// the graceful watcher
|
||||
let graceful = hyper_util::server::graceful::GracefulShutdown::new();
|
||||
|
|
@ -305,7 +400,9 @@ pub async fn make_http_server(
|
|||
}
|
||||
};
|
||||
|
||||
let conn = server.serve_connection(TokioIo::new(stream), make_service());
|
||||
let conn = ConnWithCancel::new(stream);
|
||||
let cancel = conn.cancel.clone();
|
||||
let conn = server.serve_connection(TokioIo::new(conn), make_service(cancel));
|
||||
let conn = graceful.watch(conn.into_owned());
|
||||
tokio::spawn(async move {
|
||||
conn.await.log_error("cannot serve http");
|
||||
|
|
@ -350,9 +447,14 @@ pub async fn make_http_server(
|
|||
break;
|
||||
},
|
||||
_ = tokio::time::sleep(reflexo::time::Duration::from_secs(15)) => {
|
||||
log::info!("trace-server: No activity for 15 seconds, shutting down");
|
||||
final_tx.send(()).ok();
|
||||
break;
|
||||
let held = alive_cnt.load(std::sync::atomic::Ordering::SeqCst);
|
||||
if held == 0 {
|
||||
log::info!("trace-server: No activity for 15 seconds, shutting down");
|
||||
final_tx.send(()).ok();
|
||||
break;
|
||||
} else {
|
||||
log::info!("trace-server: still {held} active connections");
|
||||
}
|
||||
},
|
||||
_ = alive_rx.recv() => {
|
||||
log::info!("trace-server: Activity detected, resetting timer");
|
||||
|
|
@ -363,7 +465,6 @@ pub async fn make_http_server(
|
|||
|
||||
addr_tx.send(addr).ok();
|
||||
join.await.unwrap();
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
/// Turns a span into a (file, line) pair.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,13 @@
|
|||
use core::fmt;
|
||||
|
||||
use std::pin::Pin;
|
||||
use std::sync::atomic::AtomicU64;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Derived<T>(pub T);
|
||||
|
||||
|
|
@ -44,6 +52,7 @@ macro_rules! get_arg_or_default {
|
|||
}};
|
||||
}
|
||||
pub(crate) use get_arg_or_default;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
pub fn try_<T>(f: impl FnOnce() -> Option<T>) -> Option<T> {
|
||||
f()
|
||||
|
|
@ -60,3 +69,75 @@ pub fn exit_on_ctrl_c() {
|
|||
std::process::exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct AliveLock(Arc<AtomicU64>);
|
||||
|
||||
impl AliveLock {
|
||||
pub fn hold(cnt: Arc<AtomicU64>) -> Self {
|
||||
let held = cnt.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||
log::info!("alive lock held, count: {held}");
|
||||
Self(cnt.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for AliveLock {
|
||||
fn drop(&mut self) {
|
||||
let cnt = self.0.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
|
||||
log::info!("alive lock dropped, count: {cnt}");
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ConnWithCancel {
|
||||
stream: TcpStream,
|
||||
pub cancel: CancellationToken,
|
||||
}
|
||||
|
||||
impl ConnWithCancel {
|
||||
pub fn new(stream: TcpStream) -> Self {
|
||||
Self {
|
||||
stream,
|
||||
cancel: CancellationToken::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ConnWithCancel {
|
||||
fn drop(&mut self) {
|
||||
self.cancel.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for ConnWithCancel {
|
||||
fn poll_read(
|
||||
self: Pin<&mut Self>,
|
||||
context: &mut Context<'_>,
|
||||
buf: &mut ReadBuf<'_>,
|
||||
) -> Poll<tokio::io::Result<()>> {
|
||||
Pin::new(&mut Pin::into_inner(self).stream).poll_read(context, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for ConnWithCancel {
|
||||
fn poll_write(
|
||||
self: Pin<&mut Self>,
|
||||
context: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<Result<usize, tokio::io::Error>> {
|
||||
Pin::new(&mut Pin::into_inner(self).stream).poll_write(context, buf)
|
||||
}
|
||||
|
||||
fn poll_flush(
|
||||
self: Pin<&mut Self>,
|
||||
context: &mut Context<'_>,
|
||||
) -> Poll<Result<(), tokio::io::Error>> {
|
||||
Pin::new(&mut Pin::into_inner(self).stream).poll_flush(context)
|
||||
}
|
||||
|
||||
fn poll_shutdown(
|
||||
self: Pin<&mut Self>,
|
||||
context: &mut Context<'_>,
|
||||
) -> Poll<Result<(), tokio::io::Error>> {
|
||||
Pin::new(&mut Pin::into_inner(self).stream).poll_shutdown(context)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ rust-version.workspace = true
|
|||
|
||||
[dependencies]
|
||||
typst.workspace = true
|
||||
typst-macros.workspace = true
|
||||
typst-timing.workspace = true
|
||||
tinymist-assets.workspace = true
|
||||
tinymist-std.workspace = true
|
||||
typst-assets.workspace = true
|
||||
|
|
|
|||
|
|
@ -151,15 +151,7 @@ impl RenderActor {
|
|||
continue;
|
||||
};
|
||||
|
||||
let data = if has_full_render {
|
||||
if let Some(data) = self.renderer.pack_current() {
|
||||
data
|
||||
} else {
|
||||
self.renderer.pack_delta(&document)
|
||||
}
|
||||
} else {
|
||||
self.renderer.pack_delta(&document)
|
||||
};
|
||||
let data = self.render(has_full_render, &document);
|
||||
let Ok(_) = self.svg_sender.send(data) else {
|
||||
log::info!("RenderActor: svg_sender is dropped");
|
||||
break;
|
||||
|
|
@ -168,6 +160,28 @@ impl RenderActor {
|
|||
log::info!("RenderActor: exiting")
|
||||
}
|
||||
|
||||
fn render(&mut self, has_full_render: bool, document: &TypstDocument) -> Vec<u8> {
|
||||
if has_full_render {
|
||||
if let Some(data) = self.render_full() {
|
||||
data
|
||||
} else {
|
||||
self.render_delta(document)
|
||||
}
|
||||
} else {
|
||||
self.render_delta(document)
|
||||
}
|
||||
}
|
||||
|
||||
#[typst_macros::time]
|
||||
fn render_full(&mut self) -> Option<Vec<u8>> {
|
||||
self.renderer.pack_current()
|
||||
}
|
||||
|
||||
#[typst_macros::time]
|
||||
fn render_delta(&mut self, document: &TypstDocument) -> Vec<u8> {
|
||||
self.renderer.pack_delta(document)
|
||||
}
|
||||
|
||||
fn view(&self) -> Option<Arc<dyn CompileView>> {
|
||||
self.view.read().clone()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ impl<
|
|||
}
|
||||
Some(svg) = self.svg_receiver.recv() => {
|
||||
log::trace!("WebviewActor: received svg from renderer");
|
||||
let _scope = typst_timing::TimingScope::new("webview_actor_send_svg");
|
||||
self.webview_websocket_conn.send(Message::Binary(svg))
|
||||
.await.log_error("WebViewActor");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ struct OutlineItem {
|
|||
children: Vec<OutlineItem>,
|
||||
}
|
||||
|
||||
#[typst_macros::time]
|
||||
pub fn outline(interner: &mut SpanInternerImpl, document: &TypstDocument) -> Outline {
|
||||
let outline = get_outline(document.introspector());
|
||||
let mut items = Vec::with_capacity(outline.as_ref().map_or(0, Vec::len));
|
||||
|
|
|
|||
|
|
@ -1211,6 +1211,11 @@
|
|||
"title": "Profile coverage of the current test module",
|
||||
"category": "Typst"
|
||||
},
|
||||
{
|
||||
"command": "tinymist.profileServer",
|
||||
"title": "Profile the entire language server",
|
||||
"category": "Typst"
|
||||
},
|
||||
{
|
||||
"command": "tinymist.syncLabel",
|
||||
"title": "%extension.tinymist.command.tinymist.syncLabel%",
|
||||
|
|
|
|||
|
|
@ -570,7 +570,10 @@ async function commandRunCodeLens(...args: string[]): Promise<void> {
|
|||
async function codeLensMore(): Promise<void> {
|
||||
const kBrowsing = "Browsing Preview Documents";
|
||||
const kPreviewIn = "Preview in ..";
|
||||
const moreCodeLens = [{ label: kBrowsing }, { label: kPreviewIn }, ...quickExports] as const;
|
||||
const kProfileServer = "Profile Server";
|
||||
const moreCodeLensOthers_ = [kBrowsing, kPreviewIn, kProfileServer] as const;
|
||||
const moreCodeLensOthers = moreCodeLensOthers_.map((label) => ({ label }));
|
||||
const moreCodeLens = [...moreCodeLensOthers, ...quickExports] as const;
|
||||
|
||||
const moreAction = (await vscode.window.showQuickPick(moreCodeLens, {
|
||||
title: "More Actions",
|
||||
|
|
@ -612,6 +615,10 @@ async function commandRunCodeLens(...args: string[]): Promise<void> {
|
|||
await commandShow(moreAction.exportKind, moreAction.extraOpts);
|
||||
return;
|
||||
}
|
||||
case kProfileServer: {
|
||||
void vscode.commands.executeCommand(`tinymist.profileServer`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,12 @@ const toolDesc: Partial<Record<EditorToolName, ToolDescriptor>> = {
|
|||
description: "Profile Current File",
|
||||
toolId: "tracing",
|
||||
},
|
||||
"profile-server": {
|
||||
command: "tinymist.profileServer",
|
||||
title: "Profiling Server",
|
||||
description: "Profile the Language SErver",
|
||||
toolId: "profile-server",
|
||||
},
|
||||
};
|
||||
|
||||
export function toolActivate(context: IContext) {
|
||||
|
|
@ -64,6 +70,7 @@ export function toolActivate(context: IContext) {
|
|||
export type EditorToolName =
|
||||
| "template-gallery"
|
||||
| "tracing"
|
||||
| "profile-server"
|
||||
| "summary"
|
||||
| "font-view"
|
||||
| "symbol-view"
|
||||
|
|
@ -75,6 +82,7 @@ export async function editorTool(context: ExtensionContext, tool: EditorToolName
|
|||
"font-view": "Font View",
|
||||
"symbol-view": "Symbol View",
|
||||
tracing: "Tracing",
|
||||
"profile-server": "Profile Server",
|
||||
summary: "Summary",
|
||||
docs: `@${opts?.pkg?.namespace}/${opts?.pkg?.name}:${opts?.pkg?.version} (Docs)`,
|
||||
}[tool];
|
||||
|
|
@ -87,7 +95,7 @@ export async function editorTool(context: ExtensionContext, tool: EditorToolName
|
|||
title,
|
||||
{
|
||||
viewColumn: vscode.ViewColumn.Beside,
|
||||
preserveFocus: tool === "summary" || tool === "tracing",
|
||||
preserveFocus: tool === "summary" || tool === "tracing" || tool === "profile-server",
|
||||
}, // Which sides
|
||||
{
|
||||
enableScripts: true,
|
||||
|
|
@ -275,6 +283,16 @@ export async function editorToolAt(
|
|||
await writeFile(path, data as string);
|
||||
break;
|
||||
}
|
||||
case "stopServerProfiling": {
|
||||
console.log("Stopping server profiling...");
|
||||
const traceDataTask = await vscode.commands.executeCommand("tinymist.stopServerProfiling");
|
||||
const traceData = await traceDataTask;
|
||||
if (!disposed) {
|
||||
panel.webview.postMessage({ type: "traceData", data: traceData });
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
console.error("Unknown message type", message.type);
|
||||
break;
|
||||
|
|
@ -326,6 +344,19 @@ export async function editorToolAt(
|
|||
|
||||
break;
|
||||
}
|
||||
case "profile-server": {
|
||||
const profileHookPromise = vscode.commands.executeCommand("tinymist.startServerProfiling");
|
||||
|
||||
// do that after the html is reloaded
|
||||
afterReloadHtml = async () => {
|
||||
const profileHook = await profileHookPromise;
|
||||
if (!disposed) {
|
||||
panel.webview.postMessage({ type: "didStartServerProfiling", data: profileHook });
|
||||
}
|
||||
};
|
||||
|
||||
break;
|
||||
}
|
||||
case "summary": {
|
||||
const fontsExportConfigure = getFontsExportConfigure(context);
|
||||
const fontsExportConfig = JSON.stringify(fontsExportConfigure.data);
|
||||
|
|
|
|||
|
|
@ -377,7 +377,7 @@ fn e2e() {
|
|||
});
|
||||
|
||||
let hash = replay_log(&tinymist_binary, &root.join("neovim"));
|
||||
insta::assert_snapshot!(hash, @"siphash128_13:94308cd99e254c72703bcc3e1386a80");
|
||||
insta::assert_snapshot!(hash, @"siphash128_13:3c4096d8e71eddb2b5a832f0918bdafa");
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -388,7 +388,7 @@ fn e2e() {
|
|||
});
|
||||
|
||||
let hash = replay_log(&tinymist_binary, &root.join("vscode"));
|
||||
insta::assert_snapshot!(hash, @"siphash128_13:bc403d246654d90466ba47cf9057fee2");
|
||||
insta::assert_snapshot!(hash, @"siphash128_13:4a6d0b2d59178ca4e600985e4cef545");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ import {
|
|||
LspMessage,
|
||||
LspNotification,
|
||||
LspResponse,
|
||||
traceData as traceReport,
|
||||
stopServerProfiling,
|
||||
programTrace,
|
||||
serverTrace,
|
||||
} from "../vscode";
|
||||
import { startModal } from "../components/modal";
|
||||
import { base64Decode } from "../utils";
|
||||
|
|
@ -12,6 +14,7 @@ const { div, h2, button, iframe, code, br, span } = van.tags;
|
|||
const ORIGIN = "https://ui.perfetto.dev";
|
||||
|
||||
const openTrace = (arrayBuffer: ArrayBuffer, traceUrl?: string) => {
|
||||
console.log("openTrace", arrayBuffer, traceUrl);
|
||||
let subWindow = document.getElementById("perfetto") as HTMLIFrameElement;
|
||||
subWindow.src = ORIGIN;
|
||||
subWindow.style.display = "block";
|
||||
|
|
@ -51,7 +54,7 @@ const openTrace = (arrayBuffer: ArrayBuffer, traceUrl?: string) => {
|
|||
url: reopenUrl.toString(),
|
||||
},
|
||||
},
|
||||
ORIGIN
|
||||
ORIGIN,
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -60,7 +63,21 @@ const openTrace = (arrayBuffer: ArrayBuffer, traceUrl?: string) => {
|
|||
|
||||
const enc = new TextEncoder();
|
||||
|
||||
export const Tracing = () => {
|
||||
export const extensionArg = <T>(key: string, defaultValue: T): T => {
|
||||
return key.startsWith(":") ? defaultValue : JSON.parse(base64Decode(key));
|
||||
};
|
||||
|
||||
export const extensionState = <T>(key: string, defaultValue: T) => {
|
||||
return van.state<T>(extensionArg(key, defaultValue));
|
||||
};
|
||||
|
||||
const enum TracingStage {
|
||||
CollectingTrace = "Collecting trace",
|
||||
WaitingForServer = "Waiting for server",
|
||||
}
|
||||
|
||||
export const Tracing = (serverLevelProfiling: boolean) => () => {
|
||||
console.log("serverLevelProfiling", serverLevelProfiling);
|
||||
// #tinymist-app.no-wrap
|
||||
document.getElementById("tinymist-app")?.classList.add("no-wrap");
|
||||
|
||||
|
|
@ -74,15 +91,34 @@ export const Tracing = () => {
|
|||
id: "message",
|
||||
style: "flex: auto",
|
||||
},
|
||||
"Collecting trace..."
|
||||
"Collecting trace...",
|
||||
),
|
||||
...(serverLevelProfiling
|
||||
? [
|
||||
button(
|
||||
{
|
||||
class: "tinymist-button",
|
||||
style: "flex: auto",
|
||||
onclick() {
|
||||
stopServerProfiling();
|
||||
},
|
||||
},
|
||||
"Stop server profiling",
|
||||
),
|
||||
]
|
||||
: []),
|
||||
|
||||
button({
|
||||
id: "open-trace",
|
||||
class: "tinymist-button",
|
||||
style: "display: none; flex: auto",
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
let stage = serverLevelProfiling ? TracingStage.WaitingForServer : TracingStage.CollectingTrace;
|
||||
let tracingContent: ArrayBuffer | undefined = undefined;
|
||||
let msg: string | undefined = undefined;
|
||||
|
||||
const since = Date.now();
|
||||
const collecting = setInterval(async () => {
|
||||
const message = document.getElementById("message")!;
|
||||
|
|
@ -92,86 +128,133 @@ export const Tracing = () => {
|
|||
const elapsed = Date.now() - since;
|
||||
const elapsedAlign = (elapsed / 1000).toFixed(1).padStart(5, " ");
|
||||
|
||||
if (traceReport.val) {
|
||||
// console.log(JSON.stringify(traceReport.val));
|
||||
// todo: merge together
|
||||
|
||||
clearInterval(collecting);
|
||||
const openTraceButton = document.getElementById(
|
||||
"open-trace"
|
||||
) as HTMLButtonElement;
|
||||
openTraceButton.style.display = "block";
|
||||
const rep = traceReport.val;
|
||||
if (serverLevelProfiling) {
|
||||
await collectServer();
|
||||
} else {
|
||||
await collectProgram();
|
||||
}
|
||||
|
||||
// find first response
|
||||
const firstResponse = rep.messages.find<LspResponse>(
|
||||
(msg: LspMessage): msg is LspResponse => "id" in msg && msg.id === 0
|
||||
);
|
||||
async function collectServer() {
|
||||
if (stage === TracingStage.WaitingForServer && serverTrace.val) {
|
||||
stage = TracingStage.CollectingTrace;
|
||||
const result = serverTrace.val;
|
||||
(async () => {
|
||||
tracingContent = await fetchResult(result).catch((e) => {
|
||||
msg = `Error: ${e.message}`;
|
||||
return undefined;
|
||||
});
|
||||
})();
|
||||
} else if (tracingContent || msg) {
|
||||
clearInterval(collecting);
|
||||
message.innerText = "";
|
||||
mainWindow.style.display = "none";
|
||||
|
||||
const diagnosticsMessage = rep.messages.find<LspNotification>(
|
||||
(msg: LspMessage): msg is LspNotification =>
|
||||
"method" in msg && msg.method === "tinymistExt/diagnostics"
|
||||
);
|
||||
startModal(
|
||||
div(
|
||||
{ style: "margin: 1em 0" },
|
||||
...((msg?.length || 0) > 0 ? [code(msg), br()] : []),
|
||||
"Run in ",
|
||||
elapsedAlign.trim(),
|
||||
"s.",
|
||||
),
|
||||
);
|
||||
|
||||
let msg: string;
|
||||
let tracingContent: ArrayBuffer | undefined = undefined;
|
||||
|
||||
if (!firstResponse) {
|
||||
msg = "No trace data found";
|
||||
} else if (firstResponse.error) {
|
||||
msg = `Error: ${firstResponse.error.message}`;
|
||||
} else {
|
||||
msg = "";
|
||||
if (firstResponse.result.tracingData) {
|
||||
tracingContent = enc.encode(firstResponse.result.tracingData).buffer;
|
||||
} else if (firstResponse.result.tracingUrl) {
|
||||
const response = await fetch(firstResponse.result.tracingUrl);
|
||||
tracingContent = await response.arrayBuffer();
|
||||
} else {
|
||||
msg = "No trace data or url found in response";
|
||||
if (tracingContent) {
|
||||
openTrace(tracingContent);
|
||||
}
|
||||
}
|
||||
|
||||
if (!firstResponse) {
|
||||
message.innerText = "No response found";
|
||||
return;
|
||||
}
|
||||
|
||||
message.innerText = "";
|
||||
mainWindow.style.display = "none";
|
||||
|
||||
startModal(
|
||||
div(
|
||||
{ style: "margin: 1em 0" },
|
||||
...(msg.length > 0 ? [code(msg), br()] : []),
|
||||
"Run ",
|
||||
diffPath(rep.request.root, rep.request.main),
|
||||
" using ",
|
||||
shortProgram(rep.request.compilerProgram),
|
||||
" in ",
|
||||
elapsedAlign.trim(),
|
||||
"s, with ",
|
||||
code(
|
||||
{
|
||||
title: base64Decode(rep.stderr),
|
||||
style: "text-decoration: underline",
|
||||
},
|
||||
"logging"
|
||||
),
|
||||
".",
|
||||
optionalInputs(rep.request.inputs),
|
||||
optionalFontPaths(rep.request.fontPaths)
|
||||
),
|
||||
diagReport(diagnosticsMessage?.params) as Node
|
||||
);
|
||||
|
||||
if (tracingContent) {
|
||||
openTrace(tracingContent);
|
||||
}
|
||||
|
||||
return;
|
||||
message.innerText = `${stage}... ${elapsedAlign}s`;
|
||||
}
|
||||
|
||||
message.innerText = `Collecting trace... ${elapsedAlign}s`;
|
||||
async function collectProgram() {
|
||||
if (programTrace.val) {
|
||||
// console.log(JSON.stringify(traceReport.val));
|
||||
|
||||
clearInterval(collecting);
|
||||
const openTraceButton = document.getElementById("open-trace") as HTMLButtonElement;
|
||||
openTraceButton.style.display = "block";
|
||||
const rep = programTrace.val;
|
||||
|
||||
// find first response
|
||||
const firstResponse = rep.messages.find<LspResponse>(
|
||||
(msg: LspMessage): msg is LspResponse => "id" in msg && msg.id === 0,
|
||||
);
|
||||
|
||||
const diagnosticsMessage = rep.messages.find<LspNotification>(
|
||||
(msg: LspMessage): msg is LspNotification =>
|
||||
"method" in msg && msg.method === "tinymistExt/diagnostics",
|
||||
);
|
||||
|
||||
if (!firstResponse) {
|
||||
msg = "No trace data found";
|
||||
} else if (firstResponse.error) {
|
||||
msg = `Error: ${firstResponse.error.message}`;
|
||||
} else {
|
||||
msg = "";
|
||||
tracingContent = await fetchResult(firstResponse.result).catch((e) => {
|
||||
msg = `Error: ${e.message}`;
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
if (!firstResponse) {
|
||||
message.innerText = "No response found";
|
||||
return;
|
||||
}
|
||||
|
||||
message.innerText = "";
|
||||
mainWindow.style.display = "none";
|
||||
|
||||
startModal(
|
||||
div(
|
||||
{ style: "margin: 1em 0" },
|
||||
...((msg?.length || 0) > 0 ? [code(msg), br()] : []),
|
||||
"Run ",
|
||||
diffPath(rep.request.root, rep.request.main),
|
||||
" using ",
|
||||
shortProgram(rep.request.compilerProgram),
|
||||
" in ",
|
||||
elapsedAlign.trim(),
|
||||
"s, with ",
|
||||
code(
|
||||
{
|
||||
title: base64Decode(rep.stderr),
|
||||
style: "text-decoration: underline",
|
||||
},
|
||||
"logging",
|
||||
),
|
||||
".",
|
||||
optionalInputs(rep.request.inputs),
|
||||
optionalFontPaths(rep.request.fontPaths),
|
||||
),
|
||||
diagReport(diagnosticsMessage?.params) as Node,
|
||||
);
|
||||
|
||||
if (tracingContent) {
|
||||
openTrace(tracingContent);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
message.innerText = `${stage}... ${elapsedAlign}s`;
|
||||
}
|
||||
|
||||
async function fetchResult(result: any) {
|
||||
if (result.tracingData) {
|
||||
return enc.encode(result.tracingData).buffer;
|
||||
} else if (result.tracingUrl) {
|
||||
const response = await fetch(result.tracingUrl);
|
||||
return await response.arrayBuffer();
|
||||
} else {
|
||||
throw new Error("No trace data or url found in response");
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
|
||||
return div(
|
||||
|
|
@ -180,7 +263,7 @@ export const Tracing = () => {
|
|||
id: "perfetto",
|
||||
style: "display: none; flex: auto; border: none;",
|
||||
// sandbox: "allow-same-origin",
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -192,7 +275,7 @@ function diffPath(root: string, main: string): ChildDom {
|
|||
|
||||
return code(
|
||||
code({ style: "color: #2486b9; text-decoration: underline" }, root),
|
||||
code({ style: "color: #8cc269; text-decoration: underline" }, main)
|
||||
code({ style: "color: #8cc269; text-decoration: underline" }, main),
|
||||
);
|
||||
}
|
||||
function shortProgram(compilerProgram: string): ChildDom {
|
||||
|
|
@ -200,10 +283,7 @@ function shortProgram(compilerProgram: string): ChildDom {
|
|||
if (lastPath) {
|
||||
// trim extension
|
||||
lastPath = lastPath.replace(/\.[^.]*$/, "");
|
||||
return code(
|
||||
{ title: compilerProgram, style: "text-decoration: underline" },
|
||||
lastPath
|
||||
);
|
||||
return code({ title: compilerProgram, style: "text-decoration: underline" }, lastPath);
|
||||
}
|
||||
}
|
||||
function optionalInputs(inputs: any): ChildDom {
|
||||
|
|
@ -238,10 +318,7 @@ function diagReport(diagnostics?: LspNotification["params"]): ChildDom {
|
|||
}
|
||||
|
||||
const pathDiv = div(
|
||||
code(
|
||||
{ style: "text-decoration: underline", title: path },
|
||||
path.split(/[\/\\]/g).pop()
|
||||
)
|
||||
code({ style: "text-decoration: underline", title: path }, path.split(/[\/\\]/g).pop()),
|
||||
);
|
||||
|
||||
const diagPre = div(
|
||||
|
|
@ -261,10 +338,10 @@ function diagReport(diagnostics?: LspNotification["params"]): ChildDom {
|
|||
span(`${d.range.end.line}:${d.range.end.character}`),
|
||||
" ",
|
||||
d.message,
|
||||
"\n"
|
||||
)
|
||||
)
|
||||
)
|
||||
"\n",
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
diagDivs.push(div(pathDiv, diagPre));
|
||||
|
|
@ -273,6 +350,6 @@ function diagReport(diagnostics?: LspNotification["params"]): ChildDom {
|
|||
return div(
|
||||
{ style: "margin-top: 1.5em" },
|
||||
h2({ style: "margin: 0.4em 0" }, "Diagnostics"),
|
||||
...diagDivs
|
||||
...diagDivs,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { setupVscodeChannel } from "./vscode";
|
|||
type PageComponent =
|
||||
| "template-gallery"
|
||||
| "tracing"
|
||||
| "profile-server"
|
||||
| "summary"
|
||||
| "diagnostics"
|
||||
| "symbol-view"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ import { FontView } from "./features/font-view";
|
|||
|
||||
mainHarness({
|
||||
"template-gallery": TemplateGallery,
|
||||
tracing: Tracing,
|
||||
tracing: Tracing(false),
|
||||
"profile-server": Tracing(true),
|
||||
summary: Summary,
|
||||
diagnostics: Diagnostics,
|
||||
"font-view": FontView,
|
||||
|
|
|
|||
|
|
@ -54,7 +54,10 @@ export interface StyleAtCursor {
|
|||
|
||||
// import { traceDataMock } from "./vscode.trace.mock";
|
||||
// export const traceData = van.state<TraceReport | undefined>(traceDataMock);
|
||||
export const traceData = van.state<TraceReport | undefined>(undefined);
|
||||
export const programTrace = van.state<TraceReport | undefined>(undefined);
|
||||
export const serverTrace = van.state<any | undefined>(undefined);
|
||||
|
||||
export const didStartServerProfiling = van.state<boolean>(false);
|
||||
|
||||
export const styleAtCursor = van.state<StyleAtCursor | undefined>(undefined);
|
||||
|
||||
|
|
@ -66,7 +69,11 @@ export function setupVscodeChannel() {
|
|||
window.addEventListener("message", (event: any) => {
|
||||
switch (event.data.type) {
|
||||
case "traceData": {
|
||||
traceData.val = event.data.data;
|
||||
programTrace.val = event.data.data;
|
||||
break;
|
||||
}
|
||||
case "didStartServerProfiling": {
|
||||
serverTrace.val = event.data.data;
|
||||
break;
|
||||
}
|
||||
case "styleAtCursor": {
|
||||
|
|
@ -101,6 +108,11 @@ export function requestRevealPath(path: string) {
|
|||
}
|
||||
}
|
||||
|
||||
export function stopServerProfiling() {
|
||||
if (vscodeAPI?.postMessage) {
|
||||
vscodeAPI.postMessage({ type: "stopServerProfiling" });
|
||||
}
|
||||
}
|
||||
export interface TextEdit {
|
||||
range?: undefined;
|
||||
newText:
|
||||
|
|
@ -135,7 +147,7 @@ export function requestTextEdit(edit: TextEdit) {
|
|||
navigator.clipboard.writeText(
|
||||
typeof edit.newText === "string"
|
||||
? edit.newText
|
||||
: edit.newText.code || edit.newText.rest || ""
|
||||
: edit.newText.code || edit.newText.rest || "",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue