diff --git a/Cargo.lock b/Cargo.lock index a1695ed..69e6756 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + [[package]] name = "anyhow" version = "1.0.102" @@ -20,12 +29,28 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +[[package]] +name = "borrow-or-share" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0b364ead1874514c8c2855ab558056ebfeb775653e7ae45ff72f28f8f3166c" + [[package]] name = "boxcar" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36f64beae40a84da1b4b26ff2761a5b895c12adc41dc25aaee1c4f2bbfe97a6e" +[[package]] +name = "bstr" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "bumpalo" version = "3.20.2" @@ -38,6 +63,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + [[package]] name = "cc" version = "1.2.56" @@ -63,12 +94,51 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crunchy" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "derive_more" version = "2.1.1" @@ -92,6 +162,26 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "either" version = "1.15.0" @@ -110,6 +200,93 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fluent-uri" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc74ac4d8359ae70623506d512209619e5cf8f347124910440dbc221714b328e" +dependencies = [ + "borrow-or-share", + "ref-cast", + "serde", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + [[package]] name = "fxhash" version = "0.2.1" @@ -119,6 +296,30 @@ dependencies = [ "byteorder", ] +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "globset" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + [[package]] name = "half" version = "2.7.1" @@ -130,12 +331,46 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "ignore" +version = "0.4.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "indexmap" version = "2.13.0" @@ -143,7 +378,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.16.1", ] [[package]] @@ -156,7 +391,7 @@ dependencies = [ "libc", "llvm-sys", "once_cell", - "thiserror", + "thiserror 2.0.18", ] [[package]] @@ -179,6 +414,12 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + [[package]] name = "lazy_static" version = "1.5.0" @@ -233,6 +474,22 @@ dependencies = [ "leaf_parser", ] +[[package]] +name = "leaf_lsp" +version = "0.1.0" +dependencies = [ + "boxcar", + "leaf_allocators", + "leaf_assembly", + "leaf_compiler", + "leaf_parser", + "rangemap", + "rust_search", + "scc", + "tokio", + "tower-lsp-server", +] + [[package]] name = "leaf_parser" version = "0.1.0" @@ -249,6 +506,15 @@ version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +[[package]] +name = "libredox" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" +dependencies = [ + "libc", +] + [[package]] name = "llvm-sys" version = "211.0.0" @@ -263,12 +529,69 @@ dependencies = [ "semver", ] +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "ls-types" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73932659f38b27a0145cbcba778739a7d76162f6534329b3ca8866125620996" +dependencies = [ + "bitflags", + "fluent-uri", + "percent-encoding", + "serde", + "serde_json", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + [[package]] name = "peg" version = "0.8.5" @@ -296,6 +619,18 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "132dca9b868d927b35b5dd728167b2dee150eb1ad686008fc71ccb298b776fca" +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + [[package]] name = "proc-macro2" version = "1.0.106" @@ -314,12 +649,100 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rangemap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68" + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + [[package]] name = "regex-lite" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "943f41321c63ef1c92fd763bfe054d2668f7f225a5c29f0105903dc2fc04ba30" +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rust_search" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27d7be20245d289c9dde663f06521de08663d73cbaefc45785aa65d02022378" +dependencies = [ + "dirs", + "ignore", + "num_cpus", + "regex", + "strsim", +] + [[package]] name = "rustc_version" version = "0.4.1" @@ -336,20 +759,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16c7f49c9d5caa3bf4b3106900484b447b9253fe99670ceb81cb6cb5027855e1" [[package]] -name = "scc" -version = "3.6.6" +name = "same-file" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6012e652611b2fdcb557a7b4be8cee00d8be19397c70011906a68aa4dac2fe37" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scc" +version = "3.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bb5ce9efd4a6e7b0f86c2697fe4c1d78d1f4e6d988c54b752d577cafe22fe8" dependencies = [ "saa", "sdd", ] [[package]] -name = "sdd" -version = "4.6.5" +name = "scopeguard" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4becc2f27bd39aafb78a8a1b4e1d6877ed0a6b5bf096722ed538dc028367a9b5" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sdd" +version = "4.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b21a75f5913ab130e4b369fb8693be25f29b983e2ecad4279df9bfa5dd8aaf3e" [[package]] name = "semver" @@ -357,12 +795,73 @@ version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "syn" version = "2.0.117" @@ -374,13 +873,39 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + +[[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", +] + [[package]] name = "thiserror" version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.18", +] + +[[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", ] [[package]] @@ -394,6 +919,117 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +dependencies = [ + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-lsp-server" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0e711655c89181a6bc6a2cc348131fcd9680085f5b06b6af13427a393a6e72" +dependencies = [ + "bytes", + "dashmap", + "futures", + "httparse", + "ls-types", + "memchr", + "serde", + "serde_json", + "tokio", + "tokio-util", + "tower", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + [[package]] name = "unicode-ident" version = "1.0.24" @@ -412,6 +1048,68 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + [[package]] name = "zerocopy" version = "0.8.40" @@ -431,3 +1129,9 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index cbc4fd3..0ab788b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] resolver = "3" -members = ["allocators", "assembly", "compiler", "parser", "backends/llvm"] +members = ["allocators", "assembly", "compiler", "parser", "backends/llvm", "lsp"] diff --git a/allocators/src/arena.rs b/allocators/src/arena.rs index a903eb9..fe719df 100644 --- a/allocators/src/arena.rs +++ b/allocators/src/arena.rs @@ -57,6 +57,12 @@ pub struct SyncArenaAllocator { allocations: Vec, } +impl SyncArenaAllocator { + pub fn reset(&mut self) { + self.bump.lock().unwrap().reset(); + } +} + impl Allocator for SyncArenaAllocator { unsafe fn alloc_unsafe(&self, data: *const u8, layout: Layout, drop_fn: DropFn) -> *mut u8 { unsafe { diff --git a/assembly/src/types/compound.rs b/assembly/src/types/compound.rs index ac2b3f3..9eee71e 100644 --- a/assembly/src/types/compound.rs +++ b/assembly/src/types/compound.rs @@ -1,7 +1,7 @@ use crate::{ assembly::Assembly, types::Type, - values::{AnyConst, AnyValue, Value, ValueFlags}, + values::{AnyConst, AnyValue, Value, ValueFlags, default_associated_values}, }; use derive_more::{Debug, Display}; use fxhash::FxBuildHasher; @@ -51,21 +51,10 @@ impl<'l> Value<'l> for &'l StructT<'l> { } fn get_associated_value(&self, name: &str) -> Option> { - todo!() + default_associated_values(self, name) } fn as_any_value(&self) -> AnyValue<'l> { AnyValue::Constant(AnyConst::Type(Type::Struct(self))) } } - -struct DebugFields<'l, 'r>(&'r OnceLock>); - -impl std::fmt::Debug for DebugFields<'_, '_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self.0.get() { - Some(v) => std::fmt::Debug::fmt(v, f), - None => f.write_str("{}"), - } - } -} diff --git a/assembly/src/values/constants.rs b/assembly/src/values/constants.rs index 57b7694..8a16fd7 100644 --- a/assembly/src/values/constants.rs +++ b/assembly/src/values/constants.rs @@ -88,7 +88,6 @@ impl<'l> Value<'l> for Float { Float::F16(v) => v.ty(), Float::F32(v) => v.ty(), Float::F64(v) => v.ty(), - _ => unreachable!(), } } @@ -199,13 +198,13 @@ impl<'l> Value<'l> for AnyConst<'l> { Self::Str(s) => s.ty(), Self::Struct(t, _) => Type::Struct(t), Self::SizeOf(_) => Type::USIZE, - _ => todo!("{self:?}"), + Self::Function(f) => Type::Func(f.ty), } } fn flags(&self) -> super::ValueFlags { match self { - AnyConst::Function(f) => ValueFlags::Function, + AnyConst::Function(_) => ValueFlags::Function, AnyConst::Type(_) => ValueFlags::Type, _ => ValueFlags::Const, } diff --git a/compiler/src/error.rs b/compiler/src/diagnostics.rs similarity index 50% rename from compiler/src/error.rs rename to compiler/src/diagnostics.rs index 1871524..61f7a2c 100644 --- a/compiler/src/error.rs +++ b/compiler/src/diagnostics.rs @@ -2,10 +2,17 @@ use derive_more::Debug; use leaf_parser::{LineCol, ParseError}; use std::ops::Range; -use crate::SourceFile; +use crate::{SourceFile, metadata::CodePosition}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Kind { + Info, + Warning, + Error, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Code { Unknown = 0x0000, Parsing = 0x0100, @@ -36,30 +43,40 @@ pub enum Location { file: SourceFile, range: Range, }, - Position { - #[debug("{:?}", file.file)] - file: SourceFile, - position: LineCol, - }, } -#[derive(Debug)] -pub struct CompilationError { +impl Location { + pub fn file(&self) -> Option<&SourceFile> { + match self { + Location::None => None, + Location::Range { file, .. } => Some(file), + } + } + + pub fn range(&self) -> Range { + match self { + Location::None => 0..0, + Location::Range { range, .. } => range.clone(), + } + } +} + +#[derive(Debug, Clone)] +pub struct Diagnostic { pub kind: Kind, + pub code: Code, pub message: String, - pub location: Location, - pub cause: Option>, + pub position: CodePosition, + pub cause: Option>, } -impl CompilationError { - pub fn parsing(file: SourceFile, err: ParseError) -> Self { +impl Diagnostic { + pub fn parsing_err(file: SourceFile, err: ParseError) -> Self { Self { - kind: Kind::Parsing, + kind: Kind::Error, + code: Code::Parsing, message: err.to_string(), - location: Location::Position { - file, - position: err.location, - }, + position: CodePosition::new(file, err.location.offset..err.location.offset + 1), cause: None, } } diff --git a/compiler/src/events.rs b/compiler/src/events.rs new file mode 100644 index 0000000..502e7b8 --- /dev/null +++ b/compiler/src/events.rs @@ -0,0 +1,15 @@ +use crate::{diagnostics::Diagnostic, metadata::CodePosition}; +use leaf_assembly::values::AnyValue; + +#[derive(Debug, Clone)] +pub enum Event<'l> { + Symbol { + value: AnyValue<'l>, + position: CodePosition, + }, + Definition { + value: AnyValue<'l>, + position: CodePosition, + }, + Diagnostic(Diagnostic), +} diff --git a/compiler/src/lib.rs b/compiler/src/lib.rs index adb3abb..838cb4f 100644 --- a/compiler/src/lib.rs +++ b/compiler/src/lib.rs @@ -1,4 +1,4 @@ -use crate::{error::CompilationError, scope::Scope}; +use crate::{diagnostics::Diagnostic, events::Event, metadata::Metadata, scope::Scope}; use arcstr::literal_substr; use leaf_allocators::SyncAllocator; use leaf_assembly::{ @@ -8,9 +8,16 @@ use leaf_assembly::{ values::{AnyConst, AnyValue}, }; use leaf_parser::{SourceCode, ast}; -use std::{collections::VecDeque, ops::Deref, sync::Arc}; +use std::{ + any::TypeId, + collections::{HashMap, VecDeque}, + ops::Deref, + sync::{Arc, Mutex}, +}; -mod error; +pub mod diagnostics; +pub mod events; +pub mod metadata; mod scope; pub type SourceFile = Arc; @@ -19,7 +26,9 @@ type FuncQueue<'l> = VecDeque<(&'l Function<'l>, Arc, Scope<'l>)>; pub struct CompilationContext<'l> { ctx: &'l Context<'l>, - alloc: &'l dyn SyncAllocator, + _alloc: &'l dyn SyncAllocator, + event_callbacks: Vec) + Send + Sync + 'static>>, + metadata: Mutex, TypeId), Arc>>, } impl<'l> Deref for CompilationContext<'l> { @@ -33,8 +42,20 @@ impl<'l> Deref for CompilationContext<'l> { impl<'l> CompilationContext<'l> { pub fn new(alloc: &'l dyn SyncAllocator) -> Self { Self { - alloc, + _alloc: alloc, ctx: Context::new(alloc), + metadata: Default::default(), + event_callbacks: vec![], + } + } + + pub fn add_event_callback(&mut self, callback: impl Fn(&Event<'_>) + Send + Sync + 'static) { + self.event_callbacks.push(Box::new(callback)); + } + + fn emit_event(&self, event: Event) { + for callback in &self.event_callbacks { + callback(&event); } } @@ -42,32 +63,52 @@ impl<'l> CompilationContext<'l> { self.ctx.get_assembly(ident) } + #[allow(private_bounds)] + pub fn get_metadata(&self, v: AnyValue<'l>) -> Option> { + let arc = { + let metadata = self.metadata.lock().unwrap(); + metadata.get(&(v, TypeId::of::()))?.clone() + }; + let ptr = Arc::into_raw(arc); + let ptr = ptr as *const T; + unsafe { Some(Arc::from_raw(ptr)) } + } + + #[allow(private_bounds)] + pub fn set_metadata(&self, v: AnyValue<'l>, meta: T) -> Arc { + let meta = Arc::new(meta); + self.metadata + .lock() + .unwrap() + .insert((v, TypeId::of::()), meta.clone()); + meta + } + pub fn extend( &'l self, ident: AssemblyIdentifier, sources: &[Arc], - ) -> Result<(), CompilationError> { + ) -> Result<(), Diagnostic> { let assembly = self.ctx.get_or_create_assembly(ident.clone()); let mut units = Vec::with_capacity(sources.len()); for src in sources { match leaf_parser::parse(src) { Ok(unit) => units.push(unit), - Err(err) => return Err(CompilationError::parsing(src.clone(), err)), + Err(err) => return Err(Diagnostic::parsing_err(src.clone(), err)), } } let mut scopes: Vec<_> = sources .iter() .map(|src| { - let mut scope = Scope::new(assembly, src.clone()); + let mut scope = Scope::new(self, assembly, src.clone()); macro_rules! insert_types { ($($id:literal : $ty:expr,)*) => { $( scope.insert( literal_substr!($id), AnyValue::Constant(AnyConst::Type($ty.into())), - false, ); )* }; diff --git a/compiler/src/main.rs b/compiler/src/main.rs index a632f3e..1483ccc 100644 --- a/compiler/src/main.rs +++ b/compiler/src/main.rs @@ -13,9 +13,11 @@ use leaf_parser::SourceCode; use std::{ path::{Path, PathBuf}, sync::Arc, + time::Instant, }; fn main() { + let start = Instant::now(); let alloc = SyncArenaAllocator::default(); let context = CompilationContext::new(&alloc); let ident = AssemblyIdentifier { @@ -32,6 +34,7 @@ fn main() { println!("{:#?}", err); return; } + let time = start.elapsed(); let assembly = context.get_assembly(&ident).unwrap(); println!("{:#?}\n", assembly); @@ -60,4 +63,6 @@ fn main() { target_machine .write_to_file(&module, FileType::Assembly, Path::new("out.asm")) .unwrap(); + + println!("Codegen time: {time:?}"); } diff --git a/compiler/src/metadata.rs b/compiler/src/metadata.rs new file mode 100644 index 0000000..098818c --- /dev/null +++ b/compiler/src/metadata.rs @@ -0,0 +1,66 @@ +use leaf_parser::{LineCol, SourceCode}; +use std::{ + any::Any, + ops::Range, + sync::{Arc, OnceLock}, +}; + +pub trait Metadata: Any + Send + Sync {} +impl Metadata for T {} + +#[derive(Debug, Clone)] +pub struct CodePosition { + pub file: Arc, + pub range: Range, + line_col: OnceLock>, +} + +impl CodePosition { + pub fn new(file: Arc, range: Range) -> Self { + Self { + file, + range, + line_col: OnceLock::new(), + } + } + + pub fn line_col(&self) -> &Range { + self.line_col.get_or_init(|| { + let mut line_col_range = LineCol { + line: 0, + column: 0, + offset: self.range.start, + }..LineCol { + line: 0, + column: 0, + offset: self.range.end, + }; + let mut line = 0; + let mut col = 0; + + for (byte_idx, ch) in self.file.text.char_indices() { + if byte_idx == self.range.start { + line_col_range.start.line = line; + line_col_range.start.column = col; + } + if byte_idx == self.range.end { + line_col_range.end.line = line; + line_col_range.end.column = col; + } + + if ch == '\n' { + line += 1; + col = 0; + } else { + col += 1; + } + } + + if self.range.end == self.file.text.len() { + line_col_range.end.line = line; + line_col_range.end.column = col; + } + line_col_range + }) + } +} diff --git a/compiler/src/scope.rs b/compiler/src/scope.rs index 58cbf28..42d3604 100644 --- a/compiler/src/scope.rs +++ b/compiler/src/scope.rs @@ -1,4 +1,4 @@ -use crate::{FuncQueue, error::*}; +use crate::{CompilationContext, FuncQueue, diagnostics::*, events::Event, metadata::CodePosition}; use arcstr::{Substr, literal_substr}; use leaf_assembly::{ assembly::Assembly, @@ -46,31 +46,35 @@ impl<'l, 'r, 'a> ExpressionContext<'l, 'r> { #[derive(Clone)] struct Variable<'l> { value: Arc>>, - mutable: bool, } #[derive(Clone)] pub struct Scope<'l> { + ctx: &'l CompilationContext<'l>, assembly: &'l Assembly<'l>, source: Arc, values: HashMap>, } impl<'l> Scope<'l> { - pub fn new(assembly: &'l Assembly<'l>, source: Arc) -> Self { + pub fn new( + ctx: &'l CompilationContext<'l>, + assembly: &'l Assembly<'l>, + source: Arc, + ) -> Self { Self { + ctx, assembly, source, values: HashMap::default(), } } - pub fn insert(&mut self, name: Substr, value: AnyValue<'l>, mutable: bool) { + pub fn insert(&mut self, name: Substr, value: AnyValue<'l>) { self.values.insert( name, Variable { value: Arc::new(OnceLock::from(value)), - mutable, }, ); } @@ -82,7 +86,6 @@ impl<'l> Scope<'l> { name.clone(), Variable { value: Arc::default(), - mutable: false, }, ); } @@ -93,7 +96,7 @@ impl<'l> Scope<'l> { &mut self, decl: &[ConstDecl], fn_queue: &mut FuncQueue<'l>, - ) -> Result<(), CompilationError> { + ) -> Result<(), Diagnostic> { for val in decl { let expr = self.compile_expression( &val.value, @@ -105,13 +108,19 @@ impl<'l> Scope<'l> { }, )?; match &val.names { - NamePattern::Single(ident) => self - .values - .get_mut(&ident.0) - .unwrap() - .value - .set(expr) - .unwrap(), + NamePattern::Single(ident) => { + self.values + .get_mut(&ident.0) + .unwrap() + .value + .set(expr) + .unwrap(); + + self.ctx.emit_event(Event::Definition { + value: expr, + position: CodePosition::new(self.source.clone(), ident.range()), + }); + } NamePattern::Tuple(_) => todo!(), NamePattern::List(_) => todo!(), } @@ -124,7 +133,7 @@ impl<'l> Scope<'l> { func: &'l Function<'l>, block: &Arc, fn_queue: &mut FuncQueue<'l>, - ) -> Result<(), CompilationError> { + ) -> Result<(), Diagnostic> { let mut builder = func.create_body().unwrap(); let mut ret = self.compile_block( block, @@ -159,42 +168,42 @@ impl<'l> Scope<'l> { &mut self, expr: &Expr, ctx: &mut ExpressionContext<'l, '_>, - ) -> Result, CompilationError> { + ) -> Result, Diagnostic> { match expr { Expr::Ident(Ident(name)) => match self.values.get(name) { - None => Err(CompilationError { - kind: Kind::SymbolNotFound, + None => Err(Diagnostic { + kind: Kind::Error, + code: Code::SymbolNotFound, message: format!("Symbol `{name}` does not exist in the current scope."), - location: Location::Range { - file: self.source.clone(), - range: name.range(), - }, + position: CodePosition::new(self.source.clone(), name.range()), cause: None, }), Some(Variable { value, .. }) => match value.get() { - None => Err(CompilationError { - kind: Kind::UninitializedSymbol, + None => Err(Diagnostic { + kind: Kind::Error, + code: Code::UninitializedSymbol, message: format!("Symbol `{name}` is not initialized at this time."), - location: Location::Range { - file: self.source.clone(), - range: name.range(), - }, + position: CodePosition::new(self.source.clone(), name.range()), cause: None, }), - Some(value) => Ok(*value), + Some(value) => { + self.ctx.emit_event(Event::Symbol { + value: *value, + position: CodePosition::new(self.source.clone(), name.range()), + }); + Ok(*value) + } }, }, Expr::Func(func) => self .make_function(func, ctx) .map(|f| AnyValue::Constant(AnyConst::Function(f))) - .map_err(|err| CompilationError { - kind: Kind::FunctionCompilationFailed, + .map_err(|err| Diagnostic { + kind: Kind::Error, + code: Code::FunctionCompilationFailed, message: "Could not compile function.".to_string(), - location: Location::Range { - file: self.source.clone(), - range: func.text.range(), - }, + position: CodePosition::new(self.source.clone(), func.text.range()), cause: Some(Box::new(err)), }), @@ -210,18 +219,16 @@ impl<'l> Scope<'l> { _ => n.text.parse::<$ty>(), } .map(|v| AnyValue::Constant(AnyConst::Int(Int::$id(v)))) - .map_err(|_| CompilationError { - kind: Kind::InvalidInteger, + .map_err(|_| Diagnostic { + kind: Kind::Error, + code: Code::InvalidInteger, message: format!("`{}` is not a valid integer.", n.text), - location: Location::Range { - file: self.source.clone(), - range: n.text.range(), - }, + position: CodePosition::new(self.source.clone(), n.text.range()), cause: None, }) }; } - match n.r#type.as_ref().map(|v| v.as_str()) { + let value = match n.r#type.as_ref().map(|v| v.as_str()) { None if ctx.type_hint == Some(Type::I8) => parse_number!(i8, I8), None if ctx.type_hint == Some(Type::I16) => parse_number!(i16, I16), None if ctx.type_hint == Some(Type::I32) => parse_number!(i32, I32), @@ -248,16 +255,19 @@ impl<'l> Scope<'l> { Some("u64") => parse_number!(u64, U64), Some("u128") => parse_number!(u128, U128), Some("usize") => parse_number!(u64, USize), - Some(ty) => Err(CompilationError { - kind: Kind::InvalidIntegerType, + Some(ty) => Err(Diagnostic { + kind: Kind::Error, + code: Code::InvalidIntegerType, message: format!("`{ty}` is not a valid integer type."), - location: Location::Range { - file: self.source.clone(), - range: n.text.range(), - }, + position: CodePosition::new(self.source.clone(), n.text.range()), cause: None, }), - } + }?; + self.ctx.emit_event(Event::Symbol { + value: value, + position: CodePosition::new(self.source.clone(), n.text.range()), + }); + Ok(value) } Expr::Access(expr) => { @@ -269,49 +279,63 @@ impl<'l> Scope<'l> { if let Some(value) = value.get_associated_value(&field.0) { return Ok(value); } - match value.ty() { - Type::Struct(StructT { fields, .. }) => { - if let Some(fields) = fields.get() { - if let Some(field) = fields.get(field.0.as_str()) { - let builder = ctx.builder.as_mut().unwrap(); - return Ok(builder - .get_element_value(value, field.name.as_any_value()) - .unwrap() - .as_any_value()); - } - } - } - Type::Ptr(PtrT { - base: Type::Struct(StructT { fields, .. }), - .. - }) => { - if let Some(fields) = fields.get() { - if let Some(field) = fields.get(field.0.as_str()) { - let builder = ctx.builder.as_mut().unwrap(); - let inst = builder - .get_element_ptr(value, field.name.as_any_value()) - .unwrap() - .as_any_value(); - unsafe { - if let AnyValue::Instruction(inst) = inst { - inst.edit_flags(|f| f | ValueFlags::LValue); - } + let value = 'value: { + match value.ty() { + Type::Struct(StructT { fields, .. }) => { + if let Some(fields) = fields.get() { + if let Some(field) = fields.get(field.0.as_str()) { + let builder = ctx.builder.as_mut().unwrap(); + break 'value Some( + builder + .get_element_value(value, field.name.as_any_value()) + .unwrap() + .as_any_value(), + ); } - - return Ok(inst); } + None } + Type::Ptr(PtrT { + base: Type::Struct(StructT { fields, .. }), + .. + }) => { + if let Some(fields) = fields.get() { + if let Some(field) = fields.get(field.0.as_str()) { + let builder = ctx.builder.as_mut().unwrap(); + let inst = builder + .get_element_ptr(value, field.name.as_any_value()) + .unwrap() + .as_any_value(); + + unsafe { + if let AnyValue::Instruction(inst) = inst { + inst.edit_flags(|f| f | ValueFlags::LValue); + } + } + + break 'value Some(inst); + } + } + None + } + _ => None, } - _ => {} }; - return Err(CompilationError { - kind: Kind::FieldNotFound, + + if let Some(value) = value { + self.ctx.emit_event(Event::Symbol { + value: value, + position: CodePosition::new(self.source.clone(), field.range()), + }); + return Ok(value); + } + + return Err(Diagnostic { + kind: Kind::Error, + code: Code::FieldNotFound, message: format!("Value does not contain field `{}`.", field.0), - location: Location::Range { - file: self.source.clone(), - range: value_expr.range(), - }, + position: CodePosition::new(self.source.clone(), field.range()), cause: None, }); } @@ -453,30 +477,24 @@ impl<'l> Scope<'l> { } (Type::Ptr(PtrT { base, .. }), ty, BinaryOp::Assign(_)) => match *base == ty { true => Ok(builder.store(lhs, rhs).unwrap()), - false => Err(CompilationError { - kind: Kind::InvalidType, + false => Err(Diagnostic { + kind: Kind::Error, + code: Code::InvalidType, message: format!( "Cannot assign a value of type `{ty}` to a value of type `{base}`." ), - location: Location::Range { - file: self.source.clone(), - range: rhs_expr.range(), - }, + position: CodePosition::new(self.source.clone(), expr.range()), cause: None, }), }, (src_ty, Type::Type, BinaryOp::Cast(_)) => { - let dst_ty = - self.assert_ty(rhs, rhs_expr) - .map_err(|err| CompilationError { - kind: Kind::InvalidCast, - message: "Cannot perform cast.".to_string(), - location: Location::Range { - file: self.source.clone(), - range: expr.range(), - }, - cause: Some(Box::new(err)), - })?; + let dst_ty = self.assert_ty(rhs, rhs_expr).map_err(|err| Diagnostic { + kind: Kind::Error, + code: Code::InvalidCast, + message: "Cannot perform cast.".to_string(), + position: CodePosition::new(self.source.clone(), expr.range()), + cause: Some(Box::new(err)), + })?; if src_ty == dst_ty { return Ok(lhs); } @@ -533,13 +551,11 @@ impl<'l> Scope<'l> { let func = match self.compile_expression(func, ctx)? { AnyValue::Constant(AnyConst::Function(func)) => func, _ => { - return Err(CompilationError { - kind: Kind::NotAFunction, + return Err(Diagnostic { + kind: Kind::Error, + code: Code::NotAFunction, message: "Value is not a function".into(), - location: Location::Range { - file: self.source.clone(), - range: func.range(), - }, + position: CodePosition::new(self.source.clone(), func.range()), cause: None, }); } @@ -574,7 +590,7 @@ impl<'l> Scope<'l> { }; let ctx = self.assembly.ctx(); - scope.insert(literal_substr!("Self"), struct_ty.as_any_value(), false); + scope.insert(literal_substr!("Self"), struct_ty.as_any_value()); let mut field_map = FieldMap::default(); for ast::Field { name, @@ -612,14 +628,12 @@ impl<'l> Scope<'l> { unreachable!() }; if mutable.is_some() && !*is_mut { - return Err(CompilationError { - kind: Kind::NotAFunction, + return Err(Diagnostic { + kind: Kind::Error, + code: Code::NotAFunction, message: "Cannot obtain a mutable pointer to an immutable value." .into(), - location: Location::Range { - file: self.source.clone(), - range: expr.range(), - }, + position: CodePosition::new(self.source.clone(), expr.range()), cause: None, }); } @@ -677,13 +691,11 @@ impl<'l> Scope<'l> { match value.is_lvalue() { false => unsafe { if !matches!(ty, Type::Ptr(_)) { - return Err(CompilationError { - kind: Kind::CannotDereference, + return Err(Diagnostic { + kind: Kind::Error, + code: Code::CannotDereference, message: format!("Cannot dereference a value of type `{ty}`."), - location: Location::Range { - file: self.source.clone(), - range: expr.range(), - }, + position: CodePosition::new(self.source.clone(), expr.range()), cause: None, }); } @@ -703,13 +715,11 @@ impl<'l> Scope<'l> { let Type::Ptr(PtrT { base, .. }) = ty else { unreachable!() }; - return Err(CompilationError { - kind: Kind::CannotDereference, + return Err(Diagnostic { + kind: Kind::Error, + code: Code::CannotDereference, message: format!("Cannot dereference a value of type `{base}`."), - location: Location::Range { - file: self.source.clone(), - range: expr.range(), - }, + position: CodePosition::new(self.source.clone(), expr.range()), cause: None, }); } @@ -735,13 +745,11 @@ impl<'l> Scope<'l> { // TODO Add support for custom indexing operations if index.ty() != Type::USIZE { - return Err(CompilationError { - kind: Kind::InvalidType, + return Err(Diagnostic { + kind: Kind::Error, + code: Code::InvalidType, message: "Value is not of type `usize`".into(), - location: Location::Range { - file: self.source.clone(), - range: expr.index.range(), - }, + position: CodePosition::new(self.source.clone(), expr.index.range()), cause: None, }); } @@ -775,13 +783,11 @@ impl<'l> Scope<'l> { None => match ctx.type_hint { Some(ty) => AnyValue::Constant(AnyConst::Type(ty)), None => { - return Err(CompilationError { - kind: Kind::CannotInferType, + return Err(Diagnostic { + kind: Kind::Error, + code: Code::CannotInferType, message: "Type cannot be inferred.".into(), - location: Location::Range { - file: self.source.clone(), - range: ctor.range(), - }, + position: CodePosition::new(self.source.clone(), ctor.range()), cause: None, }); } @@ -791,16 +797,17 @@ impl<'l> Scope<'l> { struct_ty @ StructT { fields, .. }, ))) = ty else { - return Err(CompilationError { - kind: Kind::NotAStruct, + return Err(Diagnostic { + kind: Kind::Error, + code: Code::NotAStruct, message: format!("Expected struct type, got value of type `{}`.", ty.ty()), - location: Location::Range { - file: self.source.clone(), - range: match &ctor.r#type { + position: CodePosition::new( + self.source.clone(), + match &ctor.r#type { None => ctor.range(), Some(ty) => ty.range(), }, - }, + ), cause: None, }); }; @@ -813,13 +820,11 @@ impl<'l> Scope<'l> { } in fields.values() { let Some(name_value_pair) = ctor.values.get(*fld_name) else { - return Err(CompilationError { - kind: Kind::UninitializedField, + return Err(Diagnostic { + kind: Kind::Error, + code: Code::UninitializedField, message: format!("Uninitialized field `{fld_name}`."), - location: Location::Range { - file: self.source.clone(), - range: expr.range(), - }, + position: CodePosition::new(self.source.clone(), expr.range()), cause: None, }); }; @@ -859,7 +864,7 @@ impl<'l> Scope<'l> { &mut self, ast: &Arc, ctx: &mut ExpressionContext<'l, '_>, - ) -> Result<&'l Function<'l>, CompilationError> { + ) -> Result<&'l Function<'l>, Diagnostic> { let ret_ty = match ast.ret.as_ref() { None => AnyValue::Constant(AnyConst::Type(Type::Void)), Some(ty) => self.compile_expression(ty, ctx)?, @@ -885,7 +890,7 @@ impl<'l> Scope<'l> { let mut scope = self.clone(); for (i, arg) in ast.args.iter().enumerate() { - scope.insert(arg.name.0.clone(), AnyValue::Parameter(i, func), false); + scope.insert(arg.name.0.clone(), AnyValue::Parameter(i, func)); } ctx.fn_queue.push_back((func, block.clone(), scope)); @@ -899,7 +904,7 @@ impl<'l> Scope<'l> { value: &Expr, mutable: bool, ctx: &mut ExpressionContext<'l, '_>, - ) -> Result, CompilationError> { + ) -> Result, Diagnostic> { let mut sub_ctx = ExpressionContext { decl_names: Some(names), builder: ctx.builder.as_deref_mut(), @@ -922,9 +927,13 @@ impl<'l> Scope<'l> { ident.0.clone(), Variable { value: Arc::new(OnceLock::from(value)), - mutable, }, ); + + self.ctx.emit_event(Event::Definition { + value: value, + position: CodePosition::new(self.source.clone(), ident.range()), + }); } NamePattern::Tuple(_) => todo!(), NamePattern::List(_) => todo!(), @@ -936,7 +945,7 @@ impl<'l> Scope<'l> { &mut self, block: &Block, ctx: &mut ExpressionContext<'l, '_>, - ) -> Result, CompilationError> { + ) -> Result, Diagnostic> { let mut scope = self.clone(); let builder = ctx.builder.as_mut().unwrap(); @@ -960,7 +969,7 @@ impl<'l> Scope<'l> { &mut self, expr: &If, ctx: &mut ExpressionContext<'l, '_>, - ) -> Result, CompilationError> { + ) -> Result, Diagnostic> { let If { cond, block, else_ } = expr; let condition = self.compile_expression(cond, ctx)?; self.assert_ty_eq(&condition, cond, &Type::Bool)?; @@ -1018,20 +1027,14 @@ impl<'l> Scope<'l> { .into()) } - fn assert_ty( - &self, - val: AnyValue<'l>, - value_expr: &Expr, - ) -> Result, CompilationError> { + fn assert_ty(&self, val: AnyValue<'l>, value_expr: &Expr) -> Result, Diagnostic> { match val { AnyValue::Constant(AnyConst::Type(ty)) => Ok(ty), - _ => Err(CompilationError { - kind: Kind::NotAType, + _ => Err(Diagnostic { + kind: Kind::Error, + code: Code::NotAType, message: "Value is not a type.".to_string(), - location: Location::Range { - file: self.source.clone(), - range: value_expr.range(), - }, + position: CodePosition::new(self.source.clone(), value_expr.range()), cause: None, }), } @@ -1042,18 +1045,16 @@ impl<'l> Scope<'l> { value: &AnyValue<'l>, value_expr: &Expr, expected: &Type<'l>, - ) -> Result, CompilationError> { + ) -> Result, Diagnostic> { let value_ty = value.ty(); match value_ty == *expected { true => Ok(value_ty), false => { - return Err(CompilationError { - kind: Kind::InvalidType, + return Err(Diagnostic { + kind: Kind::Error, + code: Code::InvalidType, message: format!("Expected value of type `{expected}`, found `{value_ty}`."), - location: Location::Range { - file: self.source.clone(), - range: value_expr.range(), - }, + position: CodePosition::new(self.source.clone(), value_expr.range()), cause: None, }); } diff --git a/lsp/Cargo.toml b/lsp/Cargo.toml new file mode 100644 index 0000000..bf83b99 --- /dev/null +++ b/lsp/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "leaf_lsp" +version = "0.1.0" +edition = "2024" + +[dependencies] +leaf_parser = { path = "../parser" } +leaf_compiler = { path = "../compiler" } +leaf_assembly = { path = "../assembly" } +leaf_allocators = { path = "../allocators" } +tower-lsp-server = "0.23.0" +tokio = { version = "1.50.0", features = ["io-std", "macros", "rt-multi-thread", "sync"] } +scc = "3.6.9" +rust_search = "2.1.0" +rangemap = "1.7.1" +boxcar = "0.2.14" diff --git a/lsp/src/main.rs b/lsp/src/main.rs new file mode 100644 index 0000000..7eb459b --- /dev/null +++ b/lsp/src/main.rs @@ -0,0 +1,153 @@ +use crate::utils::UriUtils; +use crate::workspace::{Workspace, start_workspace_thread}; +use std::sync::Arc; +use tokio::sync::RwLock; +use tower_lsp_server::jsonrpc::Result; +use tower_lsp_server::ls_types::request::{GotoDeclarationParams, GotoDeclarationResponse}; +use tower_lsp_server::{Client, LspService, Server}; +use tower_lsp_server::{LanguageServer, ls_types::*}; +mod utils; +mod workspace; + +struct Backend { + client: Client, + workspaces: RwLock>>, +} + +impl LanguageServer for Backend { + async fn initialize(&self, params: InitializeParams) -> Result { + let mut workspaces = self.workspaces.write().await; + for workspace in workspaces.drain(..) { + workspace.close().await; + } + + for folder in params.workspace_folders.iter().flatten() { + workspaces + .push(start_workspace_thread(folder.uri.clone(), self.client.clone()).unwrap()); + } + + Ok(InitializeResult { + capabilities: ServerCapabilities { + hover_provider: Some(HoverProviderCapability::Simple(true)), + definition_provider: Some(OneOf::Left(true)), + declaration_provider: Some(DeclarationCapability::Simple(true)), + text_document_sync: Some(TextDocumentSyncCapability::Options( + TextDocumentSyncOptions { + save: Some(TextDocumentSyncSaveOptions::SaveOptions(SaveOptions { + include_text: Some(false), + })), + ..Default::default() + }, + )), + semantic_tokens_provider: Some( + SemanticTokensServerCapabilities::SemanticTokensOptions( + SemanticTokensOptions { + legend: SemanticTokensLegend { + token_types: vec![ + SemanticTokenType::TYPE, + SemanticTokenType::FUNCTION, + SemanticTokenType::NUMBER, + ], + token_modifiers: vec![], + }, + full: Some(SemanticTokensFullOptions::Bool(true)), + range: None, + ..Default::default() + }, + ), + ), + ..Default::default() + }, + ..Default::default() + }) + } + + async fn initialized(&self, _: InitializedParams) { + self.client + .log_message(MessageType::INFO, "server initialized!") + .await; + } + + async fn hover(&self, params: HoverParams) -> Result> { + if let Some(w) = self + .find_workspace(¶ms.text_document_position_params.text_document.uri) + .await + { + return Ok(w.hover(params).await); + } + Ok(None) + } + + async fn goto_declaration( + &self, + params: GotoDeclarationParams, + ) -> Result> { + if let Some(w) = self + .find_workspace(¶ms.text_document_position_params.text_document.uri) + .await + { + return Ok(w.go_to_declaration(params).await); + } + Ok(None) + } + + async fn goto_definition( + &self, + params: GotoDeclarationParams, + ) -> Result> { + if let Some(w) = self + .find_workspace(¶ms.text_document_position_params.text_document.uri) + .await + { + return Ok(w.go_to_declaration(params).await); + } + Ok(None) + } + + async fn did_save(&self, params: DidSaveTextDocumentParams) { + if let Some(w) = self.find_workspace(¶ms.text_document.uri).await { + w.reload().await; + } + } + + async fn semantic_tokens_full( + &self, + params: SemanticTokensParams, + ) -> Result> { + if let Some(w) = self.find_workspace(¶ms.text_document.uri).await { + return Ok(w.semantic_tokens(params).await); + } + Ok(None) + } + + async fn shutdown(&self) -> Result<()> { + let mut workspaces = self.workspaces.write().await; + for workspace in workspaces.drain(..) { + workspace.close().await; + } + Ok(()) + } +} + +impl Backend { + pub async fn find_workspace(&self, file: &Uri) -> Option> { + let workspaces = self.workspaces.read().await; + let path = file.strip_header(); + workspaces + .iter() + .find(|w| path.starts_with(&w.base)) + .cloned() + } +} + +#[tokio::main] +async fn main() { + let stdin = tokio::io::stdin(); + let stdout = tokio::io::stdout(); + + let (service, socket) = LspService::new(|client| Backend { + client, + workspaces: RwLock::default(), + }); + Server::new(stdin, stdout, socket).serve(service).await; +} diff --git a/lsp/src/utils.rs b/lsp/src/utils.rs new file mode 100644 index 0000000..c851be9 --- /dev/null +++ b/lsp/src/utils.rs @@ -0,0 +1,35 @@ +use leaf_compiler::metadata::CodePosition; +use tower_lsp_server::ls_types::{Position, Range, Uri}; + +pub trait UriUtils { + fn strip_header(&self) -> &str; +} + +impl UriUtils for Uri { + fn strip_header(&self) -> &str { + let Some(("", str)) = self.as_str().split_once("file://") else { + unimplemented!() + }; + str + } +} + +pub trait CodePositionUtils { + fn lsp_range(&self) -> Range; +} + +impl CodePositionUtils for CodePosition { + fn lsp_range(&self) -> Range { + let range = self.line_col(); + Range { + start: Position { + line: range.start.line as u32, + character: range.start.column as u32, + }, + end: Position { + line: range.end.line as u32, + character: range.end.column as u32, + }, + } + } +} diff --git a/lsp/src/workspace.rs b/lsp/src/workspace.rs new file mode 100644 index 0000000..324fa0e --- /dev/null +++ b/lsp/src/workspace.rs @@ -0,0 +1,407 @@ +use leaf_allocators::SyncArenaAllocator; +use leaf_assembly::{ + assembly::{AssemblyIdentifier, Version}, + types::{Type, derivations::PtrT}, + values::{AnyConst, AnyValue, Value}, +}; +use leaf_compiler::{CompilationContext, events::Event, metadata::CodePosition}; +use leaf_parser::{ArcStr, SourceCode}; +use rangemap::RangeMap; +use rust_search::SearchBuilder; +use std::{ + borrow::Cow, + collections::HashMap, + fmt::Write, + ops::Range, + path::PathBuf, + sync::{Arc, OnceLock}, +}; +use tokio::sync::{ + Mutex, Notify, RwLock, + mpsc::{Sender, channel}, +}; +use tower_lsp_server::{ + Client, + ls_types::{ + Diagnostic, DiagnosticSeverity, Hover, HoverContents, HoverParams, Location, MarkedString, + NumberOrString, Position, Range as LspRange, SemanticToken, SemanticTokenType, + SemanticTokens, SemanticTokensParams, SemanticTokensResult, TextDocumentPositionParams, + Uri, + request::{GotoDeclarationParams, GotoDeclarationResponse}, + }, +}; + +use crate::utils::{CodePositionUtils, UriUtils}; + +pub struct Workspace { + pub base: String, + sender: Sender, +} + +impl Workspace { + pub async fn close(&self) -> bool { + let notify = Arc::new(Notify::new()); + self.sender + .send(Request::Close(notify.clone())) + .await + .unwrap(); + notify.notified().await; + true + } + + pub async fn hover(&self, params: HoverParams) -> Option { + let result = Arc::<(OnceLock, Notify)>::default(); + self.sender + .send(Request::Hover(params, result.clone())) + .await + .unwrap(); + result.1.notified().await; + result.0.get().cloned() + } + + pub async fn go_to_declaration( + &self, + params: GotoDeclarationParams, + ) -> Option { + let result = Arc::<(OnceLock, Notify)>::default(); + self.sender + .send(Request::GoToDeclaration(params, result.clone())) + .await + .unwrap(); + result.1.notified().await; + result.0.get().cloned() + } + + pub async fn semantic_tokens( + &self, + params: SemanticTokensParams, + ) -> Option { + let result = Arc::<(OnceLock, Notify)>::default(); + self.sender + .send(Request::SemanticTokens(params, result.clone())) + .await + .unwrap(); + result.1.notified().await; + result.0.get().cloned() + } + + pub async fn reload(&self) { + self.sender.send(Request::Reload).await.unwrap(); + } +} + +enum Request { + Reload, + Close(Arc), + Hover(HoverParams, Arc<(OnceLock, Notify)>), + GoToDeclaration( + GotoDeclarationParams, + Arc<(OnceLock, Notify)>, + ), + SemanticTokens( + SemanticTokensParams, + Arc<(OnceLock, Notify)>, + ), +} + +struct State<'l> { + context: CompilationContext<'l>, + files: Arc>>, +} + +#[derive(Default)] +struct FileInfo { + line_ranges: HashMap>, + symbol_ranges: RangeMap>()]>, + symbol_positions: HashMap<[u8; size_of::>()], CodePosition>, + symbol_definitions: HashMap<[u8; size_of::>()], CodePosition>, +} + +impl<'l> State<'l> { + pub fn new(alloc: &'l SyncArenaAllocator) -> Self { + State { + context: CompilationContext::new(alloc), + files: Default::default(), + } + } +} + +pub fn start_workspace_thread(url: Uri, client: Client) -> Result, String> { + let base = url.strip_header().to_string(); + let path = base.clone(); + let (sender, mut receiver) = channel(1); + let _handle = tokio::task::spawn(async move { + let mut alloc = SyncArenaAllocator::default(); + 'global: loop { + alloc.reset(); + let mut state = State::new(&alloc); + let mut diagnostics = HashMap::>::new(); + + let files: Vec<_> = tokio::task::block_in_place(|| { + SearchBuilder::default() + .location(&path) + .ext("leaf") + .build() + .map(|f| { + let mut info = state.files.blocking_write(); + let info = info.entry(f.clone()).or_default(); + let text: ArcStr = std::fs::read_to_string(&f).unwrap().into(); + info.line_ranges = calc_line_ranges(text.as_str()); + diagnostics.entry(PathBuf::from(&f)).or_default(); + Arc::new(SourceCode { + text, + file: f.into(), + }) + }) + .collect() + }); + + let diagnostics = Arc::new(Mutex::new(diagnostics)); + + { + let info = state.files.clone(); + let diagnostics = diagnostics.clone(); + state.context.add_event_callback(move |e| unsafe { + tokio::task::block_in_place(|| match e { + Event::Symbol { value, position } => { + let mut info = info.blocking_write(); + let info = info + .entry(position.file.file.to_string_lossy().to_string()) + .or_default(); + info.symbol_ranges + .insert(position.range.clone(), std::mem::transmute(*value)); + info.symbol_positions + .insert(std::mem::transmute(*value), position.clone()); + } + Event::Definition { value, position } => { + let mut info = info.blocking_write(); + let info = info + .entry(position.file.file.to_string_lossy().to_string()) + .or_default(); + info.symbol_definitions + .insert(std::mem::transmute(*value), position.clone()); + info.symbol_ranges + .insert(position.range.clone(), std::mem::transmute(*value)); + info.symbol_positions + .insert(std::mem::transmute(*value), position.clone()); + } + Event::Diagnostic(diagnostic) => { + let mut diagnostics = diagnostics.blocking_lock(); + diagnostics + .entry(diagnostic.position.file.file.clone()) + .or_default() + .extend(make_diagnostics(diagnostic)); + } + }); + }); + } + + if let Err(err) = state.context.extend( + AssemblyIdentifier { + version: Version::default(), + name: Cow::Borrowed("Leaf lsp tmp"), + }, + &files, + ) { + let mut diagnostics = diagnostics.lock().await; + diagnostics + .entry(err.position.file.file.clone()) + .or_default() + .extend(make_diagnostics(&err)); + } + + { + let mut diagnostics = diagnostics.lock().await; + for (file, diagnostics) in diagnostics.drain() { + client + .publish_diagnostics(Uri::from_file_path(file).unwrap(), diagnostics, None) + .await; + } + } + + while let Some(event) = receiver.recv().await { + match event { + Request::Reload => { + alloc = SyncArenaAllocator::default(); + continue 'global; + } + Request::Hover(params, result) => { + let value = state + .with_file_and_range( + ¶ms.text_document_position_params, + |info, range| unsafe { + let Some(symbol) = info.symbol_ranges.get( + &(range.start + + params + .text_document_position_params + .position + .character as usize), + ) else { + return None; + }; + + // This is blasphemy but what can I do? :3 + Some(std::mem::transmute::<_, AnyValue>(*symbol)) + }, + ) + .await; + + if let Some(Some(value)) = value { + let mut message = String::new(); + let _ = writeln!( + message, + "Type: {}", + match value.is_lvalue() { + false => value.ty(), + true => match value.ty() { + Type::Ptr(PtrT { base, .. }) => *base, + _ => unreachable!(), + }, + } + ); + result + .0 + .set(Hover { + contents: HoverContents::Scalar(MarkedString::String(message)), + range: None, + }) + .unwrap(); + } + + result.1.notify_one(); + } + Request::GoToDeclaration(params, result) => { + let declaration = state + .with_file_and_range( + ¶ms.text_document_position_params, + |info, range| { + let Some(symbol) = info.symbol_ranges.get( + &(range.start + + params + .text_document_position_params + .position + .character as usize), + ) else { + return None; + }; + let Some(position) = info.symbol_definitions.get(symbol) else { + return None; + }; + Some(position.clone()) + }, + ) + .await; + + if let Some(Some(decl)) = declaration { + result + .0 + .set(GotoDeclarationResponse::Scalar(Location { + uri: Uri::from_file_path(&decl.file.file).unwrap(), + range: decl.lsp_range(), + })) + .unwrap(); + } + + result.1.notify_one(); + } + Request::SemanticTokens(params, result) => { + let info = state.files.read().await; + let Some(file) = info.get(params.text_document.uri.strip_header()) else { + result.1.notify_one(); + continue; + }; + let mut tokens = SemanticTokens::default(); + for (symbol, position) in file.symbol_positions.iter() { + let symbol: AnyValue = unsafe { std::mem::transmute(*symbol) }; + let line_col = position.line_col(); + tokens.data.push(SemanticToken { + delta_line: line_col.start.line as u32, + delta_start: line_col.start.column as u32, + length: position.range.len() as u32, + token_type: match symbol { + AnyValue::Constant(AnyConst::Type(_)) => 0, + AnyValue::Constant(AnyConst::Function(_)) => 1, + AnyValue::Constant(AnyConst::Int(_) | AnyConst::Float(_)) => 2, + _ => continue, + }, + token_modifiers_bitset: 0, + }); + } + + let mut previous_line = 0; + let mut previous_start = 0; + tokens.data.sort_by_key(|v| (v.delta_line, v.delta_start)); + for SemanticToken { + delta_line, + delta_start, + .. + } in tokens.data.iter_mut() + { + let line = *delta_line; + let start = *delta_start; + *delta_start = match line == previous_line { + false => start, + true => start - previous_start, + }; + *delta_line = line - previous_line; + previous_line = line; + previous_start = start; + } + + result.0.set(SemanticTokensResult::Tokens(tokens)).unwrap(); + result.1.notify_one(); + } + Request::Close(notify) => { + notify.notify_waiters(); + break 'global; + } + } + } + } + }); + Ok(Arc::new(Workspace { base, sender })) +} + +fn calc_line_ranges(text: &str) -> HashMap> { + let mut map = HashMap::new(); + for line in text.split('\n') { + let start = line.as_ptr() as usize - text.as_ptr() as usize; + map.insert(map.len() as u32, start..start + line.len()); + } + map +} + +impl State<'_> { + async fn with_file_and_range( + &self, + params: &TextDocumentPositionParams, + action: impl FnOnce(&FileInfo, Range) -> T, + ) -> Option { + let info = self.files.read().await; + let Some(info) = info.get(params.text_document.uri.strip_header()) else { + return None; + }; + let Some(range) = info.line_ranges.get(¶ms.position.line) else { + return None; + }; + Some(action(info, range.clone())) + } +} + +fn make_diagnostics(diag: &leaf_compiler::diagnostics::Diagnostic) -> Vec { + vec![Diagnostic { + range: diag.position.lsp_range(), + severity: Some(match diag.kind { + leaf_compiler::diagnostics::Kind::Info => DiagnosticSeverity::INFORMATION, + leaf_compiler::diagnostics::Kind::Warning => DiagnosticSeverity::WARNING, + leaf_compiler::diagnostics::Kind::Error => DiagnosticSeverity::ERROR, + }), + code: Some(NumberOrString::Number(diag.code as i32)), + code_description: None, + source: Some("Leaf compiler".into()), + message: diag.message.clone(), + related_information: None, + tags: None, + data: None, + }] +} diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 6ff1a7d..2495c1e 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -1,4 +1,4 @@ -use arcstr::ArcStr; +pub use arcstr::ArcStr; pub use parser::{compilation_unit as parse, *}; use std::{fmt::Debug, path::PathBuf};