diff --git a/Cargo.lock b/Cargo.lock index c507edd..e07b37c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "anstream" @@ -91,15 +91,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[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", @@ -107,7 +107,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -147,9 +147,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "block-buffer" @@ -168,15 +168,15 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "clap" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" dependencies = [ "clap_builder", "clap_derive", @@ -184,9 +184,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" dependencies = [ "anstream", "anstyle", @@ -196,9 +196,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" dependencies = [ "heck", "proc-macro2", @@ -208,9 +208,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "colorchoice" @@ -293,12 +293,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.11" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -429,21 +429,22 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -452,31 +453,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", @@ -484,67 +465,54 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "idna" version = "1.0.3" @@ -558,14 +526,25 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", ] +[[package]] +name = "io-uring" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] + [[package]] name = "is_ci" version = "1.2.0" @@ -586,9 +565,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "linux-raw-sys" @@ -598,15 +577,15 @@ checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -633,15 +612,15 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "miette" -version = "7.5.0" +version = "7.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a955165f87b37fd1862df2a59547ac542c77ef6d17c666f619d1ad22dd89484" +checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" dependencies = [ "backtrace", "backtrace-ext", @@ -653,15 +632,14 @@ dependencies = [ "supports-unicode", "terminal_size", "textwrap", - "thiserror 1.0.69", "unicode-width 0.1.14", ] [[package]] name = "miette-derive" -version = "7.5.0" +version = "7.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf45bf44ab49be92fd1227a3be6fc6f617f1a337c06af54981048574d8783147" +checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" dependencies = [ "proc-macro2", "quote", @@ -670,22 +648,22 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "wasi", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -711,15 +689,15 @@ checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] name = "owo-colors" -version = "4.2.0" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564" +checksum = "48dd4f4a2c8405440fd0462561f0e5806bd0f77e86f51c761481bdd4018b545e" [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -727,15 +705,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -746,23 +724,23 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" +checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" dependencies = [ "memchr", "miette", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" +checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" dependencies = [ "pest", "pest_generator", @@ -770,9 +748,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" +checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" dependencies = [ "pest", "pest_meta", @@ -783,11 +761,10 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" +checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" dependencies = [ - "once_cell", "pest", "sha2", ] @@ -824,6 +801,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "proc-macro2" version = "1.0.95" @@ -844,11 +830,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.11" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -863,17 +849,17 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" [[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", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys", @@ -937,9 +923,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", @@ -948,33 +934,30 @@ dependencies = [ [[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", ] [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", @@ -1021,9 +1004,9 @@ checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" [[package]] name = "syn" -version = "2.0.100" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -1032,9 +1015,9 @@ 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", @@ -1058,16 +1041,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" dependencies = [ "unicode-linebreak", - "unicode-width 0.2.0", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", + "unicode-width 0.2.1", ] [[package]] @@ -1076,18 +1050,7 @@ version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.12", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "thiserror-impl", ] [[package]] @@ -1103,9 +1066,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", @@ -1113,17 +1076,19 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.2" +version = "1.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +checksum = "1140bb80481756a8cbe10541f37433b459c5aa1e727b4c020fbfebdc25bf3ec4" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", + "slab", "socket2", "tokio-macros", "windows-sys 0.52.0", @@ -1142,9 +1107,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", @@ -1228,9 +1193,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", @@ -1239,18 +1204,18 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", ] [[package]] name = "tx3-lang" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fff1648f3f0a1310dd1a51cc696d02d69119561c6cbb500fb4de96346837392" +checksum = "c0a45723cea8deff15587048a1b4a2fa6d4fe9ac5c0e66ab2e9a4d22b2eb6bbd" dependencies = [ "bincode", "hex", @@ -1258,7 +1223,7 @@ dependencies = [ "pest", "pest_derive", "serde", - "thiserror 2.0.12", + "thiserror", ] [[package]] @@ -1272,7 +1237,7 @@ dependencies = [ "pest", "ropey", "serde_json", - "thiserror 2.0.12", + "thiserror", "tokio", "tower", "tower-lsp", @@ -1312,9 +1277,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-width" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" [[package]] name = "unty" @@ -1334,12 +1299,6 @@ dependencies = [ "serde", ] -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -1366,9 +1325,9 @@ checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "windows-sys" @@ -1376,7 +1335,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1385,7 +1344,16 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.2", ] [[package]] @@ -1394,14 +1362,30 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -1410,42 +1394,84 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -1453,22 +1479,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "write16" -version = "1.0.0" +name = "windows_x86_64_msvc" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", @@ -1478,9 +1504,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", @@ -1509,11 +1535,22 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + [[package]] name = "zerovec" -version = "0.10.4" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" dependencies = [ "yoke", "zerofrom", @@ -1522,9 +1559,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 126b6a3..60776b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ tower-lsp = "0.20.0" tower = { version = "0.4", features = ["util"] } dashmap = "6.1.0" ropey = "1.6.1" -tx3-lang = "0.6.0" +tx3-lang = "0.7.0" pest = "2.7.15" miette = "7.5.0" serde_json = "1.0.140" diff --git a/src/lib.rs b/src/lib.rs index a934493..04e07e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ use tx3_lang::Protocol; mod ast_to_svg; mod cmds; mod server; +mod visitor; #[derive(Error, Debug)] pub enum Error { @@ -63,6 +64,22 @@ pub fn char_index_to_line_col(rope: &Rope, idx: usize) -> (usize, usize) { (line, col) } +pub fn position_to_offset(text: &str, position: Position) -> usize { + let mut offset = 0; + for (line_num, line) in text.lines().enumerate() { + if line_num == position.line as usize { + offset += position.character.min(line.len() as u32) as usize; + break; + } + offset += line.len() + 1; + } + offset +} + +pub fn span_contains(span: &tx3_lang::ast::Span, offset: usize) -> bool { + offset >= span.start && offset < span.end +} + pub fn span_to_lsp_range(rope: &Rope, loc: &tx3_lang::ast::Span) -> Range { let (start_line, start_col) = char_index_to_line_col(rope, loc.start); let (end_line, end_col) = char_index_to_line_col(rope, loc.end); @@ -118,6 +135,168 @@ pub struct Context { } impl Context { + fn is_type_field_reference( + ast: &tx3_lang::ast::Program, + identifier: &str, + offset: usize, + ) -> bool { + for type_def in &ast.types { + if crate::span_contains(&type_def.span, offset) { + for case in &type_def.cases { + for field in &case.fields { + if identifier == field.r#type.to_string() { + return true; + } + } + } + } + } + false + } + fn collect_semantic_tokens( + &self, + ast: &tx3_lang::ast::Program, + rope: &Rope, + ) -> Vec { + const TOKEN_TYPE: u32 = 0; + const TOKEN_PARAMETER: u32 = 1; + const TOKEN_VARIABLE: u32 = 2; + const TOKEN_CLASS: u32 = 3; + const TOKEN_PARTY: u32 = 4; + const TOKEN_POLICY: u32 = 5; + const TOKEN_FUNCTION: u32 = 6; + // const TOKEN_KEYWORD: u32 = 7; + // const TOKEN_PROPERTY: u32 = 8; + + const MOD_DECLARATION: u32 = 1 << 0; + const MOD_DEFINITION: u32 = 1 << 1; + + #[derive(Debug, Clone)] + struct TokenInfo { + range: Range, + token_type: u32, + token_modifiers: u32, + } + + let mut token_infos: Vec = Vec::new(); + let text = rope.to_string(); + + let mut processed_spans = std::collections::HashSet::new(); + + for offset in 0..text.len() { + if let Some(symbol) = crate::visitor::find_symbol_in_program(ast, offset) { + match symbol { + crate::visitor::SymbolAtOffset::Identifier(identifier) => { + // Skip if we've already processed this exact span + let span_key = (identifier.span.start, identifier.span.end); + if processed_spans.contains(&span_key) { + continue; + } + processed_spans.insert(span_key); + + let token_type = if ast + .parties + .iter() + .any(|p| p.name.value == identifier.value) + { + TOKEN_PARTY + } else if ast + .policies + .iter() + .any(|p| p.name.value == identifier.value) + { + TOKEN_POLICY + } else if ast.types.iter().any(|t| t.name.value == identifier.value) { + TOKEN_TYPE + } else if Context::is_type_field_reference(ast, &identifier.value, offset) { + TOKEN_TYPE + } else if ast.assets.iter().any(|a| a.name.value == identifier.value) { + TOKEN_CLASS + } else { + let mut found_type = None; + + for tx in &ast.txs { + if tx.name.value == identifier.value { + found_type = Some(TOKEN_FUNCTION); + break; + } + + if crate::span_contains(&tx.span, offset) { + for param in &tx.parameters.parameters { + if param.name.value == identifier.value { + found_type = Some(TOKEN_PARAMETER); + break; + } + } + } + + if found_type.is_some() { + break; + } + } + found_type.unwrap_or(TOKEN_VARIABLE) + }; + + token_infos.push(TokenInfo { + range: crate::span_to_lsp_range(rope, &identifier.span), + token_type, + token_modifiers: MOD_DECLARATION | MOD_DEFINITION, + }); + } + visitor::SymbolAtOffset::TypeIdentifier(x) => { + // TODO: wait for the introduction of `TypeAnnotation` in AST + + // token_infos.push(TokenInfo { + // range: crate::span_to_lsp_range(rope, &x.span), + // token_type: TOKEN_TYPE, + // token_modifiers: MOD_DECLARATION | MOD_DEFINITION, + // }); + } + } + } + } + token_infos.sort_by(|a, b| match a.range.start.line.cmp(&b.range.start.line) { + std::cmp::Ordering::Equal => a.range.start.character.cmp(&b.range.start.character), + other => other, + }); + + token_infos.dedup_by(|a, b| a.range.start == b.range.start && a.range.end == b.range.end); + + let mut semantic_tokens = Vec::new(); + let mut prev_line = 0; + let mut prev_start = 0; + + for token_info in token_infos { + let line = token_info.range.start.line; + let start = token_info.range.start.character; + let length = token_info.range.end.character.saturating_sub(start); + + if length == 0 { + continue; + } + + let delta_line = line.saturating_sub(prev_line); + let delta_start = if delta_line == 0 { + start.saturating_sub(prev_start) + } else { + start + }; + + semantic_tokens.push(SemanticToken { + delta_line, + delta_start, + length, + token_type: token_info.token_type, + token_modifiers_bitset: token_info.token_modifiers, + }); + + prev_line = line; + prev_start = start; + } + + semantic_tokens + } + pub fn new_for_client(client: Client) -> Self { Self { client, diff --git a/src/server.rs b/src/server.rs index 44feac2..9ba4d4f 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,7 +1,11 @@ use serde_json::Value; use tower_lsp::{jsonrpc::Result, lsp_types::*, LanguageServer}; -use crate::{cmds, span_to_lsp_range, Context}; +use crate::{ + cmds, position_to_offset, span_contains, span_to_lsp_range, + visitor::{find_symbol_in_program, SymbolAtOffset}, + Context, +}; #[tower_lsp::async_trait] impl LanguageServer for Context { @@ -18,6 +22,34 @@ impl LanguageServer for Context { text_document_sync: Some(TextDocumentSyncCapability::Kind( TextDocumentSyncKind::FULL, )), + semantic_tokens_provider: Some( + SemanticTokensServerCapabilities::SemanticTokensOptions( + SemanticTokensOptions { + work_done_progress_options: WorkDoneProgressOptions::default(), + legend: SemanticTokensLegend { + token_types: vec![ + SemanticTokenType::TYPE, + SemanticTokenType::PARAMETER, + SemanticTokenType::VARIABLE, + SemanticTokenType::CLASS, + SemanticTokenType::new("party"), + SemanticTokenType::new("policy"), + SemanticTokenType::FUNCTION, + // SemanticTokenType::KEYWORD, + // SemanticTokenType::PROPERTY, + ], + token_modifiers: vec![ + SemanticTokenModifier::DECLARATION, + SemanticTokenModifier::DEFINITION, + SemanticTokenModifier::READONLY, + SemanticTokenModifier::STATIC, + ], + }, + range: Some(true), + full: Some(SemanticTokensFullOptions::Bool(true)), + }, + ), + ), execute_command_provider: Some(ExecuteCommandOptions { commands: vec!["generate-tir".to_string(), "generate-ast".to_string()], work_done_progress_options: WorkDoneProgressOptions { @@ -44,11 +76,141 @@ impl LanguageServer for Context { Ok(Some(CompletionResponse::Array(vec![]))) } + async fn semantic_tokens_full( + &self, + params: SemanticTokensParams, + ) -> Result> { + let uri = ¶ms.text_document.uri; + let document = self.documents.get(uri); + + if let Some(document) = document { + let text = document.value().to_string(); + let rope = document.value(); + + let ast = match tx3_lang::parsing::parse_string(text.as_str()) { + Ok(ast) => ast, + Err(_) => return Ok(None), + }; + + let tokens = self.collect_semantic_tokens(&ast, rope); + + Ok(Some(SemanticTokensResult::Tokens(SemanticTokens { + result_id: None, + data: tokens, + }))) + } else { + Ok(None) + } + } + + async fn semantic_tokens_range( + &self, + params: SemanticTokensRangeParams, + ) -> Result> { + // TODO: optimize this for the specific range + let full_params = SemanticTokensParams { + text_document: params.text_document, + work_done_progress_params: params.work_done_progress_params, + partial_result_params: params.partial_result_params, + }; + + self.semantic_tokens_full(full_params).await.map(|result| { + result.map(|tokens| match tokens { + SemanticTokensResult::Tokens(t) => SemanticTokensRangeResult::Tokens(t), + SemanticTokensResult::Partial(p) => SemanticTokensRangeResult::Partial(p), + }) + }) + } + async fn goto_definition( &self, - _: GotoDefinitionParams, + params: GotoDefinitionParams, ) -> Result> { - // Return None for now, indicating no definition found + let uri = ¶ms.text_document_position_params.text_document.uri; + let position = params.text_document_position_params.position; + + let document = self.documents.get(uri); + if let Some(document) = document { + let text = document.value().to_string(); + + let ast = match tx3_lang::parsing::parse_string(text.as_str()) { + Ok(ast) => ast, + Err(_) => return Ok(None), + }; + + let offset = position_to_offset(&text, position); + + if let Some(symbol) = find_symbol_in_program(&ast, offset) { + let identifier = match symbol { + SymbolAtOffset::Identifier(x) => x, + SymbolAtOffset::TypeIdentifier(ty) => match ty { + tx3_lang::ast::Type::Custom(x) => x, + _ => return Ok(None), + }, + }; + + for party in &ast.parties { + if party.name.value == identifier.value { + return Ok(Some(GotoDefinitionResponse::Scalar(Location { + uri: uri.clone(), + range: span_to_lsp_range(document.value(), &party.span), + }))); + } + } + + for policy in &ast.policies { + if policy.name.value == identifier.value { + return Ok(Some(GotoDefinitionResponse::Scalar(Location { + uri: uri.clone(), + range: span_to_lsp_range(document.value(), &policy.span), + }))); + } + } + + for tx in &ast.txs { + if span_contains(&tx.span, offset) { + for param in &tx.parameters.parameters { + if param.name.value == identifier.value { + return Ok(Some(GotoDefinitionResponse::Scalar(Location { + uri: uri.clone(), + range: span_to_lsp_range(document.value(), &tx.parameters.span), + }))); + } + } + + for input in &tx.inputs { + if input.name == identifier.value { + return Ok(Some(GotoDefinitionResponse::Scalar(Location { + uri: uri.clone(), + range: span_to_lsp_range(document.value(), &input.span), + }))); + } + } + + for output in &tx.outputs { + if let Some(output_name) = &output.name { + if output_name == &identifier.value { + return Ok(Some(GotoDefinitionResponse::Scalar(Location { + uri: uri.clone(), + range: span_to_lsp_range(document.value(), &output.span), + }))); + } + } + } + + for reference in &tx.references { + if reference.name == identifier.value { + return Ok(Some(GotoDefinitionResponse::Scalar(Location { + uri: uri.clone(), + range: span_to_lsp_range(document.value(), &reference.span), + }))); + } + } + } + } + } + } + Ok(None) } @@ -58,31 +220,165 @@ impl LanguageServer for Context { } async fn hover(&self, params: HoverParams) -> Result> { - // Get the position where the user is hovering + let uri = ¶ms.text_document_position_params.text_document.uri; let position = params.text_document_position_params.position; - // Here you would typically: - // 1. Parse the document to identify the symbol at the hover position - // 2. Look up information about that symbol - // 3. Return a Hover object with the information + let document = self.documents.get(uri); + if let Some(document) = document { + let text = document.value().to_string(); - // For now, let's return a simple example hover - Ok(Some(Hover { - contents: HoverContents::Markup(MarkupContent { - kind: MarkupKind::Markdown, - value: "This is a symbol hover example".to_string(), - }), - range: Some(Range { - start: Position { - line: position.line, - character: position.character, - }, - end: Position { - line: position.line, - character: position.character + 1, - }, - }), - })) + let ast = match tx3_lang::parsing::parse_string(text.as_str()) { + Ok(ast) => ast, + Err(_) => return Ok(None), + }; + + let offset = position_to_offset(&text, position); + + for party in &ast.parties { + if span_contains(&party.span, offset) { + return Ok(Some(Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: format!( + "**Party**: `{}`\n\nA party in the transaction. It can be an address for a script or a wallet.", + party.name.value + ), + }), + range: Some(span_to_lsp_range(document.value(), &party.span)), + })); + } + } + + for policy in &ast.policies { + if span_contains(&policy.span, offset) { + return Ok(Some(Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: format!( + "**Policy**: `{}`\n\nA policy definition.", + policy.name.value + ), + }), + range: Some(span_to_lsp_range(document.value(), &policy.span)), + })); + } + } + + for type_def in &ast.types { + if span_contains(&type_def.span, offset) { + return Ok(Some(Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: format!( + "**Type**: `{}`\n\nA type definition.", + type_def.name.value + ), + }), + range: Some(span_to_lsp_range(document.value(), &type_def.span)), + })); + } + } + + for asset in &ast.assets { + if span_contains(&asset.span, offset) { + return Ok(Some(Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: format!( + "**Asset**: `{}`\n\nAn asset definition.", + asset.name.value + ), + }), + range: Some(span_to_lsp_range(document.value(), &asset.span)), + })); + } + } + + for tx in &ast.txs { + for input in &tx.inputs { + if span_contains(&input.span, offset) { + return Ok(Some(Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: format!("**Input**: `{}`\n\nTransaction input.", input.name), + }), + range: Some(span_to_lsp_range(document.value(), &input.span)), + })); + } + } + + for output in &tx.outputs { + if span_contains(&output.span, offset) { + let default_output = "output".to_string(); + let name = output.name.as_ref().unwrap_or(&default_output); + return Ok(Some(Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: format!("**Output**: `{}`\n\nTransaction output.", name), + }), + range: Some(span_to_lsp_range(document.value(), &output.span)), + })); + } + } + + if span_contains(&tx.parameters.span, offset) { + for param in &tx.parameters.parameters { + return Ok(Some(Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: format!( + "**Parameter**: `{}`\n\n**Type**: `{:?}`", + param.name.value, param.r#type + ), + }), + range: Some(span_to_lsp_range(document.value(), &tx.parameters.span)), + })); + } + } + + if span_contains(&tx.span, offset) { + let mut hover_text = format!("**Transaction**: `{}`\n\n", tx.name.value); + + if !tx.parameters.parameters.is_empty() { + hover_text.push_str("**Parameters**:\n"); + for param in &tx.parameters.parameters { + hover_text.push_str(&format!( + "- `{}`: `{:?}`\n", + param.name.value, param.r#type + )); + } + hover_text.push_str("\n"); + } + + if !tx.inputs.is_empty() { + hover_text.push_str("**Inputs**:\n"); + for input in &tx.inputs { + hover_text.push_str(&format!("- `{}`\n", input.name)); + } + hover_text.push_str("\n"); + } + + if !tx.outputs.is_empty() { + hover_text.push_str("**Outputs**:\n"); + for output in &tx.outputs { + let default_output = "output".to_string(); + let name = output.name.as_ref().unwrap_or(&default_output); + hover_text.push_str(&format!("- `{}`\n", name)); + } + } + + return Ok(Some(Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: hover_text, + }), + range: Some(span_to_lsp_range(document.value(), &tx.span)), + })); + } + } + } + + Ok(None) } // TODO: Add error handling and improve @@ -120,7 +416,7 @@ impl LanguageServer for Context { let ast = ast.unwrap(); for party in ast.parties { symbols.push(make_symbol( - party.name.clone(), + party.name.value.clone(), "Party".to_string(), SymbolKind::OBJECT, span_to_lsp_range(document.value(), &party.span), @@ -130,7 +426,7 @@ impl LanguageServer for Context { for policy in ast.policies { symbols.push(make_symbol( - policy.name.clone(), + policy.name.value.clone(), "Policy".to_string(), SymbolKind::KEY, span_to_lsp_range(document.value(), &policy.span), @@ -142,7 +438,7 @@ impl LanguageServer for Context { let mut children: Vec = Vec::new(); for parameter in tx.parameters.parameters { children.push(make_symbol( - parameter.name.clone(), + parameter.name.value.clone(), format!("Parameter<{:?}>", parameter.r#type), SymbolKind::FIELD, span_to_lsp_range(document.value(), &tx.parameters.span), @@ -171,7 +467,7 @@ impl LanguageServer for Context { } symbols.push(make_symbol( - tx.name.clone(), + tx.name.value.clone(), "Tx".to_string(), SymbolKind::METHOD, span_to_lsp_range(document.value(), &tx.span), diff --git a/src/visitor.rs b/src/visitor.rs new file mode 100644 index 0000000..e9b3bfa --- /dev/null +++ b/src/visitor.rs @@ -0,0 +1,465 @@ +#[derive(Debug)] +pub enum SymbolAtOffset<'a> { + Identifier(&'a tx3_lang::ast::Identifier), + TypeIdentifier(&'a tx3_lang::ast::Type), +} + +pub fn find_symbol_in_program<'a>( + program: &'a tx3_lang::ast::Program, + offset: usize, +) -> Option> { + for tx in &program.txs { + if let Some(sym) = visit_tx_def(tx, offset) { + return Some(sym); + } + } + for asset in &program.assets { + if let Some(sym) = visit_asset_def(asset, offset) { + return Some(sym); + } + } + for ty in &program.types { + if let Some(sym) = visit_type_def(ty, offset) { + return Some(sym); + } + } + for party in &program.parties { + if let Some(sym) = visit_party_def(party, offset) { + return Some(sym); + } + } + for policy in &program.policies { + if let Some(sym) = visit_policy_def(policy, offset) { + return Some(sym); + } + } + None +} + +fn visit_tx_def<'a>(tx: &'a tx3_lang::ast::TxDef, offset: usize) -> Option> { + if in_span(&tx.name.span, offset) { + return Some(SymbolAtOffset::Identifier(&tx.name)); + } + if let Some(sym) = visit_parameter_list(&tx.parameters, offset) { + return Some(sym); + } + for input in &tx.inputs { + if let Some(sym) = visit_input_block(input, offset) { + return Some(sym); + } + } + for output in &tx.outputs { + if let Some(sym) = visit_output_block(output, offset) { + return Some(sym); + } + } + for mint in &tx.mints { + if let Some(sym) = visit_mint_block(mint, offset) { + return Some(sym); + } + } + for ref_block in &tx.references { + if let Some(sym) = visit_reference_block(ref_block, offset) { + return Some(sym); + } + } + for adhoc in &tx.adhoc { + if let Some(sym) = visit_chain_specific_block(adhoc, offset) { + return Some(sym); + } + } + for col in &tx.collateral { + if let Some(sym) = visit_collateral_block(col, offset) { + return Some(sym); + } + } + if let Some(signers) = &tx.signers { + if let Some(sym) = visit_signers_block(signers, offset) { + return Some(sym); + } + } + if let Some(validity) = &tx.validity { + if let Some(sym) = visit_validity_block(validity, offset) { + return Some(sym); + } + } + if let Some(burn) = &tx.burn { + if let Some(sym) = visit_burn_block(burn, offset) { + return Some(sym); + } + } + if let Some(metadata) = &tx.metadata { + if let Some(sym) = visit_metadata_block(metadata, offset) { + return Some(sym); + } + } + None +} + +fn visit_parameter_list<'a>( + params: &'a tx3_lang::ast::ParameterList, + offset: usize, +) -> Option> { + for param in ¶ms.parameters { + if in_span(¶m.name.span, offset) { + return Some(SymbolAtOffset::Identifier(¶m.name)); + } + if let Some(sym) = visit_type(¶m.r#type, offset) { + return Some(sym); + } + } + None +} + +fn visit_type<'a>(ty: &'a tx3_lang::ast::Type, offset: usize) -> Option> { + // TODO - complete for all types + match &ty { + tx3_lang::ast::Type::Custom(id) => visit_identifier(id, offset), + tx3_lang::ast::Type::List(inner) => visit_type(inner, offset), + _ => None, + } +} + +fn visit_identifier<'a>( + id: &'a tx3_lang::ast::Identifier, + offset: usize, +) -> Option> { + if in_span(&id.span, offset) { + Some(SymbolAtOffset::Identifier(id)) + } else { + None + } +} + +fn visit_input_block<'a>( + input: &'a tx3_lang::ast::InputBlock, + offset: usize, +) -> Option> { + for field in &input.fields { + if let Some(sym) = visit_input_block_field(field, offset) { + return Some(sym); + } + } + None +} + +fn visit_input_block_field<'a>( + field: &'a tx3_lang::ast::InputBlockField, + offset: usize, +) -> Option> { + match field { + tx3_lang::ast::InputBlockField::From(addr) => visit_address_expr(addr, offset), + tx3_lang::ast::InputBlockField::DatumIs(ty) => visit_type(ty, offset), + tx3_lang::ast::InputBlockField::MinAmount(expr) => visit_data_expr(expr, offset), + tx3_lang::ast::InputBlockField::Redeemer(expr) => visit_data_expr(expr, offset), + tx3_lang::ast::InputBlockField::Ref(expr) => visit_data_expr(expr, offset), + } +} + +fn visit_output_block<'a>( + output: &'a tx3_lang::ast::OutputBlock, + offset: usize, +) -> Option> { + for field in &output.fields { + if let Some(sym) = visit_output_block_field(field, offset) { + return Some(sym); + } + } + None +} + +fn visit_output_block_field<'a>( + field: &'a tx3_lang::ast::OutputBlockField, + offset: usize, +) -> Option> { + match field { + tx3_lang::ast::OutputBlockField::To(addr) => visit_address_expr(addr, offset), + tx3_lang::ast::OutputBlockField::Amount(expr) => visit_data_expr(expr, offset), + tx3_lang::ast::OutputBlockField::Datum(expr) => visit_data_expr(expr, offset), + } +} + +fn visit_data_expr<'a>( + expr: &'a tx3_lang::ast::DataExpr, + offset: usize, +) -> Option> { + match expr { + tx3_lang::ast::DataExpr::Identifier(id) => visit_identifier(id, offset), + tx3_lang::ast::DataExpr::StructConstructor(sc) => visit_struct_constructor(sc, offset), + tx3_lang::ast::DataExpr::ListConstructor(lc) => { + for el in &lc.elements { + if let Some(sym) = visit_data_expr(el, offset) { + return Some(sym); + } + } + None + } + _ => None, + } +} + +fn visit_struct_constructor<'a>( + sc: &'a tx3_lang::ast::StructConstructor, + offset: usize, +) -> Option> { + if let Some(sym) = visit_identifier(&sc.r#type, offset) { + return Some(sym); + } + visit_variant_case_constructor(&sc.case, offset) +} + +fn visit_variant_case_constructor<'a>( + vc: &'a tx3_lang::ast::VariantCaseConstructor, + offset: usize, +) -> Option> { + if let Some(sym) = visit_identifier(&vc.name, offset) { + return Some(sym); + } + for field in &vc.fields { + if let Some(sym) = visit_record_constructor_field(field, offset) { + return Some(sym); + } + } + if let Some(spread) = &vc.spread { + return visit_data_expr(spread, offset); + } + None +} + +fn visit_record_constructor_field<'a>( + field: &'a tx3_lang::ast::RecordConstructorField, + offset: usize, +) -> Option> { + if let Some(sym) = visit_identifier(&field.name, offset) { + return Some(sym); + } + visit_data_expr(&field.value, offset) +} + +fn visit_reference_block<'a>( + rb: &'a tx3_lang::ast::ReferenceBlock, + offset: usize, +) -> Option> { + visit_data_expr(&rb.r#ref, offset) +} + +fn visit_chain_specific_block<'a>( + _cb: &'a tx3_lang::ast::ChainSpecificBlock, + _offset: usize, +) -> Option> { + None +} + +fn visit_collateral_block<'a>( + cb: &'a tx3_lang::ast::CollateralBlock, + offset: usize, +) -> Option> { + for field in &cb.fields { + match field { + tx3_lang::ast::CollateralBlockField::From(addr) => { + if let Some(sym) = visit_address_expr(addr, offset) { + return Some(sym); + } + } + tx3_lang::ast::CollateralBlockField::MinAmount(expr) => { + if let Some(sym) = visit_data_expr(expr, offset) { + return Some(sym); + } + } + tx3_lang::ast::CollateralBlockField::Ref(expr) => { + if let Some(sym) = visit_data_expr(expr, offset) { + return Some(sym); + } + } + } + } + None +} + +fn visit_signers_block<'a>( + sb: &'a tx3_lang::ast::SignersBlock, + offset: usize, +) -> Option> { + for signer in &sb.signers { + if let Some(sym) = visit_data_expr(signer, offset) { + return Some(sym); + } + } + None +} + +fn visit_validity_block<'a>( + vb: &'a tx3_lang::ast::ValidityBlock, + offset: usize, +) -> Option> { + for field in &vb.fields { + match field { + tx3_lang::ast::ValidityBlockField::SinceSlot(expr) + | tx3_lang::ast::ValidityBlockField::UntilSlot(expr) => { + if let Some(sym) = visit_data_expr(expr, offset) { + return Some(sym); + } + } + } + } + None +} + +fn visit_burn_block<'a>( + bb: &'a tx3_lang::ast::BurnBlock, + offset: usize, +) -> Option> { + for field in &bb.fields { + match field { + tx3_lang::ast::MintBlockField::Amount(expr) => { + if let Some(sym) = visit_data_expr(expr, offset) { + return Some(sym); + } + } + tx3_lang::ast::MintBlockField::Redeemer(expr) => { + if let Some(sym) = visit_data_expr(expr, offset) { + return Some(sym); + } + } + } + } + None +} + +fn visit_metadata_block<'a>( + _mb: &'a tx3_lang::ast::MetadataBlock, + _offset: usize, +) -> Option> { + None +} + +fn visit_mint_block<'a>( + mb: &'a tx3_lang::ast::MintBlock, + offset: usize, +) -> Option> { + for field in &mb.fields { + match field { + tx3_lang::ast::MintBlockField::Amount(expr) => { + if let Some(sym) = visit_data_expr(expr, offset) { + return Some(sym); + } + } + tx3_lang::ast::MintBlockField::Redeemer(expr) => { + if let Some(sym) = visit_data_expr(expr, offset) { + return Some(sym); + } + } + } + } + None +} + +fn visit_asset_def<'a>( + asset: &'a tx3_lang::ast::AssetDef, + offset: usize, +) -> Option> { + if let Some(sym) = visit_data_expr(&asset.policy, offset) { + return Some(sym); + } + if let Some(sym) = visit_data_expr(&asset.asset_name, offset) { + return Some(sym); + } + None +} + +fn visit_type_def<'a>(ty: &'a tx3_lang::ast::TypeDef, offset: usize) -> Option> { + if in_span(&ty.name.span, offset) { + return Some(SymbolAtOffset::Identifier(&ty.name)); + } + for case in &ty.cases { + for field in &case.fields { + // TODO: wait for the introduction of `TypeAnnotation` in AST + + // if in_span(&field.r#type.span, offset) { + // return Some(SymbolAtOffset::TypeIdentifier(&field.r#type)); + // } + } + if let Some(sym) = visit_variant_case(case, offset) { + return Some(sym); + } + } + None +} + +fn visit_variant_case<'a>( + case: &'a tx3_lang::ast::VariantCase, + offset: usize, +) -> Option> { + for field in &case.fields { + if let Some(sym) = visit_record_field(field, offset) { + return Some(sym); + } + } + None +} + +fn visit_record_field<'a>( + field: &'a tx3_lang::ast::RecordField, + offset: usize, +) -> Option> { + if in_span(&field.name.span, offset) { + return Some(SymbolAtOffset::Identifier(&field.name)); + } + visit_type(&field.r#type, offset) +} + +fn visit_party_def<'a>( + party: &'a tx3_lang::ast::PartyDef, + offset: usize, +) -> Option> { + if in_span(&party.span, offset) { + return Some(SymbolAtOffset::Identifier(&party.name)); + } + None +} + +fn visit_policy_def<'a>( + policy: &'a tx3_lang::ast::PolicyDef, + offset: usize, +) -> Option> { + match &policy.value { + tx3_lang::ast::PolicyValue::Constructor(constr) => { + for field in &constr.fields { + if let Some(sym) = visit_policy_field(field, offset) { + return Some(sym); + } + } + } + tx3_lang::ast::PolicyValue::Assign(_) => { + if in_span(&policy.span, offset) { + return Some(SymbolAtOffset::Identifier(&policy.name)); + } + } + } + None +} + +fn visit_policy_field<'a>( + field: &'a tx3_lang::ast::PolicyField, + offset: usize, +) -> Option> { + match field { + tx3_lang::ast::PolicyField::Hash(expr) => visit_data_expr(expr, offset), + tx3_lang::ast::PolicyField::Script(expr) => visit_data_expr(expr, offset), + tx3_lang::ast::PolicyField::Ref(expr) => visit_data_expr(expr, offset), + } +} + +fn visit_address_expr<'a>( + expr: &'a tx3_lang::ast::AddressExpr, + offset: usize, +) -> Option> { + match expr { + tx3_lang::ast::AddressExpr::Identifier(id) => visit_identifier(id, offset), + _ => None, + } +} + +fn in_span(span: &tx3_lang::ast::Span, offset: usize) -> bool { + span.start <= offset && offset < span.end +}