Merge branch 'master' into pull-diagnostics

pull/11315/head
Sofus Addington 4 months ago
commit d8fd20482b

230
Cargo.lock generated

@ -136,9 +136,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.1.6" version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@ -272,7 +272,7 @@ dependencies = [
"filedescriptor", "filedescriptor",
"futures-core", "futures-core",
"libc", "libc",
"mio", "mio 0.8.11",
"parking_lot", "parking_lot",
"signal-hook", "signal-hook",
"signal-hook-mio", "signal-hook-mio",
@ -541,9 +541,9 @@ checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
[[package]] [[package]]
name = "gix" name = "gix"
version = "0.63.0" version = "0.64.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "984c5018adfa7a4536ade67990b3ebc6e11ab57b3d6cd9968de0947ca99b4b06" checksum = "d78414d29fcc82329080166077e0f7689f4016551fdb334d787c3d040fe2634f"
dependencies = [ dependencies = [
"gix-actor", "gix-actor",
"gix-attributes", "gix-attributes",
@ -584,16 +584,15 @@ dependencies = [
"gix-validate", "gix-validate",
"gix-worktree", "gix-worktree",
"once_cell", "once_cell",
"parking_lot",
"smallvec", "smallvec",
"thiserror", "thiserror",
] ]
[[package]] [[package]]
name = "gix-actor" name = "gix-actor"
version = "0.31.2" version = "0.31.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d69c59d392c7e6c94385b6fd6089d6df0fe945f32b4357687989f3aee253cd7f" checksum = "a0e454357e34b833cc3a00b6efbbd3dd4d18b24b9fb0c023876ec2645e8aa3f2"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-date", "gix-date",
@ -605,9 +604,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-attributes" name = "gix-attributes"
version = "0.22.2" version = "0.22.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eefb48f42eac136a4a0023f49a54ec31be1c7a9589ed762c45dcb9b953f7ecc8" checksum = "e37ce99c7e81288c28b703641b6d5d119aacc45c1a6b247156e6249afa486257"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-glob", "gix-glob",
@ -640,9 +639,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-command" name = "gix-command"
version = "0.3.7" version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c22e086314095c43ffe5cdc5c0922d5439da4fd726f3b0438c56147c34dc225" checksum = "0d76867867da891cbe32021ad454e8cae90242f6afb06762e4dd0d357afd1d7b"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-path", "gix-path",
@ -652,9 +651,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-commitgraph" name = "gix-commitgraph"
version = "0.24.2" version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7b102311085da4af18823413b5176d7c500fb2272eaf391cfa8635d8bcb12c4" checksum = "133b06f67f565836ec0c473e2116a60fb74f80b6435e21d88013ac0e3c60fc78"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-chunk", "gix-chunk",
@ -666,9 +665,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-config" name = "gix-config"
version = "0.37.0" version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53fafe42957e11d98e354a66b6bd70aeea00faf2f62dd11164188224a507c840" checksum = "28f53fd03d1bf09ebcc2c8654f08969439c4556e644ca925f27cf033bc43e658"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-config-value", "gix-config-value",
@ -687,9 +686,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-config-value" name = "gix-config-value"
version = "0.14.6" version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbd06203b1a9b33a78c88252a625031b094d9e1b647260070c25b09910c0a804" checksum = "b328997d74dd15dc71b2773b162cb4af9a25c424105e4876e6d0686ab41c383e"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"bstr", "bstr",
@ -700,9 +699,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-date" name = "gix-date"
version = "0.8.6" version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "367ee9093b0c2b04fd04c5c7c8b6a1082713534eab537597ae343663a518fa99" checksum = "9eed6931f21491ee0aeb922751bd7ec97b4b2fe8fbfedcb678e2a2dce5f3b8c0"
dependencies = [ dependencies = [
"bstr", "bstr",
"itoa", "itoa",
@ -712,9 +711,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-diff" name = "gix-diff"
version = "0.44.0" version = "0.44.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40b9bd8b2d07b6675a840b56a6c177d322d45fa082672b0dad8f063b25baf0a4" checksum = "1996d5c8a305b59709467d80617c9fde48d9d75fd1f4179ea970912630886c9d"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-command", "gix-command",
@ -732,9 +731,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-dir" name = "gix-dir"
version = "0.5.0" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60c99f8c545abd63abe541d20ab6cda347de406c0a3f1c80aadc12d9b0e94974" checksum = "0c975679aa00dd2d757bfd3ddb232e8a188c0094c3306400575a0813858b1365"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-discover", "gix-discover",
@ -752,9 +751,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-discover" name = "gix-discover"
version = "0.32.0" version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc27c699b63da66b50d50c00668bc0b7e90c3a382ef302865e891559935f3dbf" checksum = "67662731cec3cb31ba3ed2463809493f76d8e5d6c6d245de8b0560438c13450e"
dependencies = [ dependencies = [
"bstr", "bstr",
"dunce", "dunce",
@ -787,9 +786,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-filter" name = "gix-filter"
version = "0.11.2" version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00ce6ea5ac8fca7adbc63c48a1b9e0492c222c386aa15f513405f1003f2f4ab2" checksum = "e6547738da28275f4dff4e9f3a0f28509f53f94dd6bd822733c91cb306bca61a"
dependencies = [ dependencies = [
"bstr", "bstr",
"encoding_rs", "encoding_rs",
@ -808,9 +807,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-fs" name = "gix-fs"
version = "0.11.0" version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f78f7d6dcda7a5809efd73a33b145e3dce7421c460df21f32126f9732736b0c" checksum = "6adf99c27cdf17b1c4d77680c917e0d94d8783d4e1c73d3be0d1d63107163d7a"
dependencies = [ dependencies = [
"fastrand", "fastrand",
"gix-features", "gix-features",
@ -819,9 +818,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-glob" name = "gix-glob"
version = "0.16.2" version = "0.16.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "682bdc43cb3c00dbedfcc366de2a849b582efd8d886215dbad2ea662ec156bb5" checksum = "fa7df15afa265cc8abe92813cd354d522f1ac06b29ec6dfa163ad320575cb447"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"bstr", "bstr",
@ -852,9 +851,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-ignore" name = "gix-ignore"
version = "0.11.2" version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "640dbeb4f5829f9fc14d31f654a34a0350e43a24e32d551ad130d99bf01f63f1" checksum = "5e6afb8f98e314d4e1adc822449389ada863c174b5707cedd327d67b84dba527"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-glob", "gix-glob",
@ -865,9 +864,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-index" name = "gix-index"
version = "0.33.0" version = "0.33.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d8c5a5f1c58edcbc5692b174cda2703aba82ed17d7176ff4c1752eb48b1b167" checksum = "9a9a44eb55bd84bb48f8a44980e951968ced21e171b22d115d1cdcef82a7d73f"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"bstr", "bstr",
@ -915,9 +914,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-object" name = "gix-object"
version = "0.42.2" version = "0.42.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fe2dc4a41191c680c942e6ebd630c8107005983c4679214fdb1007dcf5ae1df" checksum = "25da2f46b4e7c2fa7b413ce4dffb87f69eaf89c2057e386491f4c55cadbfe386"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-actor", "gix-actor",
@ -934,9 +933,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-odb" name = "gix-odb"
version = "0.61.0" version = "0.61.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e92b9790e2c919166865d0825b26cc440a387c175bed1b43a2fa99c0e9d45e98" checksum = "20d384fe541d93d8a3bb7d5d5ef210780d6df4f50c4e684ccba32665a5e3bc9b"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"gix-date", "gix-date",
@ -954,9 +953,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-pack" name = "gix-pack"
version = "0.51.0" version = "0.51.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a8da51212dbff944713edb2141ed7e002eea326b8992070374ce13a6cb610b3" checksum = "3e0594491fffe55df94ba1c111a6566b7f56b3f8d2e1efc750e77d572f5f5229"
dependencies = [ dependencies = [
"clru", "clru",
"gix-chunk", "gix-chunk",
@ -965,9 +964,7 @@ dependencies = [
"gix-hashtable", "gix-hashtable",
"gix-object", "gix-object",
"gix-path", "gix-path",
"gix-tempfile",
"memmap2", "memmap2",
"parking_lot",
"smallvec", "smallvec",
"thiserror", "thiserror",
] ]
@ -986,9 +983,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-path" name = "gix-path"
version = "0.10.7" version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23623cf0f475691a6d943f898c4d0b89f5c1a2a64d0f92bce0e0322ee6528783" checksum = "8d23d5bbda31344d8abc8de7c075b3cf26e5873feba7c4a15d916bce67382bd9"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-trace", "gix-trace",
@ -999,9 +996,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-pathspec" name = "gix-pathspec"
version = "0.7.5" version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a76cab098dc10ba2d89f634f66bf196dea4d7db4bf10b75c7a9c201c55a2ee19" checksum = "d307d1b8f84dc8386c4aa20ce0cf09242033840e15469a3ecba92f10cfb5c046"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"bstr", "bstr",
@ -1025,12 +1022,11 @@ dependencies = [
[[package]] [[package]]
name = "gix-ref" name = "gix-ref"
version = "0.44.0" version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b36752b448647acd59c9668fdd830b16d07db1e6d9c3b3af105c1605a6e23d9" checksum = "636e96a0a5562715153fee098c217110c33a6f8218f08f4687ff99afde159bb5"
dependencies = [ dependencies = [
"gix-actor", "gix-actor",
"gix-date",
"gix-features", "gix-features",
"gix-fs", "gix-fs",
"gix-hash", "gix-hash",
@ -1047,9 +1043,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-refspec" name = "gix-refspec"
version = "0.23.0" version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dde848865834a54fe4d9b4573f15d0e9a68eaf3d061b42d3ed52b4b8acf880b2" checksum = "6868f8cd2e62555d1f7c78b784bece43ace40dd2a462daf3b588d5416e603f37"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-hash", "gix-hash",
@ -1061,25 +1057,23 @@ dependencies = [
[[package]] [[package]]
name = "gix-revision" name = "gix-revision"
version = "0.27.1" version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e08f8107ed1f93a83bcfbb4c38084c7cb3f6cd849793f1d5eec235f9b13b2b" checksum = "01b13e43c2118c4b0537ddac7d0821ae0dfa90b7b8dbf20c711e153fb749adce"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-date", "gix-date",
"gix-hash", "gix-hash",
"gix-hashtable",
"gix-object", "gix-object",
"gix-revwalk", "gix-revwalk",
"gix-trace",
"thiserror", "thiserror",
] ]
[[package]] [[package]]
name = "gix-revwalk" name = "gix-revwalk"
version = "0.13.1" version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4181db9cfcd6d1d0fd258e91569dbb61f94cb788b441b5294dd7f1167a3e788f" checksum = "1b030ccaab71af141f537e0225f19b9e74f25fefdba0372246b844491cab43e0"
dependencies = [ dependencies = [
"gix-commitgraph", "gix-commitgraph",
"gix-date", "gix-date",
@ -1092,9 +1086,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-sec" name = "gix-sec"
version = "0.10.6" version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fddc27984a643b20dd03e97790555804f98cf07404e0e552c0ad8133266a79a1" checksum = "1547d26fa5693a7f34f05b4a3b59a90890972922172653bcb891ab3f09f436df"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"gix-path", "gix-path",
@ -1104,9 +1098,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-status" name = "gix-status"
version = "0.10.0" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f4373d989713809554d136f51bc7da565adf45c91aa4d86ef6a79801621bfc8" checksum = "83f7b084cb65c3d007ce6bb479755ca13d602ca3cd91c4f08d7e59904de33736"
dependencies = [ dependencies = [
"bstr", "bstr",
"filetime", "filetime",
@ -1121,14 +1115,15 @@ dependencies = [
"gix-path", "gix-path",
"gix-pathspec", "gix-pathspec",
"gix-worktree", "gix-worktree",
"portable-atomic",
"thiserror", "thiserror",
] ]
[[package]] [[package]]
name = "gix-submodule" name = "gix-submodule"
version = "0.11.0" version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "921cd49924ac14b6611b22e5fb7bbba74d8780dc7ad26153304b64d1272460ac" checksum = "0f2e0f69aa00805e39d39ec80472a7e9da20ed5d73318b27925a2cc198e854fd"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-config", "gix-config",
@ -1161,9 +1156,9 @@ checksum = "f924267408915fddcd558e3f37295cc7d6a3e50f8bd8b606cee0808c3915157e"
[[package]] [[package]]
name = "gix-traverse" name = "gix-traverse"
version = "0.39.1" version = "0.39.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f20cb69b63eb3e4827939f42c05b7756e3488ef49c25c412a876691d568ee2a0" checksum = "e499a18c511e71cf4a20413b743b9f5bcf64b3d9e81e9c3c6cd399eae55a8840"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"gix-commitgraph", "gix-commitgraph",
@ -1178,9 +1173,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-url" name = "gix-url"
version = "0.27.3" version = "0.27.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0db829ebdca6180fbe32be7aed393591df6db4a72dbbc0b8369162390954d1cf" checksum = "e2eb9b35bba92ea8f0b5ab406fad3cf6b87f7929aa677ff10aa042c6da621156"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-features", "gix-features",
@ -1213,9 +1208,9 @@ dependencies = [
[[package]] [[package]]
name = "gix-worktree" name = "gix-worktree"
version = "0.34.0" version = "0.34.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53f6b7de83839274022aff92157d7505f23debf739d257984a300a35972ca94e" checksum = "26f7326ebe0b9172220694ea69d344c536009a9b98fb0f9de092c440f3efe7a6"
dependencies = [ dependencies = [
"bstr", "bstr",
"gix-attributes", "gix-attributes",
@ -1393,10 +1388,10 @@ dependencies = [
"globset", "globset",
"helix-core", "helix-core",
"helix-loader", "helix-loader",
"helix-lsp-types",
"helix-parsec", "helix-parsec",
"helix-stdx", "helix-stdx",
"log", "log",
"lsp-types",
"parking_lot", "parking_lot",
"serde", "serde",
"serde_json", "serde_json",
@ -1406,6 +1401,17 @@ dependencies = [
"tokio-stream", "tokio-stream",
] ]
[[package]]
name = "helix-lsp-types"
version = "0.95.1"
dependencies = [
"bitflags 1.3.2",
"serde",
"serde_json",
"serde_repr",
"url",
]
[[package]] [[package]]
name = "helix-parsec" name = "helix-parsec"
version = "24.7.0" version = "24.7.0"
@ -1455,6 +1461,7 @@ dependencies = [
"once_cell", "once_cell",
"open", "open",
"pulldown-cmark", "pulldown-cmark",
"same-file",
"serde", "serde",
"serde_json", "serde_json",
"signal-hook", "signal-hook",
@ -1545,6 +1552,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "hermit-abi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]] [[package]]
name = "home" name = "home"
version = "0.5.9" version = "0.5.9"
@ -1606,9 +1619,9 @@ dependencies = [
[[package]] [[package]]
name = "imara-diff" name = "imara-diff"
version = "0.1.6" version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af13c8ceb376860ff0c6a66d83a8cdd4ecd9e464da24621bbffcd02b49619434" checksum = "fc9da1a252bd44cd341657203722352efc9bc0c847d06ea6d2dc1cd1135e0a01"
dependencies = [ dependencies = [
"ahash", "ahash",
"hashbrown 0.14.5", "hashbrown 0.14.5",
@ -1720,19 +1733,6 @@ version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "lsp-types"
version = "0.95.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e34d33a8e9b006cd3fc4fe69a921affa097bae4bb65f76271f4644f9a334365"
dependencies = [
"bitflags 1.3.2",
"serde",
"serde_json",
"serde_repr",
"url",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.6.3" version = "2.6.3"
@ -1778,6 +1778,18 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "mio"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
dependencies = [
"hermit-abi 0.3.9",
"libc",
"wasi",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "nucleo" name = "nucleo"
version = "0.5.0" version = "0.5.0"
@ -1820,7 +1832,7 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi 0.2.6",
"libc", "libc",
] ]
@ -1906,6 +1918,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "portable-atomic"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265"
[[package]] [[package]]
name = "powerfmt" name = "powerfmt"
version = "0.2.0" version = "0.2.0"
@ -2134,11 +2152,12 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.120" version = "1.0.121"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr",
"ryu", "ryu",
"serde", "serde",
] ]
@ -2156,9 +2175,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_spanned" name = "serde_spanned"
version = "0.6.6" version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
dependencies = [ dependencies = [
"serde", "serde",
] ]
@ -2192,7 +2211,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
dependencies = [ dependencies = [
"libc", "libc",
"mio", "mio 0.8.11",
"signal-hook", "signal-hook",
] ]
@ -2422,28 +2441,27 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.38.1" version = "1.39.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df" checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes", "bytes",
"libc", "libc",
"mio", "mio 1.0.1",
"num_cpus",
"parking_lot", "parking_lot",
"pin-project-lite", "pin-project-lite",
"signal-hook-registry", "signal-hook-registry",
"socket2", "socket2",
"tokio-macros", "tokio-macros",
"windows-sys 0.48.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
name = "tokio-macros" name = "tokio-macros"
version = "2.3.0" version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2463,9 +2481,9 @@ dependencies = [
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.8.15" version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28" checksum = "81967dd0dd2c1ab0bc3468bd7caecc32b8a4aa47d0c8c695d8c2b2108168d62c"
dependencies = [ dependencies = [
"serde", "serde",
"serde_spanned", "serde_spanned",
@ -2475,18 +2493,18 @@ dependencies = [
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.6.6" version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db"
dependencies = [ dependencies = [
"serde", "serde",
] ]
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.22.16" version = "0.22.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788" checksum = "8d9f8729f5aea9562aac1cc0441f5d6de3cff1ee0c5d67293eeca5eb36ee7c16"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"serde", "serde",

@ -5,6 +5,7 @@ members = [
"helix-view", "helix-view",
"helix-term", "helix-term",
"helix-tui", "helix-tui",
"helix-lsp-types",
"helix-lsp", "helix-lsp",
"helix-event", "helix-event",
"helix-dap", "helix-dap",

@ -10,6 +10,7 @@
- [Surround](./surround.md) - [Surround](./surround.md)
- [Textobjects](./textobjects.md) - [Textobjects](./textobjects.md)
- [Syntax aware motions](./syntax-aware-motions.md) - [Syntax aware motions](./syntax-aware-motions.md)
- [Pickers](./pickers.md)
- [Keymap](./keymap.md) - [Keymap](./keymap.md)
- [Commands](./commands.md) - [Commands](./commands.md)
- [Language support](./lang-support.md) - [Language support](./lang-support.md)

@ -69,7 +69,7 @@
| glsl | ✓ | ✓ | ✓ | | | glsl | ✓ | ✓ | ✓ | |
| gn | ✓ | | | | | gn | ✓ | | | |
| go | ✓ | ✓ | ✓ | `gopls`, `golangci-lint-langserver` | | go | ✓ | ✓ | ✓ | `gopls`, `golangci-lint-langserver` |
| godot-resource | ✓ | | | | | godot-resource | ✓ | | | |
| gomod | ✓ | | | `gopls` | | gomod | ✓ | | | `gopls` |
| gotmpl | ✓ | | | `gopls` | | gotmpl | ✓ | | | `gopls` |
| gowork | ✓ | | | `gopls` | | gowork | ✓ | | | `gopls` |
@ -96,6 +96,7 @@
| java | ✓ | ✓ | ✓ | `jdtls` | | java | ✓ | ✓ | ✓ | `jdtls` |
| javascript | ✓ | ✓ | ✓ | `typescript-language-server` | | javascript | ✓ | ✓ | ✓ | `typescript-language-server` |
| jinja | ✓ | | | | | jinja | ✓ | | | |
| jjdescription | ✓ | | | |
| jsdoc | ✓ | | | | | jsdoc | ✓ | | | |
| json | ✓ | ✓ | ✓ | `vscode-json-language-server` | | json | ✓ | ✓ | ✓ | `vscode-json-language-server` |
| json5 | ✓ | | | | | json5 | ✓ | | | |

@ -436,6 +436,8 @@ you to selectively add search terms to your selections.
## Picker ## Picker
Keys to use within picker. Remapping currently not supported. Keys to use within picker. Remapping currently not supported.
See the documentation page on [pickers](./pickers.md) for more info.
[Prompt](#prompt) keybinds also work in pickers, except where they conflict with picker keybinds.
| Key | Description | | Key | Description |
| ----- | ------------- | | ----- | ------------- |

@ -0,0 +1,11 @@
## Using pickers
Helix has a variety of pickers, which are interactive windows used to select various kinds of items. These include a file picker, global search picker, and more. Most pickers are accessed via keybindings in [space mode](./keymap.md#space-mode). Pickers have their own [keymap](./keymap.md#picker) for navigation.
### Filtering Picker Results
Most pickers perform fuzzy matching using [fzf syntax](https://github.com/junegunn/fzf?tab=readme-ov-file#search-syntax). Two exceptions are the global search picker, which uses regex, and the workspace symbol picker, which passes search terms to the LSP. Note that OR operations (`|`) are not currently supported.
If a picker shows multiple columns, you may apply the filter to a specific column by prefixing the column name with `%`. Column names can be shortened to any prefix, so `%p`, `%pa` or `%pat` all mean the same as `%path`. For example, a query of `helix %p .toml !lang` in the global search picker searches for the term "helix" within files with paths ending in ".toml" but not including "lang".
You can insert the contents of a [register](./registers.md) using `Ctrl-r` followed by a register name. For example, one could insert the currently selected text using `Ctrl-r`-`.`, or the directory of the current file using `Ctrl-r`-`%` followed by `Ctrl-w` to remove the last path section. The global search picker will use the contents of the [search register](./registers.md#default-registers) if you press `Enter` without typing a filter. For example, pressing `*`-`Space-/`-`Enter` will start a global search for the currently selected text.

@ -293,12 +293,13 @@ These scopes are used for theming the editor interface:
| `ui.statusline.select` | Statusline mode during select mode ([only if `editor.color-modes` is enabled][editor-section]) | | `ui.statusline.select` | Statusline mode during select mode ([only if `editor.color-modes` is enabled][editor-section]) |
| `ui.statusline.separator` | Separator character in statusline | | `ui.statusline.separator` | Separator character in statusline |
| `ui.bufferline` | Style for the buffer line | | `ui.bufferline` | Style for the buffer line |
| `ui.bufferline.active` | Style for the active buffer in buffer line | | `ui.bufferline.active` | Style for the active buffer in buffer line |
| `ui.bufferline.background` | Style for bufferline background | | `ui.bufferline.background` | Style for bufferline background |
| `ui.popup` | Documentation popups (e.g. Space + k) | | `ui.popup` | Documentation popups (e.g. Space + k) |
| `ui.popup.info` | Prompt for multiple key options | | `ui.popup.info` | Prompt for multiple key options |
| `ui.picker.header` | Column names in pickers with multiple columns | | `ui.picker.header` | Header row area in pickers with multiple columns |
| `ui.picker.header.active` | The column name in pickers with multiple columns where the cursor is entering into. | | `ui.picker.header.column` | Column names in pickers with multiple columns |
| `ui.picker.header.column.active` | The column name in pickers with multiple columns where the cursor is entering into. |
| `ui.window` | Borderlines separating splits | | `ui.window` | Borderlines separating splits |
| `ui.help` | Description box for commands | | `ui.help` | Description box for commands |
| `ui.text` | Default text style, command prompts, popup text, etc. | | `ui.text` | Default text style, command prompts, popup text, etc. |

@ -2,23 +2,31 @@
# Bash completion script for Helix editor # Bash completion script for Helix editor
_hx() { _hx() {
# $1 command name local cur prev languages
# $2 word being completed COMPREPLY=()
# $3 word preceding cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD - 1]}"
case "$3" in case "$prev" in
-g | --grammar) -g | --grammar)
COMPREPLY="$(compgen -W 'fetch build' -- $2)" COMPREPLY=($(compgen -W 'fetch build' -- "$cur"))
;; return 0
--health) ;;
local languages=$(hx --health |tail -n '+7' |awk '{print $1}' |sed 's/\x1b\[[0-9;]*m//g') --health)
COMPREPLY="$(compgen -W """$languages""" -- $2)" languages=$(hx --health | tail -n '+7' | awk '{print $1}' | sed 's/\x1b\[[0-9;]*m//g')
;; COMPREPLY=($(compgen -W """$languages""" -- "$cur"))
*) return 0
COMPREPLY="$(compgen -fd -W "-h --help --tutor -V --version -v -vv -vvv --health -g --grammar --vsplit --hsplit -c --config --log" -- """$2""")" ;;
;; esac
esac
local IFS=$'\n' case "$2" in
COMPREPLY=($COMPREPLY) -*)
COMPREPLY=($(compgen -W "-h --help --tutor -V --version -v -vv -vvv --health -g --grammar --vsplit --hsplit -c --config --log" -- """$2"""))
return 0
;;
*)
COMPREPLY=($(compgen -fd -- """$2"""))
return 0
;;
esac
} && complete -o filenames -F _hx hx } && complete -o filenames -F _hx hx

@ -4,7 +4,11 @@
# so it has not been specified here and will not be proposed in the autocompletion of Nushell. # so it has not been specified here and will not be proposed in the autocompletion of Nushell.
# The help message won't be overriden though, so it will still be present here # The help message won't be overriden though, so it will still be present here
def health_categories [] { ["all", "clipboard", "languages"] } def health_categories [] {
let languages = ^hx --health languages | detect columns | get Language | filter { $in != null }
let completions = [ "all", "clipboard", "languages" ] | append $languages
return $completions
}
def grammar_categories [] { ["fetch", "build"] } def grammar_categories [] { ["fetch", "build"] }
@ -12,7 +16,7 @@ def grammar_categories [] { ["fetch", "build"] }
export extern hx [ export extern hx [
--help(-h), # Prints help information --help(-h), # Prints help information
--tutor, # Loads the tutorial --tutor, # Loads the tutorial
--health: string@health_categories = "all", # Checks for potential errors in editor setup --health: string@health_categories, # Checks for potential errors in editor setup
--grammar(-g): string@grammar_categories, # Fetches or builds tree-sitter grammars listed in `languages.toml` --grammar(-g): string@grammar_categories, # Fetches or builds tree-sitter grammars listed in `languages.toml`
--config(-c): glob, # Specifies a file to use for configuration --config(-c): glob, # Specifies a file to use for configuration
-v, # Increases logging verbosity each use for up to 3 times -v, # Increases logging verbosity each use for up to 3 times

@ -53,6 +53,10 @@ Existing tests can be used as examples. Helpers can be found in
[helpers.rs][helpers.rs]. The log level can be set with the `HELIX_LOG_LEVEL` [helpers.rs][helpers.rs]. The log level can be set with the `HELIX_LOG_LEVEL`
environment variable, e.g. `HELIX_LOG_LEVEL=debug cargo integration-test`. environment variable, e.g. `HELIX_LOG_LEVEL=debug cargo integration-test`.
Contributors using MacOS might encounter `Too many open files (os error 24)`
failures while running integration tests. This can be resolved by increasing
the default value (e.g. to `10240` from `256`) by running `ulimit -n 10240`.
## Minimum Stable Rust Version (MSRV) Policy ## Minimum Stable Rust Version (MSRV) Policy
Helix follows the MSRV of Firefox. Helix follows the MSRV of Firefox.

@ -1,13 +1,14 @@
| Crate | Description | | Crate | Description |
| ----------- | ----------- | | ----------- | ----------- |
| helix-core | Core editing primitives, functional. | | helix-core | Core editing primitives, functional. |
| helix-lsp | Language server client | | helix-lsp | Language server client |
| helix-dap | Debug Adapter Protocol (DAP) client | | helix-lsp-types | Language Server Protocol type definitions |
| helix-loader | Functions for building, fetching, and loading external resources | | helix-dap | Debug Adapter Protocol (DAP) client |
| helix-view | UI abstractions for use in backends, imperative shell. | | helix-loader | Functions for building, fetching, and loading external resources |
| helix-term | Terminal UI | | helix-view | UI abstractions for use in backends, imperative shell. |
| helix-tui | TUI primitives, forked from tui-rs, inspired by Cursive | | helix-term | Terminal UI |
| helix-tui | TUI primitives, forked from tui-rs, inspired by Cursive |
This document contains a high-level overview of Helix internals. This document contains a high-level overview of Helix internals.

@ -23,7 +23,12 @@ ropey = { version = "1.6.1", default-features = false, features = ["simd"] }
smallvec = "1.13" smallvec = "1.13"
smartstring = "1.0.1" smartstring = "1.0.1"
unicode-segmentation = "1.11" unicode-segmentation = "1.11"
unicode-width = "0.1" # unicode-width is changing width definitions
# that both break our logic and disagree with common
# width definitions in terminals, we need to replace it.
# For now lets lock the version to avoid rendering glitches
# when installing without `--locked`
unicode-width = "=0.1.12"
unicode-general-category = "0.6" unicode-general-category = "0.6"
slotmap.workspace = true slotmap.workspace = true
tree-sitter.workspace = true tree-sitter.workspace = true
@ -41,7 +46,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
toml = "0.8" toml = "0.8"
imara-diff = "0.1.6" imara-diff = "0.1.7"
encoding_rs = "0.8" encoding_rs = "0.8"

@ -75,9 +75,9 @@ impl From<(&char, &char)> for Pair {
impl AutoPairs { impl AutoPairs {
/// Make a new AutoPairs set with the given pairs and default conditions. /// Make a new AutoPairs set with the given pairs and default conditions.
pub fn new<'a, V: 'a, A>(pairs: V) -> Self pub fn new<'a, V, A>(pairs: V) -> Self
where where
V: IntoIterator<Item = A>, V: IntoIterator<Item = A> + 'a,
A: Into<Pair>, A: Into<Pair>,
{ {
let mut auto_pairs = HashMap::new(); let mut auto_pairs = HashMap::new();

@ -265,7 +265,7 @@ fn is_first_in_line(node: Node, text: RopeSlice, new_line_byte_pos: Option<usize
/// This is usually constructed in one of 2 ways: /// This is usually constructed in one of 2 ways:
/// - Successively add indent captures to get the (added) indent from a single line /// - Successively add indent captures to get the (added) indent from a single line
/// - Successively add the indent results for each line /// - Successively add the indent results for each line
/// The string that this indentation defines starts with the string contained in the align field (unless it is None), followed by: /// The string that this indentation defines starts with the string contained in the align field (unless it is None), followed by:
/// - max(0, indent - outdent) tabs, if tabs are used for indentation /// - max(0, indent - outdent) tabs, if tabs are used for indentation
/// - max(0, indent - outdent)*indent_width spaces, if spaces are used for indentation /// - max(0, indent - outdent)*indent_width spaces, if spaces are used for indentation
#[derive(Default, Debug, PartialEq, Eq, Clone)] #[derive(Default, Debug, PartialEq, Eq, Clone)]
@ -457,7 +457,7 @@ fn query_indents<'a>(
// Skip matches where not all custom predicates are fulfilled // Skip matches where not all custom predicates are fulfilled
if !query.general_predicates(m.pattern_index).iter().all(|pred| { if !query.general_predicates(m.pattern_index).iter().all(|pred| {
match pred.operator.as_ref() { match pred.operator.as_ref() {
"not-kind-eq?" => match (pred.args.get(0), pred.args.get(1)) { "not-kind-eq?" => match (pred.args.first(), pred.args.get(1)) {
( (
Some(QueryPredicateArg::Capture(capture_idx)), Some(QueryPredicateArg::Capture(capture_idx)),
Some(QueryPredicateArg::String(kind)), Some(QueryPredicateArg::String(kind)),
@ -473,7 +473,7 @@ fn query_indents<'a>(
} }
}, },
"same-line?" | "not-same-line?" => { "same-line?" | "not-same-line?" => {
match (pred.args.get(0), pred.args.get(1)) { match (pred.args.first(), pred.args.get(1)) {
( (
Some(QueryPredicateArg::Capture(capt1)), Some(QueryPredicateArg::Capture(capt1)),
Some(QueryPredicateArg::Capture(capt2)) Some(QueryPredicateArg::Capture(capt2))
@ -495,7 +495,7 @@ fn query_indents<'a>(
} }
} }
} }
"one-line?" | "not-one-line?" => match pred.args.get(0) { "one-line?" | "not-one-line?" => match pred.args.first() {
Some(QueryPredicateArg::Capture(capture_idx)) => { Some(QueryPredicateArg::Capture(capture_idx)) => {
let node = m.nodes_for_capture_index(*capture_idx).next(); let node = m.nodes_for_capture_index(*capture_idx).next();
@ -786,6 +786,7 @@ fn init_indent_query<'a, 'b>(
/// - The line after the node. This is defined by: /// - The line after the node. This is defined by:
/// - The scope `tail`. /// - The scope `tail`.
/// - The scope `all` if this node is not the first node on its line. /// - The scope `all` if this node is not the first node on its line.
///
/// Intuitively, `all` applies to everything contained in this node while `tail` applies to everything except for the first line of the node. /// Intuitively, `all` applies to everything contained in this node while `tail` applies to everything except for the first line of the node.
/// The indents from different nodes for the same line are then combined. /// The indents from different nodes for the same line are then combined.
/// The result [Indentation] is simply the sum of the [Indentation] for all lines. /// The result [Indentation] is simply the sum of the [Indentation] for all lines.

@ -1027,9 +1027,10 @@ impl Loader {
match capture { match capture {
InjectionLanguageMarker::Name(string) => self.language_config_for_name(string), InjectionLanguageMarker::Name(string) => self.language_config_for_name(string),
InjectionLanguageMarker::Filename(file) => self.language_config_for_file_name(file), InjectionLanguageMarker::Filename(file) => self.language_config_for_file_name(file),
InjectionLanguageMarker::Shebang(shebang) => { InjectionLanguageMarker::Shebang(shebang) => self
self.language_config_for_language_id(shebang) .language_config_ids_by_shebang
} .get(shebang)
.and_then(|&id| self.language_configs.get(id).cloned()),
} }
} }
@ -1432,8 +1433,11 @@ impl Syntax {
// The `captures` iterator borrows the `Tree` and the `QueryCursor`, which // The `captures` iterator borrows the `Tree` and the `QueryCursor`, which
// prevents them from being moved. But both of these values are really just // prevents them from being moved. But both of these values are really just
// pointers, so it's actually ok to move them. // pointers, so it's actually ok to move them.
let cursor_ref = let cursor_ref = unsafe {
unsafe { mem::transmute::<_, &'static mut QueryCursor>(&mut cursor) }; mem::transmute::<&mut tree_sitter::QueryCursor, &mut tree_sitter::QueryCursor>(
&mut cursor,
)
};
// if reusing cursors & no range this resets to whole range // if reusing cursors & no range this resets to whole range
cursor_ref.set_byte_range(range.clone().unwrap_or(0..usize::MAX)); cursor_ref.set_byte_range(range.clone().unwrap_or(0..usize::MAX));
@ -1738,7 +1742,7 @@ pub(crate) fn generate_edits(
} }
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::{iter, mem, ops, str, usize}; use std::{iter, mem, ops, str};
use tree_sitter::{ use tree_sitter::{
Language as Grammar, Node, Parser, Point, Query, QueryCaptures, QueryCursor, QueryError, Language as Grammar, Node, Parser, Point, Query, QueryCaptures, QueryCursor, QueryError,
QueryMatch, Range, TextProvider, Tree, QueryMatch, Range, TextProvider, Tree,

@ -204,7 +204,7 @@ impl<'a> TreeCursor<'a> {
self.injection_ranges[start_idx..] self.injection_ranges[start_idx..]
.iter() .iter()
.take_while(|range| range.start < end) .take_while(|range| range.start < end || range.depth > 1)
.find_map(|range| (range.start <= start).then_some(range.layer_id)) .find_map(|range| (range.start <= start).then_some(range.layer_id))
.unwrap_or(self.root) .unwrap_or(self.root)
} }

@ -0,0 +1,176 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "form_urlencoded"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
dependencies = [
"percent-encoding",
]
[[package]]
name = "idna"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "itoa"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
[[package]]
name = "lsp-types"
version = "0.95.1"
dependencies = [
"bitflags",
"serde",
"serde_json",
"serde_repr",
"url",
]
[[package]]
name = "percent-encoding"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]]
name = "proc-macro2"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ryu"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
[[package]]
name = "serde"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serde_repr"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "unicode-bidi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
[[package]]
name = "unicode-ident"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
[[package]]
name = "unicode-normalization"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
[[package]]
name = "url"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
"serde",
]

@ -0,0 +1,34 @@
[package]
name = "helix-lsp-types"
version = "0.95.1"
authors = [
# Original authors
"Markus Westerlind <marwes91@gmail.com>",
"Bruno Medeiros <bruno.do.medeiros@gmail.com>",
# Since forking
"Helix contributors"
]
edition = "2018"
description = "Types for interaction with a language server, using VSCode's Language Server Protocol"
repository = "https://github.com/gluon-lang/lsp-types"
documentation = "https://docs.rs/lsp-types"
readme = "README.md"
keywords = ["language", "server", "lsp", "vscode", "lsif"]
license = "MIT"
[dependencies]
bitflags = "1.0.1"
serde = { version = "1.0.34", features = ["derive"] }
serde_json = "1.0.50"
serde_repr = "0.1"
url = {version = "2.0.0", features = ["serde"]}
[features]
default = []
# Enables proposed LSP extensions.
# NOTE: No semver compatibility is guaranteed for types enabled by this feature.
proposed = []

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2016 Markus Westerlind
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

@ -0,0 +1,3 @@
# Helix's `lsp-types`
This is a fork of the [`lsp-types`](https://crates.io/crates/lsp-types) crate ([`gluon-lang/lsp-types`](https://github.com/gluon-lang/lsp-types)) taken at version v0.95.1 (commit [3e6daee](https://github.com/gluon-lang/lsp-types/commit/3e6daee771d14db4094a554b8d03e29c310dfcbe)). This fork focuses usability improvements that make the types easier to work with for the Helix codebase. For example the URL type - the `uri` crate at this version of `lsp-types` - will be replaced with a wrapper around a string.

@ -0,0 +1,127 @@
use serde::{Deserialize, Serialize};
use serde_json::Value;
use url::Url;
use crate::{
DynamicRegistrationClientCapabilities, PartialResultParams, Range, SymbolKind, SymbolTag,
TextDocumentPositionParams, WorkDoneProgressOptions, WorkDoneProgressParams,
};
pub type CallHierarchyClientCapabilities = DynamicRegistrationClientCapabilities;
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize, Copy)]
#[serde(rename_all = "camelCase")]
pub struct CallHierarchyOptions {
#[serde(flatten)]
pub work_done_progress_options: WorkDoneProgressOptions,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize, Copy)]
#[serde(untagged)]
pub enum CallHierarchyServerCapability {
Simple(bool),
Options(CallHierarchyOptions),
}
impl From<CallHierarchyOptions> for CallHierarchyServerCapability {
fn from(from: CallHierarchyOptions) -> Self {
Self::Options(from)
}
}
impl From<bool> for CallHierarchyServerCapability {
fn from(from: bool) -> Self {
Self::Simple(from)
}
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CallHierarchyPrepareParams {
#[serde(flatten)]
pub text_document_position_params: TextDocumentPositionParams,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
#[serde(rename_all = "camelCase")]
pub struct CallHierarchyItem {
/// The name of this item.
pub name: String,
/// The kind of this item.
pub kind: SymbolKind,
/// Tags for this item.
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<SymbolTag>>,
/// More detail for this item, e.g. the signature of a function.
#[serde(skip_serializing_if = "Option::is_none")]
pub detail: Option<String>,
/// The resource identifier of this item.
pub uri: Url,
/// The range enclosing this symbol not including leading/trailing whitespace but everything else, e.g. comments and code.
pub range: Range,
/// The range that should be selected and revealed when this symbol is being picked, e.g. the name of a function.
/// Must be contained by the [`range`](#CallHierarchyItem.range).
pub selection_range: Range,
/// A data entry field that is preserved between a call hierarchy prepare and incoming calls or outgoing calls requests.
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Value>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CallHierarchyIncomingCallsParams {
pub item: CallHierarchyItem,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
}
/// Represents an incoming call, e.g. a caller of a method or constructor.
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
#[serde(rename_all = "camelCase")]
pub struct CallHierarchyIncomingCall {
/// The item that makes the call.
pub from: CallHierarchyItem,
/// The range at which at which the calls appears. This is relative to the caller
/// denoted by [`this.from`](#CallHierarchyIncomingCall.from).
pub from_ranges: Vec<Range>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CallHierarchyOutgoingCallsParams {
pub item: CallHierarchyItem,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
}
/// Represents an outgoing call, e.g. calling a getter from a method or a method from a constructor etc.
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
#[serde(rename_all = "camelCase")]
pub struct CallHierarchyOutgoingCall {
/// The item that is called.
pub to: CallHierarchyItem,
/// The range at which this item is called. This is the range relative to the caller, e.g the item
/// passed to [`provideCallHierarchyOutgoingCalls`](#CallHierarchyItemProvider.provideCallHierarchyOutgoingCalls)
/// and not [`this.to`](#CallHierarchyOutgoingCall.to).
pub from_ranges: Vec<Range>,
}

@ -0,0 +1,395 @@
use crate::{
Command, Diagnostic, PartialResultParams, Range, TextDocumentIdentifier,
WorkDoneProgressOptions, WorkDoneProgressParams, WorkspaceEdit,
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::borrow::Cow;
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum CodeActionProviderCapability {
Simple(bool),
Options(CodeActionOptions),
}
impl From<CodeActionOptions> for CodeActionProviderCapability {
fn from(from: CodeActionOptions) -> Self {
Self::Options(from)
}
}
impl From<bool> for CodeActionProviderCapability {
fn from(from: bool) -> Self {
Self::Simple(from)
}
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CodeActionClientCapabilities {
///
/// This capability supports dynamic registration.
///
#[serde(skip_serializing_if = "Option::is_none")]
pub dynamic_registration: Option<bool>,
/// The client support code action literals as a valid
/// response of the `textDocument/codeAction` request.
#[serde(skip_serializing_if = "Option::is_none")]
pub code_action_literal_support: Option<CodeActionLiteralSupport>,
/// Whether code action supports the `isPreferred` property.
///
/// @since 3.15.0
#[serde(skip_serializing_if = "Option::is_none")]
pub is_preferred_support: Option<bool>,
/// Whether code action supports the `disabled` property.
///
/// @since 3.16.0
#[serde(skip_serializing_if = "Option::is_none")]
pub disabled_support: Option<bool>,
/// Whether code action supports the `data` property which is
/// preserved between a `textDocument/codeAction` and a
/// `codeAction/resolve` request.
///
/// @since 3.16.0
#[serde(skip_serializing_if = "Option::is_none")]
pub data_support: Option<bool>,
/// Whether the client supports resolving additional code action
/// properties via a separate `codeAction/resolve` request.
///
/// @since 3.16.0
#[serde(skip_serializing_if = "Option::is_none")]
pub resolve_support: Option<CodeActionCapabilityResolveSupport>,
/// Whether the client honors the change annotations in
/// text edits and resource operations returned via the
/// `CodeAction#edit` property by for example presenting
/// the workspace edit in the user interface and asking
/// for confirmation.
///
/// @since 3.16.0
#[serde(skip_serializing_if = "Option::is_none")]
pub honors_change_annotations: Option<bool>,
}
/// Whether the client supports resolving additional code action
/// properties via a separate `codeAction/resolve` request.
///
/// @since 3.16.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CodeActionCapabilityResolveSupport {
/// The properties that a client can resolve lazily.
pub properties: Vec<String>,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CodeActionLiteralSupport {
/// The code action kind is support with the following value set.
pub code_action_kind: CodeActionKindLiteralSupport,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CodeActionKindLiteralSupport {
/// The code action kind values the client supports. When this
/// property exists the client also guarantees that it will
/// handle values outside its set gracefully and falls back
/// to a default value when unknown.
pub value_set: Vec<String>,
}
/// Params for the CodeActionRequest
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CodeActionParams {
/// The document in which the command was invoked.
pub text_document: TextDocumentIdentifier,
/// The range for which the command was invoked.
pub range: Range,
/// Context carrying additional information.
pub context: CodeActionContext,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
}
/// response for CodeActionRequest
pub type CodeActionResponse = Vec<CodeActionOrCommand>;
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum CodeActionOrCommand {
Command(Command),
CodeAction(CodeAction),
}
impl From<Command> for CodeActionOrCommand {
fn from(command: Command) -> Self {
CodeActionOrCommand::Command(command)
}
}
impl From<CodeAction> for CodeActionOrCommand {
fn from(action: CodeAction) -> Self {
CodeActionOrCommand::CodeAction(action)
}
}
#[derive(Debug, Eq, PartialEq, Hash, PartialOrd, Clone, Deserialize, Serialize)]
pub struct CodeActionKind(Cow<'static, str>);
impl CodeActionKind {
/// Empty kind.
pub const EMPTY: CodeActionKind = CodeActionKind::new("");
/// Base kind for quickfix actions: 'quickfix'
pub const QUICKFIX: CodeActionKind = CodeActionKind::new("quickfix");
/// Base kind for refactoring actions: 'refactor'
pub const REFACTOR: CodeActionKind = CodeActionKind::new("refactor");
/// Base kind for refactoring extraction actions: 'refactor.extract'
///
/// Example extract actions:
///
/// - Extract method
/// - Extract function
/// - Extract variable
/// - Extract interface from class
/// - ...
pub const REFACTOR_EXTRACT: CodeActionKind = CodeActionKind::new("refactor.extract");
/// Base kind for refactoring inline actions: 'refactor.inline'
///
/// Example inline actions:
///
/// - Inline function
/// - Inline variable
/// - Inline constant
/// - ...
pub const REFACTOR_INLINE: CodeActionKind = CodeActionKind::new("refactor.inline");
/// Base kind for refactoring rewrite actions: 'refactor.rewrite'
///
/// Example rewrite actions:
///
/// - Convert JavaScript function to class
/// - Add or remove parameter
/// - Encapsulate field
/// - Make method static
/// - Move method to base class
/// - ...
pub const REFACTOR_REWRITE: CodeActionKind = CodeActionKind::new("refactor.rewrite");
/// Base kind for source actions: `source`
///
/// Source code actions apply to the entire file.
pub const SOURCE: CodeActionKind = CodeActionKind::new("source");
/// Base kind for an organize imports source action: `source.organizeImports`
pub const SOURCE_ORGANIZE_IMPORTS: CodeActionKind =
CodeActionKind::new("source.organizeImports");
/// Base kind for a 'fix all' source action: `source.fixAll`.
///
/// 'Fix all' actions automatically fix errors that have a clear fix that
/// do not require user input. They should not suppress errors or perform
/// unsafe fixes such as generating new types or classes.
///
/// @since 3.17.0
pub const SOURCE_FIX_ALL: CodeActionKind = CodeActionKind::new("source.fixAll");
pub const fn new(tag: &'static str) -> Self {
CodeActionKind(Cow::Borrowed(tag))
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<String> for CodeActionKind {
fn from(from: String) -> Self {
CodeActionKind(Cow::from(from))
}
}
impl From<&'static str> for CodeActionKind {
fn from(from: &'static str) -> Self {
CodeActionKind::new(from)
}
}
#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CodeAction {
/// A short, human-readable, title for this code action.
pub title: String,
/// The kind of the code action.
/// Used to filter code actions.
#[serde(skip_serializing_if = "Option::is_none")]
pub kind: Option<CodeActionKind>,
/// The diagnostics that this code action resolves.
#[serde(skip_serializing_if = "Option::is_none")]
pub diagnostics: Option<Vec<Diagnostic>>,
/// The workspace edit this code action performs.
#[serde(skip_serializing_if = "Option::is_none")]
pub edit: Option<WorkspaceEdit>,
/// A command this code action executes. If a code action
/// provides an edit and a command, first the edit is
/// executed and then the command.
#[serde(skip_serializing_if = "Option::is_none")]
pub command: Option<Command>,
/// Marks this as a preferred action. Preferred actions are used by the `auto fix` command and can be targeted
/// by keybindings.
/// A quick fix should be marked preferred if it properly addresses the underlying error.
/// A refactoring should be marked preferred if it is the most reasonable choice of actions to take.
///
/// @since 3.15.0
#[serde(skip_serializing_if = "Option::is_none")]
pub is_preferred: Option<bool>,
/// Marks that the code action cannot currently be applied.
///
/// Clients should follow the following guidelines regarding disabled code actions:
///
/// - Disabled code actions are not shown in automatic
/// [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action)
/// code action menu.
///
/// - Disabled actions are shown as faded out in the code action menu when the user request
/// a more specific type of code action, such as refactorings.
///
/// - If the user has a keybinding that auto applies a code action and only a disabled code
/// actions are returned, the client should show the user an error message with `reason`
/// in the editor.
///
/// @since 3.16.0
#[serde(skip_serializing_if = "Option::is_none")]
pub disabled: Option<CodeActionDisabled>,
/// A data entry field that is preserved on a code action between
/// a `textDocument/codeAction` and a `codeAction/resolve` request.
///
/// @since 3.16.0
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Value>,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CodeActionDisabled {
/// Human readable description of why the code action is currently disabled.
///
/// This is displayed in the code actions UI.
pub reason: String,
}
/// The reason why code actions were requested.
///
/// @since 3.17.0
#[derive(Eq, PartialEq, Clone, Copy, Deserialize, Serialize)]
#[serde(transparent)]
pub struct CodeActionTriggerKind(i32);
lsp_enum! {
impl CodeActionTriggerKind {
/// Code actions were explicitly requested by the user or by an extension.
pub const INVOKED: CodeActionTriggerKind = CodeActionTriggerKind(1);
/// Code actions were requested automatically.
///
/// This typically happens when current selection in a file changes, but can
/// also be triggered when file content changes.
pub const AUTOMATIC: CodeActionTriggerKind = CodeActionTriggerKind(2);
}
}
/// Contains additional diagnostic information about the context in which
/// a code action is run.
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CodeActionContext {
/// An array of diagnostics.
pub diagnostics: Vec<Diagnostic>,
/// Requested kind of actions to return.
///
/// Actions not of this kind are filtered out by the client before being shown. So servers
/// can omit computing them.
#[serde(skip_serializing_if = "Option::is_none")]
pub only: Option<Vec<CodeActionKind>>,
/// The reason why code actions were requested.
///
/// @since 3.17.0
#[serde(skip_serializing_if = "Option::is_none")]
pub trigger_kind: Option<CodeActionTriggerKind>,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct CodeActionOptions {
/// CodeActionKinds that this server may return.
///
/// The list of kinds may be generic, such as `CodeActionKind.Refactor`, or the server
/// may list out every specific kind they provide.
#[serde(skip_serializing_if = "Option::is_none")]
pub code_action_kinds: Option<Vec<CodeActionKind>>,
#[serde(flatten)]
pub work_done_progress_options: WorkDoneProgressOptions,
/// The server provides support to resolve additional
/// information for a code action.
///
/// @since 3.16.0
#[serde(skip_serializing_if = "Option::is_none")]
pub resolve_provider: Option<bool>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::test_serialization;
#[test]
fn test_code_action_response() {
test_serialization(
&vec![
CodeActionOrCommand::Command(Command {
title: "title".to_string(),
command: "command".to_string(),
arguments: None,
}),
CodeActionOrCommand::CodeAction(CodeAction {
title: "title".to_string(),
kind: Some(CodeActionKind::QUICKFIX),
command: None,
diagnostics: None,
edit: None,
is_preferred: None,
..CodeAction::default()
}),
],
r#"[{"title":"title","command":"command"},{"title":"title","kind":"quickfix"}]"#,
)
}
}

@ -0,0 +1,66 @@
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::{
Command, DynamicRegistrationClientCapabilities, PartialResultParams, Range,
TextDocumentIdentifier, WorkDoneProgressParams,
};
pub type CodeLensClientCapabilities = DynamicRegistrationClientCapabilities;
/// Code Lens options.
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize, Copy)]
#[serde(rename_all = "camelCase")]
pub struct CodeLensOptions {
/// Code lens has a resolve provider as well.
#[serde(skip_serializing_if = "Option::is_none")]
pub resolve_provider: Option<bool>,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CodeLensParams {
/// The document to request code lens for.
pub text_document: TextDocumentIdentifier,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
}
/// A code lens represents a command that should be shown along with
/// source text, like the number of references, a way to run tests, etc.
///
/// A code lens is _unresolved_ when no command is associated to it. For performance
/// reasons the creation of a code lens and resolving should be done in two stages.
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CodeLens {
/// The range in which this code lens is valid. Should only span a single line.
pub range: Range,
/// The command this code lens represents.
#[serde(skip_serializing_if = "Option::is_none")]
pub command: Option<Command>,
/// A data entry field that is preserved on a code lens item between
/// a code lens and a code lens resolve request.
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Value>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CodeLensWorkspaceClientCapabilities {
/// Whether the client implementation supports a refresh request sent from the
/// server to the client.
///
/// Note that this event is global and will force the client to refresh all
/// code lenses currently shown. It should be used with absolute care and is
/// useful for situation where a server for example detect a project wide
/// change that requires such a calculation.
#[serde(skip_serializing_if = "Option::is_none")]
pub refresh_support: Option<bool>,
}

@ -0,0 +1,122 @@
use crate::{
DocumentSelector, DynamicRegistrationClientCapabilities, PartialResultParams, Range,
TextDocumentIdentifier, TextEdit, WorkDoneProgressParams,
};
use serde::{Deserialize, Serialize};
pub type DocumentColorClientCapabilities = DynamicRegistrationClientCapabilities;
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ColorProviderOptions {}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct StaticTextDocumentColorProviderOptions {
/// A document selector to identify the scope of the registration. If set to null
/// the document selector provided on the client side will be used.
pub document_selector: Option<DocumentSelector>,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum ColorProviderCapability {
Simple(bool),
ColorProvider(ColorProviderOptions),
Options(StaticTextDocumentColorProviderOptions),
}
impl From<ColorProviderOptions> for ColorProviderCapability {
fn from(from: ColorProviderOptions) -> Self {
Self::ColorProvider(from)
}
}
impl From<StaticTextDocumentColorProviderOptions> for ColorProviderCapability {
fn from(from: StaticTextDocumentColorProviderOptions) -> Self {
Self::Options(from)
}
}
impl From<bool> for ColorProviderCapability {
fn from(from: bool) -> Self {
Self::Simple(from)
}
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentColorParams {
/// The text document
pub text_document: TextDocumentIdentifier,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ColorInformation {
/// The range in the document where this color appears.
pub range: Range,
/// The actual color value for this color range.
pub color: Color,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Copy)]
#[serde(rename_all = "camelCase")]
pub struct Color {
/// The red component of this color in the range [0-1].
pub red: f32,
/// The green component of this color in the range [0-1].
pub green: f32,
/// The blue component of this color in the range [0-1].
pub blue: f32,
/// The alpha component of this color in the range [0-1].
pub alpha: f32,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ColorPresentationParams {
/// The text document.
pub text_document: TextDocumentIdentifier,
/// The color information to request presentations for.
pub color: Color,
/// The range where the color would be inserted. Serves as a context.
pub range: Range,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
}
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize, Default, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ColorPresentation {
/// The label of this color presentation. It will be shown on the color
/// picker header. By default this is also the text that is inserted when selecting
/// this color presentation.
pub label: String,
/// An [edit](#TextEdit) which is applied to a document when selecting
/// this presentation for the color. When `falsy` the [label](#ColorPresentation.label)
/// is used.
#[serde(skip_serializing_if = "Option::is_none")]
pub text_edit: Option<TextEdit>,
/// An optional array of additional [text edits](#TextEdit) that are applied when
/// selecting this color presentation. Edits must not overlap with the main [edit](#ColorPresentation.textEdit) nor with themselves.
#[serde(skip_serializing_if = "Option::is_none")]
pub additional_text_edits: Option<Vec<TextEdit>>,
}

@ -0,0 +1,622 @@
use serde::{Deserialize, Serialize};
use crate::{
Command, Documentation, MarkupKind, PartialResultParams, TagSupport,
TextDocumentPositionParams, TextDocumentRegistrationOptions, TextEdit, WorkDoneProgressOptions,
WorkDoneProgressParams,
};
use crate::Range;
use serde_json::Value;
use std::fmt::Debug;
/// Defines how to interpret the insert text in a completion item
#[derive(Eq, PartialEq, Clone, Copy, Serialize, Deserialize)]
#[serde(transparent)]
pub struct InsertTextFormat(i32);
lsp_enum! {
impl InsertTextFormat {
pub const PLAIN_TEXT: InsertTextFormat = InsertTextFormat(1);
pub const SNIPPET: InsertTextFormat = InsertTextFormat(2);
}
}
/// The kind of a completion entry.
#[derive(Eq, PartialEq, Clone, Copy, Serialize, Deserialize)]
#[serde(transparent)]
pub struct CompletionItemKind(i32);
lsp_enum! {
impl CompletionItemKind {
pub const TEXT: CompletionItemKind = CompletionItemKind(1);
pub const METHOD: CompletionItemKind = CompletionItemKind(2);
pub const FUNCTION: CompletionItemKind = CompletionItemKind(3);
pub const CONSTRUCTOR: CompletionItemKind = CompletionItemKind(4);
pub const FIELD: CompletionItemKind = CompletionItemKind(5);
pub const VARIABLE: CompletionItemKind = CompletionItemKind(6);
pub const CLASS: CompletionItemKind = CompletionItemKind(7);
pub const INTERFACE: CompletionItemKind = CompletionItemKind(8);
pub const MODULE: CompletionItemKind = CompletionItemKind(9);
pub const PROPERTY: CompletionItemKind = CompletionItemKind(10);
pub const UNIT: CompletionItemKind = CompletionItemKind(11);
pub const VALUE: CompletionItemKind = CompletionItemKind(12);
pub const ENUM: CompletionItemKind = CompletionItemKind(13);
pub const KEYWORD: CompletionItemKind = CompletionItemKind(14);
pub const SNIPPET: CompletionItemKind = CompletionItemKind(15);
pub const COLOR: CompletionItemKind = CompletionItemKind(16);
pub const FILE: CompletionItemKind = CompletionItemKind(17);
pub const REFERENCE: CompletionItemKind = CompletionItemKind(18);
pub const FOLDER: CompletionItemKind = CompletionItemKind(19);
pub const ENUM_MEMBER: CompletionItemKind = CompletionItemKind(20);
pub const CONSTANT: CompletionItemKind = CompletionItemKind(21);
pub const STRUCT: CompletionItemKind = CompletionItemKind(22);
pub const EVENT: CompletionItemKind = CompletionItemKind(23);
pub const OPERATOR: CompletionItemKind = CompletionItemKind(24);
pub const TYPE_PARAMETER: CompletionItemKind = CompletionItemKind(25);
}
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CompletionItemCapability {
/// Client supports snippets as insert text.
///
/// A snippet can define tab stops and placeholders with `$1`, `$2`
/// and `${3:foo}`. `$0` defines the final tab stop, it defaults to
/// the end of the snippet. Placeholders with equal identifiers are linked,
/// that is typing in one will update others too.
#[serde(skip_serializing_if = "Option::is_none")]
pub snippet_support: Option<bool>,
/// Client supports commit characters on a completion item.
#[serde(skip_serializing_if = "Option::is_none")]
pub commit_characters_support: Option<bool>,
/// Client supports the follow content formats for the documentation
/// property. The order describes the preferred format of the client.
#[serde(skip_serializing_if = "Option::is_none")]
pub documentation_format: Option<Vec<MarkupKind>>,
/// Client supports the deprecated property on a completion item.
#[serde(skip_serializing_if = "Option::is_none")]
pub deprecated_support: Option<bool>,
/// Client supports the preselect property on a completion item.
#[serde(skip_serializing_if = "Option::is_none")]
pub preselect_support: Option<bool>,
/// Client supports the tag property on a completion item. Clients supporting
/// tags have to handle unknown tags gracefully. Clients especially need to
/// preserve unknown tags when sending a completion item back to the server in
/// a resolve call.
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "TagSupport::deserialize_compat"
)]
pub tag_support: Option<TagSupport<CompletionItemTag>>,
/// Client support insert replace edit to control different behavior if a
/// completion item is inserted in the text or should replace text.
///
/// @since 3.16.0
#[serde(skip_serializing_if = "Option::is_none")]
pub insert_replace_support: Option<bool>,
/// Indicates which properties a client can resolve lazily on a completion
/// item. Before version 3.16.0 only the predefined properties `documentation`
/// and `details` could be resolved lazily.
///
/// @since 3.16.0
#[serde(skip_serializing_if = "Option::is_none")]
pub resolve_support: Option<CompletionItemCapabilityResolveSupport>,
/// The client supports the `insertTextMode` property on
/// a completion item to override the whitespace handling mode
/// as defined by the client.
///
/// @since 3.16.0
#[serde(skip_serializing_if = "Option::is_none")]
pub insert_text_mode_support: Option<InsertTextModeSupport>,
/// The client has support for completion item label
/// details (see also `CompletionItemLabelDetails`).
///
/// @since 3.17.0
#[serde(skip_serializing_if = "Option::is_none")]
pub label_details_support: Option<bool>,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CompletionItemCapabilityResolveSupport {
/// The properties that a client can resolve lazily.
pub properties: Vec<String>,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InsertTextModeSupport {
pub value_set: Vec<InsertTextMode>,
}
/// How whitespace and indentation is handled during completion
/// item insertion.
///
/// @since 3.16.0
#[derive(Eq, PartialEq, Clone, Copy, Serialize, Deserialize)]
#[serde(transparent)]
pub struct InsertTextMode(i32);
lsp_enum! {
impl InsertTextMode {
/// The insertion or replace strings is taken as it is. If the
/// value is multi line the lines below the cursor will be
/// inserted using the indentation defined in the string value.
/// The client will not apply any kind of adjustments to the
/// string.
pub const AS_IS: InsertTextMode = InsertTextMode(1);
/// The editor adjusts leading whitespace of new lines so that
/// they match the indentation up to the cursor of the line for
/// which the item is accepted.
///
/// Consider a line like this: `<2tabs><cursor><3tabs>foo`. Accepting a
/// multi line completion item is indented using 2 tabs all
/// following lines inserted will be indented using 2 tabs as well.
pub const ADJUST_INDENTATION: InsertTextMode = InsertTextMode(2);
}
}
#[derive(Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(transparent)]
pub struct CompletionItemTag(i32);
lsp_enum! {
impl CompletionItemTag {
pub const DEPRECATED: CompletionItemTag = CompletionItemTag(1);
}
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CompletionItemKindCapability {
/// The completion item kind values the client supports. When this
/// property exists the client also guarantees that it will
/// handle values outside its set gracefully and falls back
/// to a default value when unknown.
///
/// If this property is not present the client only supports
/// the completion items kinds from `Text` to `Reference` as defined in
/// the initial version of the protocol.
#[serde(skip_serializing_if = "Option::is_none")]
pub value_set: Option<Vec<CompletionItemKind>>,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CompletionListCapability {
/// The client supports the following itemDefaults on
/// a completion list.
///
/// The value lists the supported property names of the
/// `CompletionList.itemDefaults` object. If omitted
/// no properties are supported.
///
/// @since 3.17.0
#[serde(skip_serializing_if = "Option::is_none")]
pub item_defaults: Option<Vec<String>>,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CompletionClientCapabilities {
/// Whether completion supports dynamic registration.
#[serde(skip_serializing_if = "Option::is_none")]
pub dynamic_registration: Option<bool>,
/// The client supports the following `CompletionItem` specific
/// capabilities.
#[serde(skip_serializing_if = "Option::is_none")]
pub completion_item: Option<CompletionItemCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub completion_item_kind: Option<CompletionItemKindCapability>,
/// The client supports to send additional context information for a
/// `textDocument/completion` request.
#[serde(skip_serializing_if = "Option::is_none")]
pub context_support: Option<bool>,
/// The client's default when the completion item doesn't provide a
/// `insertTextMode` property.
///
/// @since 3.17.0
#[serde(skip_serializing_if = "Option::is_none")]
pub insert_text_mode: Option<InsertTextMode>,
/// The client supports the following `CompletionList` specific
/// capabilities.
///
/// @since 3.17.0
#[serde(skip_serializing_if = "Option::is_none")]
pub completion_list: Option<CompletionListCapability>,
}
/// A special text edit to provide an insert and a replace operation.
///
/// @since 3.16.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InsertReplaceEdit {
/// The string to be inserted.
pub new_text: String,
/// The range if the insert is requested
pub insert: Range,
/// The range if the replace is requested.
pub replace: Range,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum CompletionTextEdit {
Edit(TextEdit),
InsertAndReplace(InsertReplaceEdit),
}
impl From<TextEdit> for CompletionTextEdit {
fn from(edit: TextEdit) -> Self {
CompletionTextEdit::Edit(edit)
}
}
impl From<InsertReplaceEdit> for CompletionTextEdit {
fn from(edit: InsertReplaceEdit) -> Self {
CompletionTextEdit::InsertAndReplace(edit)
}
}
/// Completion options.
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CompletionOptions {
/// The server provides support to resolve additional information for a completion item.
#[serde(skip_serializing_if = "Option::is_none")]
pub resolve_provider: Option<bool>,
/// Most tools trigger completion request automatically without explicitly
/// requesting it using a keyboard shortcut (e.g. Ctrl+Space). Typically they
/// do so when the user starts to type an identifier. For example if the user
/// types `c` in a JavaScript file code complete will automatically pop up
/// present `console` besides others as a completion item. Characters that
/// make up identifiers don't need to be listed here.
///
/// If code complete should automatically be trigger on characters not being
/// valid inside an identifier (for example `.` in JavaScript) list them in
/// `triggerCharacters`.
#[serde(skip_serializing_if = "Option::is_none")]
pub trigger_characters: Option<Vec<String>>,
/// The list of all possible characters that commit a completion. This field
/// can be used if clients don't support individual commit characters per
/// completion item. See client capability
/// `completion.completionItem.commitCharactersSupport`.
///
/// If a server provides both `allCommitCharacters` and commit characters on
/// an individual completion item the ones on the completion item win.
///
/// @since 3.2.0
#[serde(skip_serializing_if = "Option::is_none")]
pub all_commit_characters: Option<Vec<String>>,
#[serde(flatten)]
pub work_done_progress_options: WorkDoneProgressOptions,
/// The server supports the following `CompletionItem` specific
/// capabilities.
///
/// @since 3.17.0
#[serde(skip_serializing_if = "Option::is_none")]
pub completion_item: Option<CompletionOptionsCompletionItem>,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CompletionOptionsCompletionItem {
/// The server has support for completion item label
/// details (see also `CompletionItemLabelDetails`) when receiving
/// a completion item in a resolve call.
///
/// @since 3.17.0
#[serde(skip_serializing_if = "Option::is_none")]
pub label_details_support: Option<bool>,
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct CompletionRegistrationOptions {
#[serde(flatten)]
pub text_document_registration_options: TextDocumentRegistrationOptions,
#[serde(flatten)]
pub completion_options: CompletionOptions,
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum CompletionResponse {
Array(Vec<CompletionItem>),
List(CompletionList),
}
impl From<Vec<CompletionItem>> for CompletionResponse {
fn from(items: Vec<CompletionItem>) -> Self {
CompletionResponse::Array(items)
}
}
impl From<CompletionList> for CompletionResponse {
fn from(list: CompletionList) -> Self {
CompletionResponse::List(list)
}
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CompletionParams {
// This field was "mixed-in" from TextDocumentPositionParams
#[serde(flatten)]
pub text_document_position: TextDocumentPositionParams,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
// CompletionParams properties:
#[serde(skip_serializing_if = "Option::is_none")]
pub context: Option<CompletionContext>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CompletionContext {
/// How the completion was triggered.
pub trigger_kind: CompletionTriggerKind,
/// The trigger character (a single character) that has trigger code complete.
/// Is undefined if `triggerKind !== CompletionTriggerKind.TriggerCharacter`
#[serde(skip_serializing_if = "Option::is_none")]
pub trigger_character: Option<String>,
}
/// How a completion was triggered.
#[derive(Eq, PartialEq, Clone, Copy, Deserialize, Serialize)]
#[serde(transparent)]
pub struct CompletionTriggerKind(i32);
lsp_enum! {
impl CompletionTriggerKind {
pub const INVOKED: CompletionTriggerKind = CompletionTriggerKind(1);
pub const TRIGGER_CHARACTER: CompletionTriggerKind = CompletionTriggerKind(2);
pub const TRIGGER_FOR_INCOMPLETE_COMPLETIONS: CompletionTriggerKind = CompletionTriggerKind(3);
}
}
/// Represents a collection of [completion items](#CompletionItem) to be presented
/// in the editor.
#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CompletionList {
/// This list it not complete. Further typing should result in recomputing
/// this list.
pub is_incomplete: bool,
/// The completion items.
pub items: Vec<CompletionItem>,
}
#[derive(Debug, PartialEq, Default, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct CompletionItem {
/// The label of this completion item. By default
/// also the text that is inserted when selecting
/// this completion.
pub label: String,
/// Additional details for the label
///
/// @since 3.17.0
#[serde(skip_serializing_if = "Option::is_none")]
pub label_details: Option<CompletionItemLabelDetails>,
/// The kind of this completion item. Based of the kind
/// an icon is chosen by the editor.
#[serde(skip_serializing_if = "Option::is_none")]
pub kind: Option<CompletionItemKind>,
/// A human-readable string with additional information
/// about this item, like type or symbol information.
#[serde(skip_serializing_if = "Option::is_none")]
pub detail: Option<String>,
/// A human-readable string that represents a doc-comment.
#[serde(skip_serializing_if = "Option::is_none")]
pub documentation: Option<Documentation>,
/// Indicates if this item is deprecated.
#[serde(skip_serializing_if = "Option::is_none")]
pub deprecated: Option<bool>,
/// Select this item when showing.
#[serde(skip_serializing_if = "Option::is_none")]
pub preselect: Option<bool>,
/// A string that should be used when comparing this item
/// with other items. When `falsy` the label is used
/// as the sort text for this item.
#[serde(skip_serializing_if = "Option::is_none")]
pub sort_text: Option<String>,
/// A string that should be used when filtering a set of
/// completion items. When `falsy` the label is used as the
/// filter text for this item.
#[serde(skip_serializing_if = "Option::is_none")]
pub filter_text: Option<String>,
/// A string that should be inserted into a document when selecting
/// this completion. When `falsy` the label is used as the insert text
/// for this item.
///
/// The `insertText` is subject to interpretation by the client side.
/// Some tools might not take the string literally. For example
/// VS Code when code complete is requested in this example
/// `con<cursor position>` and a completion item with an `insertText` of
/// `console` is provided it will only insert `sole`. Therefore it is
/// recommended to use `textEdit` instead since it avoids additional client
/// side interpretation.
#[serde(skip_serializing_if = "Option::is_none")]
pub insert_text: Option<String>,
/// The format of the insert text. The format applies to both the `insertText` property
/// and the `newText` property of a provided `textEdit`. If omitted defaults to `InsertTextFormat.PlainText`.
///
/// @since 3.16.0
#[serde(skip_serializing_if = "Option::is_none")]
pub insert_text_format: Option<InsertTextFormat>,
/// How whitespace and indentation is handled during completion
/// item insertion. If not provided the client's default value depends on
/// the `textDocument.completion.insertTextMode` client capability.
///
/// @since 3.16.0
/// @since 3.17.0 - support for `textDocument.completion.insertTextMode`
#[serde(skip_serializing_if = "Option::is_none")]
pub insert_text_mode: Option<InsertTextMode>,
/// An edit which is applied to a document when selecting
/// this completion. When an edit is provided the value of
/// insertText is ignored.
///
/// Most editors support two different operation when accepting a completion item. One is to insert a
/// completion text and the other is to replace an existing text with a completion text. Since this can
/// usually not predetermined by a server it can report both ranges. Clients need to signal support for
/// `InsertReplaceEdits` via the `textDocument.completion.insertReplaceSupport` client capability
/// property.
///
/// *Note 1:* The text edit's range as well as both ranges from a insert replace edit must be a
/// [single line] and they must contain the position at which completion has been requested.
/// *Note 2:* If an `InsertReplaceEdit` is returned the edit's insert range must be a prefix of
/// the edit's replace range, that means it must be contained and starting at the same position.
///
/// @since 3.16.0 additional type `InsertReplaceEdit`
#[serde(skip_serializing_if = "Option::is_none")]
pub text_edit: Option<CompletionTextEdit>,
/// An optional array of additional text edits that are applied when
/// selecting this completion. Edits must not overlap with the main edit
/// nor with themselves.
#[serde(skip_serializing_if = "Option::is_none")]
pub additional_text_edits: Option<Vec<TextEdit>>,
/// An optional command that is executed *after* inserting this completion. *Note* that
/// additional modifications to the current document should be described with the
/// additionalTextEdits-property.
#[serde(skip_serializing_if = "Option::is_none")]
pub command: Option<Command>,
/// An optional set of characters that when pressed while this completion is
/// active will accept it first and then type that character. *Note* that all
/// commit characters should have `length=1` and that superfluous characters
/// will be ignored.
#[serde(skip_serializing_if = "Option::is_none")]
pub commit_characters: Option<Vec<String>>,
/// An data entry field that is preserved on a completion item between
/// a completion and a completion resolve request.
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Value>,
/// Tags for this completion item.
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<CompletionItemTag>>,
}
impl CompletionItem {
/// Create a CompletionItem with the minimum possible info (label and detail).
pub fn new_simple(label: String, detail: String) -> CompletionItem {
CompletionItem {
label,
detail: Some(detail),
..Self::default()
}
}
}
/// Additional details for a completion item label.
///
/// @since 3.17.0
#[derive(Debug, PartialEq, Default, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct CompletionItemLabelDetails {
/// An optional string which is rendered less prominently directly after
/// {@link CompletionItemLabel.label label}, without any spacing. Should be
/// used for function signatures or type annotations.
#[serde(skip_serializing_if = "Option::is_none")]
pub detail: Option<String>,
/// An optional string which is rendered less prominently after
/// {@link CompletionItemLabel.detail}. Should be used for fully qualified
/// names or file path.
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::test_deserialization;
#[test]
fn test_tag_support_deserialization() {
let empty = CompletionItemCapability {
tag_support: None,
..CompletionItemCapability::default()
};
test_deserialization(r#"{}"#, &empty);
test_deserialization(r#"{"tagSupport": false}"#, &empty);
let t = CompletionItemCapability {
tag_support: Some(TagSupport { value_set: vec![] }),
..CompletionItemCapability::default()
};
test_deserialization(r#"{"tagSupport": true}"#, &t);
let t = CompletionItemCapability {
tag_support: Some(TagSupport {
value_set: vec![CompletionItemTag::DEPRECATED],
}),
..CompletionItemCapability::default()
};
test_deserialization(r#"{"tagSupport": {"valueSet": [1]}}"#, &t);
}
#[test]
fn test_debug_enum() {
assert_eq!(format!("{:?}", CompletionItemKind::TEXT), "Text");
assert_eq!(
format!("{:?}", CompletionItemKind::TYPE_PARAMETER),
"TypeParameter"
);
}
#[test]
fn test_try_from_enum() {
use std::convert::TryInto;
assert_eq!("Text".try_into(), Ok(CompletionItemKind::TEXT));
assert_eq!(
"TypeParameter".try_into(),
Ok(CompletionItemKind::TYPE_PARAMETER)
);
}
}

@ -0,0 +1,269 @@
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use url::Url;
use crate::{
Diagnostic, PartialResultParams, StaticRegistrationOptions, TextDocumentIdentifier,
TextDocumentRegistrationOptions, WorkDoneProgressOptions, WorkDoneProgressParams,
};
/// Client capabilities specific to diagnostic pull requests.
///
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DiagnosticClientCapabilities {
/// Whether implementation supports dynamic registration.
///
/// If this is set to `true` the client supports the new `(TextDocumentRegistrationOptions &
/// StaticRegistrationOptions)` return value for the corresponding server capability as well.
#[serde(skip_serializing_if = "Option::is_none")]
pub dynamic_registration: Option<bool>,
/// Whether the clients supports related documents for document diagnostic pulls.
#[serde(skip_serializing_if = "Option::is_none")]
pub related_document_support: Option<bool>,
}
/// Diagnostic options.
///
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DiagnosticOptions {
/// An optional identifier under which the diagnostics are
/// managed by the client.
#[serde(skip_serializing_if = "Option::is_none")]
pub identifier: Option<String>,
/// Whether the language has inter file dependencies, meaning that editing code in one file can
/// result in a different diagnostic set in another file. Inter file dependencies are common
/// for most programming languages and typically uncommon for linters.
pub inter_file_dependencies: bool,
/// The server provides support for workspace diagnostics as well.
pub workspace_diagnostics: bool,
#[serde(flatten)]
pub work_done_progress_options: WorkDoneProgressOptions,
}
/// Diagnostic registration options.
///
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DiagnosticRegistrationOptions {
#[serde(flatten)]
pub text_document_registration_options: TextDocumentRegistrationOptions,
#[serde(flatten)]
pub diagnostic_options: DiagnosticOptions,
#[serde(flatten)]
pub static_registration_options: StaticRegistrationOptions,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum DiagnosticServerCapabilities {
Options(DiagnosticOptions),
RegistrationOptions(DiagnosticRegistrationOptions),
}
/// Parameters of the document diagnostic request.
///
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentDiagnosticParams {
/// The text document.
pub text_document: TextDocumentIdentifier,
/// The additional identifier provided during registration.
pub identifier: Option<String>,
/// The result ID of a previous response if provided.
pub previous_result_id: Option<String>,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
}
/// A diagnostic report with a full set of problems.
///
/// @since 3.17.0
#[derive(Debug, PartialEq, Default, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct FullDocumentDiagnosticReport {
/// An optional result ID. If provided it will be sent on the next diagnostic request for the
/// same document.
#[serde(skip_serializing_if = "Option::is_none")]
pub result_id: Option<String>,
/// The actual items.
pub items: Vec<Diagnostic>,
}
/// A diagnostic report indicating that the last returned report is still accurate.
///
/// A server can only return `unchanged` if result ids are provided.
///
/// @since 3.17.0
#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct UnchangedDocumentDiagnosticReport {
/// A result ID which will be sent on the next diagnostic request for the same document.
pub result_id: String,
}
/// The document diagnostic report kinds.
///
/// @since 3.17.0
#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum DocumentDiagnosticReportKind {
/// A diagnostic report with a full set of problems.
Full(FullDocumentDiagnosticReport),
/// A report indicating that the last returned report is still accurate.
Unchanged(UnchangedDocumentDiagnosticReport),
}
impl From<FullDocumentDiagnosticReport> for DocumentDiagnosticReportKind {
fn from(from: FullDocumentDiagnosticReport) -> Self {
DocumentDiagnosticReportKind::Full(from)
}
}
impl From<UnchangedDocumentDiagnosticReport> for DocumentDiagnosticReportKind {
fn from(from: UnchangedDocumentDiagnosticReport) -> Self {
DocumentDiagnosticReportKind::Unchanged(from)
}
}
/// A full diagnostic report with a set of related documents.
///
/// @since 3.17.0
#[derive(Debug, PartialEq, Default, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct RelatedFullDocumentDiagnosticReport {
/// Diagnostics of related documents.
///
/// This information is useful in programming languages where code in a file A can generate
/// diagnostics in a file B which A depends on. An example of such a language is C/C++ where
/// macro definitions in a file `a.cpp` result in errors in a header file `b.hpp`.
///
/// @since 3.17.0
#[serde(with = "crate::url_map")]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub related_documents: Option<HashMap<Url, DocumentDiagnosticReportKind>>,
// relatedDocuments?: { [uri: string]: FullDocumentDiagnosticReport | UnchangedDocumentDiagnosticReport; };
#[serde(flatten)]
pub full_document_diagnostic_report: FullDocumentDiagnosticReport,
}
/// An unchanged diagnostic report with a set of related documents.
///
/// @since 3.17.0
#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct RelatedUnchangedDocumentDiagnosticReport {
/// Diagnostics of related documents.
///
/// This information is useful in programming languages where code in a file A can generate
/// diagnostics in a file B which A depends on. An example of such a language is C/C++ where
/// macro definitions in a file `a.cpp` result in errors in a header file `b.hpp`.
///
/// @since 3.17.0
#[serde(with = "crate::url_map")]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub related_documents: Option<HashMap<Url, DocumentDiagnosticReportKind>>,
// relatedDocuments?: { [uri: string]: FullDocumentDiagnosticReport | UnchangedDocumentDiagnosticReport; };
#[serde(flatten)]
pub unchanged_document_diagnostic_report: UnchangedDocumentDiagnosticReport,
}
/// The result of a document diagnostic pull request.
///
/// A report can either be a full report containing all diagnostics for the requested document or
/// an unchanged report indicating that nothing has changed in terms of diagnostics in comparison
/// to the last pull request.
///
/// @since 3.17.0
#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum DocumentDiagnosticReport {
/// A diagnostic report with a full set of problems.
Full(RelatedFullDocumentDiagnosticReport),
/// A report indicating that the last returned report is still accurate.
Unchanged(RelatedUnchangedDocumentDiagnosticReport),
}
impl From<RelatedFullDocumentDiagnosticReport> for DocumentDiagnosticReport {
fn from(from: RelatedFullDocumentDiagnosticReport) -> Self {
DocumentDiagnosticReport::Full(from)
}
}
impl From<RelatedUnchangedDocumentDiagnosticReport> for DocumentDiagnosticReport {
fn from(from: RelatedUnchangedDocumentDiagnosticReport) -> Self {
DocumentDiagnosticReport::Unchanged(from)
}
}
/// A partial result for a document diagnostic report.
///
/// @since 3.17.0
#[derive(Debug, PartialEq, Default, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct DocumentDiagnosticReportPartialResult {
#[serde(with = "crate::url_map")]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub related_documents: Option<HashMap<Url, DocumentDiagnosticReportKind>>,
// relatedDocuments?: { [uri: string]: FullDocumentDiagnosticReport | UnchangedDocumentDiagnosticReport; };
}
#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
#[serde(untagged)]
pub enum DocumentDiagnosticReportResult {
Report(DocumentDiagnosticReport),
Partial(DocumentDiagnosticReportPartialResult),
}
impl From<DocumentDiagnosticReport> for DocumentDiagnosticReportResult {
fn from(from: DocumentDiagnosticReport) -> Self {
DocumentDiagnosticReportResult::Report(from)
}
}
impl From<DocumentDiagnosticReportPartialResult> for DocumentDiagnosticReportResult {
fn from(from: DocumentDiagnosticReportPartialResult) -> Self {
DocumentDiagnosticReportResult::Partial(from)
}
}
/// Cancellation data returned from a diagnostic request.
///
/// If no data is provided, it defaults to `{ retrigger_request: true }`.
///
/// @since 3.17.0
#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct DiagnosticServerCancellationData {
pub retrigger_request: bool,
}
impl Default for DiagnosticServerCancellationData {
fn default() -> Self {
DiagnosticServerCancellationData {
retrigger_request: true,
}
}
}

@ -0,0 +1,51 @@
use serde::{Deserialize, Serialize};
use crate::{
DynamicRegistrationClientCapabilities, PartialResultParams, Range, TextDocumentPositionParams,
WorkDoneProgressParams,
};
pub type DocumentHighlightClientCapabilities = DynamicRegistrationClientCapabilities;
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentHighlightParams {
#[serde(flatten)]
pub text_document_position_params: TextDocumentPositionParams,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
}
/// A document highlight is a range inside a text document which deserves
/// special attention. Usually a document highlight is visualized by changing
/// the background color of its range.
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
pub struct DocumentHighlight {
/// The range this highlight applies to.
pub range: Range,
/// The highlight kind, default is DocumentHighlightKind.Text.
#[serde(skip_serializing_if = "Option::is_none")]
pub kind: Option<DocumentHighlightKind>,
}
/// A document highlight kind.
#[derive(Eq, PartialEq, Copy, Clone, Deserialize, Serialize)]
#[serde(transparent)]
pub struct DocumentHighlightKind(i32);
lsp_enum! {
impl DocumentHighlightKind {
/// A textual occurrence.
pub const TEXT: DocumentHighlightKind = DocumentHighlightKind(1);
/// Read-access of a symbol, like reading a variable.
pub const READ: DocumentHighlightKind = DocumentHighlightKind(2);
/// Write-access of a symbol, like writing to a variable.
pub const WRITE: DocumentHighlightKind = DocumentHighlightKind(3);
}
}

@ -0,0 +1,67 @@
use crate::{
PartialResultParams, Range, TextDocumentIdentifier, WorkDoneProgressOptions,
WorkDoneProgressParams,
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use url::Url;
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentLinkClientCapabilities {
/// Whether document link supports dynamic registration.
#[serde(skip_serializing_if = "Option::is_none")]
pub dynamic_registration: Option<bool>,
/// Whether the client support the `tooltip` property on `DocumentLink`.
#[serde(skip_serializing_if = "Option::is_none")]
pub tooltip_support: Option<bool>,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentLinkOptions {
/// Document links have a resolve provider as well.
#[serde(skip_serializing_if = "Option::is_none")]
pub resolve_provider: Option<bool>,
#[serde(flatten)]
pub work_done_progress_options: WorkDoneProgressOptions,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentLinkParams {
/// The document to provide document links for.
pub text_document: TextDocumentIdentifier,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
}
/// A document link is a range in a text document that links to an internal or external resource, like another
/// text document or a web site.
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
pub struct DocumentLink {
/// The range this link applies to.
pub range: Range,
/// The uri this link points to.
#[serde(skip_serializing_if = "Option::is_none")]
pub target: Option<Url>,
/// The tooltip text when you hover over this link.
///
/// If a tooltip is provided, is will be displayed in a string that includes instructions on how to
/// trigger the link, such as `{0} (ctrl + click)`. The specific instructions vary depending on OS,
/// user settings, and localization.
#[serde(skip_serializing_if = "Option::is_none")]
pub tooltip: Option<String>,
/// A data entry field that is preserved on a document link between a DocumentLinkRequest
/// and a DocumentLinkResolveRequest.
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Value>,
}

@ -0,0 +1,134 @@
use crate::{
Location, PartialResultParams, Range, SymbolKind, SymbolKindCapability, TextDocumentIdentifier,
WorkDoneProgressParams,
};
use crate::{SymbolTag, TagSupport};
use serde::{Deserialize, Serialize};
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentSymbolClientCapabilities {
/// This capability supports dynamic registration.
#[serde(skip_serializing_if = "Option::is_none")]
pub dynamic_registration: Option<bool>,
/// Specific capabilities for the `SymbolKind`.
#[serde(skip_serializing_if = "Option::is_none")]
pub symbol_kind: Option<SymbolKindCapability>,
/// The client support hierarchical document symbols.
#[serde(skip_serializing_if = "Option::is_none")]
pub hierarchical_document_symbol_support: Option<bool>,
/// The client supports tags on `SymbolInformation`. Tags are supported on
/// `DocumentSymbol` if `hierarchicalDocumentSymbolSupport` is set to true.
/// Clients supporting tags have to handle unknown tags gracefully.
///
/// @since 3.16.0
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "TagSupport::deserialize_compat"
)]
pub tag_support: Option<TagSupport<SymbolTag>>,
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum DocumentSymbolResponse {
Flat(Vec<SymbolInformation>),
Nested(Vec<DocumentSymbol>),
}
impl From<Vec<SymbolInformation>> for DocumentSymbolResponse {
fn from(info: Vec<SymbolInformation>) -> Self {
DocumentSymbolResponse::Flat(info)
}
}
impl From<Vec<DocumentSymbol>> for DocumentSymbolResponse {
fn from(symbols: Vec<DocumentSymbol>) -> Self {
DocumentSymbolResponse::Nested(symbols)
}
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentSymbolParams {
/// The text document.
pub text_document: TextDocumentIdentifier,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
}
/// Represents programming constructs like variables, classes, interfaces etc.
/// that appear in a document. Document symbols can be hierarchical and they have two ranges:
/// one that encloses its definition and one that points to its most interesting range,
/// e.g. the range of an identifier.
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentSymbol {
/// The name of this symbol.
pub name: String,
/// More detail for this symbol, e.g the signature of a function. If not provided the
/// name is used.
#[serde(skip_serializing_if = "Option::is_none")]
pub detail: Option<String>,
/// The kind of this symbol.
pub kind: SymbolKind,
/// Tags for this completion item.
///
/// @since 3.15.0
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<SymbolTag>>,
/// Indicates if this symbol is deprecated.
#[serde(skip_serializing_if = "Option::is_none")]
#[deprecated(note = "Use tags instead")]
pub deprecated: Option<bool>,
/// The range enclosing this symbol not including leading/trailing whitespace but everything else
/// like comments. This information is typically used to determine if the the clients cursor is
/// inside the symbol to reveal in the symbol in the UI.
pub range: Range,
/// The range that should be selected and revealed when this symbol is being picked, e.g the name of a function.
/// Must be contained by the the `range`.
pub selection_range: Range,
/// Children of this symbol, e.g. properties of a class.
#[serde(skip_serializing_if = "Option::is_none")]
pub children: Option<Vec<DocumentSymbol>>,
}
/// Represents information about programming constructs like variables, classes,
/// interfaces etc.
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SymbolInformation {
/// The name of this symbol.
pub name: String,
/// The kind of this symbol.
pub kind: SymbolKind,
/// Tags for this completion item.
///
/// @since 3.16.0
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<SymbolTag>>,
/// Indicates if this symbol is deprecated.
#[serde(skip_serializing_if = "Option::is_none")]
#[deprecated(note = "Use tags instead")]
pub deprecated: Option<bool>,
/// The location of this symbol.
pub location: Location,
/// The name of the symbol containing this symbol.
#[serde(skip_serializing_if = "Option::is_none")]
pub container_name: Option<String>,
}

@ -0,0 +1,54 @@
//! In this module we only define constants for lsp specific error codes.
//! There are other error codes that are defined in the
//! [JSON RPC specification](https://www.jsonrpc.org/specification#error_object).
/// Defined in the LSP specification but in the range reserved for JSON-RPC error codes,
/// namely the -32099 to -32000 "Reserved for implementation-defined server-errors." range.
/// The code has, nonetheless, been left in this range for backwards compatibility reasons.
pub const SERVER_NOT_INITIALIZED: i64 = -32002;
/// Defined in the LSP specification but in the range reserved for JSON-RPC error codes,
/// namely the -32099 to -32000 "Reserved for implementation-defined server-errors." range.
/// The code has, nonetheless, left in this range for backwards compatibility reasons.
pub const UNKNOWN_ERROR_CODE: i64 = -32001;
/// This is the start range of LSP reserved error codes.
/// It doesn't denote a real error code.
///
/// @since 3.16.0
pub const LSP_RESERVED_ERROR_RANGE_START: i64 = -32899;
/// A request failed but it was syntactically correct, e.g the
/// method name was known and the parameters were valid. The error
/// message should contain human readable information about why
/// the request failed.
///
/// @since 3.17.0
pub const REQUEST_FAILED: i64 = -32803;
/// The server cancelled the request. This error code should
/// only be used for requests that explicitly support being
/// server cancellable.
///
/// @since 3.17.0
pub const SERVER_CANCELLED: i64 = -32802;
/// The server detected that the content of a document got
/// modified outside normal conditions. A server should
/// NOT send this error code if it detects a content change
/// in it unprocessed messages. The result even computed
/// on an older state might still be useful for the client.
///
/// If a client decides that a result is not of any use anymore
/// the client should cancel the request.
pub const CONTENT_MODIFIED: i64 = -32801;
/// The client has canceled a request and a server as detected
/// the cancel.
pub const REQUEST_CANCELLED: i64 = -32800;
/// This is the end range of LSP reserved error codes.
/// It doesn't denote a real error code.
///
/// @since 3.16.0
pub const LSP_RESERVED_ERROR_RANGE_END: i64 = -32800;

@ -0,0 +1,213 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WorkspaceFileOperationsClientCapabilities {
/// Whether the client supports dynamic registration for file
/// requests/notifications.
#[serde(skip_serializing_if = "Option::is_none")]
pub dynamic_registration: Option<bool>,
/// The client has support for sending didCreateFiles notifications.
#[serde(skip_serializing_if = "Option::is_none")]
pub did_create: Option<bool>,
/// The server is interested in receiving willCreateFiles requests.
#[serde(skip_serializing_if = "Option::is_none")]
pub will_create: Option<bool>,
/// The server is interested in receiving didRenameFiles requests.
#[serde(skip_serializing_if = "Option::is_none")]
pub did_rename: Option<bool>,
/// The server is interested in receiving willRenameFiles requests.
#[serde(skip_serializing_if = "Option::is_none")]
pub will_rename: Option<bool>,
/// The server is interested in receiving didDeleteFiles requests.
#[serde(skip_serializing_if = "Option::is_none")]
pub did_delete: Option<bool>,
/// The server is interested in receiving willDeleteFiles requests.
#[serde(skip_serializing_if = "Option::is_none")]
pub will_delete: Option<bool>,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WorkspaceFileOperationsServerCapabilities {
/// The server is interested in receiving didCreateFiles
/// notifications.
#[serde(skip_serializing_if = "Option::is_none")]
pub did_create: Option<FileOperationRegistrationOptions>,
/// The server is interested in receiving willCreateFiles requests.
#[serde(skip_serializing_if = "Option::is_none")]
pub will_create: Option<FileOperationRegistrationOptions>,
/// The server is interested in receiving didRenameFiles
/// notifications.
#[serde(skip_serializing_if = "Option::is_none")]
pub did_rename: Option<FileOperationRegistrationOptions>,
/// The server is interested in receiving willRenameFiles requests.
#[serde(skip_serializing_if = "Option::is_none")]
pub will_rename: Option<FileOperationRegistrationOptions>,
/// The server is interested in receiving didDeleteFiles file
/// notifications.
#[serde(skip_serializing_if = "Option::is_none")]
pub did_delete: Option<FileOperationRegistrationOptions>,
/// The server is interested in receiving willDeleteFiles file
/// requests.
#[serde(skip_serializing_if = "Option::is_none")]
pub will_delete: Option<FileOperationRegistrationOptions>,
}
/// The options to register for file operations.
///
/// @since 3.16.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FileOperationRegistrationOptions {
/// The actual filters.
pub filters: Vec<FileOperationFilter>,
}
/// A filter to describe in which file operation requests or notifications
/// the server is interested in.
///
/// @since 3.16.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FileOperationFilter {
/// A Uri like `file` or `untitled`.
pub scheme: Option<String>,
/// The actual file operation pattern.
pub pattern: FileOperationPattern,
}
/// A pattern kind describing if a glob pattern matches a file a folder or
/// both.
///
/// @since 3.16.0
#[derive(Debug, Eq, PartialEq, Deserialize, Serialize, Clone)]
#[serde(rename_all = "lowercase")]
pub enum FileOperationPatternKind {
/// The pattern matches a file only.
File,
/// The pattern matches a folder only.
Folder,
}
/// Matching options for the file operation pattern.
///
/// @since 3.16.0
///
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FileOperationPatternOptions {
/// The pattern should be matched ignoring casing.
#[serde(skip_serializing_if = "Option::is_none")]
pub ignore_case: Option<bool>,
}
/// A pattern to describe in which file operation requests or notifications
/// the server is interested in.
///
/// @since 3.16.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FileOperationPattern {
/// The glob pattern to match. Glob patterns can have the following syntax:
/// - `*` to match one or more characters in a path segment
/// - `?` to match on one character in a path segment
/// - `**` to match any number of path segments, including none
/// - `{}` to group conditions (e.g. `**/*.{ts,js}` matches all TypeScript
/// and JavaScript files)
/// - `[]` to declare a range of characters to match in a path segment
/// (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …)
/// - `[!...]` to negate a range of characters to match in a path segment
/// (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but
/// not `example.0`)
pub glob: String,
/// Whether to match files or folders with this pattern.
///
/// Matches both if undefined.
#[serde(skip_serializing_if = "Option::is_none")]
pub matches: Option<FileOperationPatternKind>,
/// Additional options used during matching.
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<FileOperationPatternOptions>,
}
/// The parameters sent in notifications/requests for user-initiated creation
/// of files.
///
/// @since 3.16.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateFilesParams {
/// An array of all files/folders created in this operation.
pub files: Vec<FileCreate>,
}
/// Represents information on a file/folder create.
///
/// @since 3.16.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FileCreate {
/// A file:// URI for the location of the file/folder being created.
pub uri: String,
}
/// The parameters sent in notifications/requests for user-initiated renames
/// of files.
///
/// @since 3.16.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RenameFilesParams {
/// An array of all files/folders renamed in this operation. When a folder
/// is renamed, only the folder will be included, and not its children.
pub files: Vec<FileRename>,
}
/// Represents information on a file/folder rename.
///
/// @since 3.16.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FileRename {
/// A file:// URI for the original location of the file/folder being renamed.
pub old_uri: String,
/// A file:// URI for the new location of the file/folder being renamed.
pub new_uri: String,
}
/// The parameters sent in notifications/requests for user-initiated deletes
/// of files.
///
/// @since 3.16.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DeleteFilesParams {
/// An array of all files/folders deleted in this operation.
pub files: Vec<FileDelete>,
}
/// Represents information on a file/folder delete.
///
/// @since 3.16.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FileDelete {
/// A file:// URI for the location of the file/folder being deleted.
pub uri: String,
}

@ -0,0 +1,145 @@
use crate::{
PartialResultParams, StaticTextDocumentColorProviderOptions, TextDocumentIdentifier,
WorkDoneProgressParams,
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FoldingRangeParams {
/// The text document.
pub text_document: TextDocumentIdentifier,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum FoldingRangeProviderCapability {
Simple(bool),
FoldingProvider(FoldingProviderOptions),
Options(StaticTextDocumentColorProviderOptions),
}
impl From<StaticTextDocumentColorProviderOptions> for FoldingRangeProviderCapability {
fn from(from: StaticTextDocumentColorProviderOptions) -> Self {
Self::Options(from)
}
}
impl From<FoldingProviderOptions> for FoldingRangeProviderCapability {
fn from(from: FoldingProviderOptions) -> Self {
Self::FoldingProvider(from)
}
}
impl From<bool> for FoldingRangeProviderCapability {
fn from(from: bool) -> Self {
Self::Simple(from)
}
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
pub struct FoldingProviderOptions {}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FoldingRangeKindCapability {
/// The folding range kind values the client supports. When this
/// property exists the client also guarantees that it will
/// handle values outside its set gracefully and falls back
/// to a default value when unknown.
#[serde(skip_serializing_if = "Option::is_none")]
pub value_set: Option<Vec<FoldingRangeKind>>,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FoldingRangeCapability {
/// If set, the client signals that it supports setting collapsedText on
/// folding ranges to display custom labels instead of the default text.
///
/// @since 3.17.0
#[serde(skip_serializing_if = "Option::is_none")]
pub collapsed_text: Option<bool>,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FoldingRangeClientCapabilities {
/// Whether implementation supports dynamic registration for folding range providers. If this is set to `true`
/// the client supports the new `(FoldingRangeProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions)`
/// return value for the corresponding server capability as well.
#[serde(skip_serializing_if = "Option::is_none")]
pub dynamic_registration: Option<bool>,
/// The maximum number of folding ranges that the client prefers to receive per document. The value serves as a
/// hint, servers are free to follow the limit.
#[serde(skip_serializing_if = "Option::is_none")]
pub range_limit: Option<u32>,
/// If set, the client signals that it only supports folding complete lines. If set, client will
/// ignore specified `startCharacter` and `endCharacter` properties in a FoldingRange.
#[serde(skip_serializing_if = "Option::is_none")]
pub line_folding_only: Option<bool>,
/// Specific options for the folding range kind.
///
/// @since 3.17.0
#[serde(skip_serializing_if = "Option::is_none")]
pub folding_range_kind: Option<FoldingRangeKindCapability>,
/// Specific options for the folding range.
///
/// @since 3.17.0
#[serde(skip_serializing_if = "Option::is_none")]
pub folding_range: Option<FoldingRangeCapability>,
}
/// Enum of known range kinds
#[derive(Debug, Eq, PartialEq, Deserialize, Serialize, Clone)]
#[serde(rename_all = "lowercase")]
pub enum FoldingRangeKind {
/// Folding range for a comment
Comment,
/// Folding range for a imports or includes
Imports,
/// Folding range for a region (e.g. `#region`)
Region,
}
/// Represents a folding range.
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FoldingRange {
/// The zero-based line number from where the folded range starts.
pub start_line: u32,
/// The zero-based character offset from where the folded range starts. If not defined, defaults to the length of the start line.
#[serde(skip_serializing_if = "Option::is_none")]
pub start_character: Option<u32>,
/// The zero-based line number where the folded range ends.
pub end_line: u32,
/// The zero-based character offset before the folded range ends. If not defined, defaults to the length of the end line.
#[serde(skip_serializing_if = "Option::is_none")]
pub end_character: Option<u32>,
/// Describes the kind of the folding range such as `comment' or 'region'. The kind
/// is used to categorize folding ranges and used by commands like 'Fold all comments'. See
/// [FoldingRangeKind](#FoldingRangeKind) for an enumeration of standardized kinds.
#[serde(skip_serializing_if = "Option::is_none")]
pub kind: Option<FoldingRangeKind>,
/// The text that the client should show when the specified range is
/// collapsed. If not defined or not supported by the client, a default
/// will be chosen by the client.
///
/// @since 3.17.0
#[serde(skip_serializing_if = "Option::is_none")]
pub collapsed_text: Option<String>,
}

@ -0,0 +1,153 @@
use serde::{Deserialize, Serialize};
use crate::{
DocumentSelector, DynamicRegistrationClientCapabilities, Range, TextDocumentIdentifier,
TextDocumentPositionParams, WorkDoneProgressParams,
};
use std::collections::HashMap;
pub type DocumentFormattingClientCapabilities = DynamicRegistrationClientCapabilities;
pub type DocumentRangeFormattingClientCapabilities = DynamicRegistrationClientCapabilities;
pub type DocumentOnTypeFormattingClientCapabilities = DynamicRegistrationClientCapabilities;
/// Format document on type options
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentOnTypeFormattingOptions {
/// A character on which formatting should be triggered, like `}`.
pub first_trigger_character: String,
/// More trigger characters.
#[serde(skip_serializing_if = "Option::is_none")]
pub more_trigger_character: Option<Vec<String>>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentFormattingParams {
/// The document to format.
pub text_document: TextDocumentIdentifier,
/// The format options.
pub options: FormattingOptions,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
}
/// Value-object describing what options formatting should use.
#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FormattingOptions {
/// Size of a tab in spaces.
pub tab_size: u32,
/// Prefer spaces over tabs.
pub insert_spaces: bool,
/// Signature for further properties.
#[serde(flatten)]
pub properties: HashMap<String, FormattingProperty>,
/// Trim trailing whitespace on a line.
#[serde(skip_serializing_if = "Option::is_none")]
pub trim_trailing_whitespace: Option<bool>,
/// Insert a newline character at the end of the file if one does not exist.
#[serde(skip_serializing_if = "Option::is_none")]
pub insert_final_newline: Option<bool>,
/// Trim all newlines after the final newline at the end of the file.
#[serde(skip_serializing_if = "Option::is_none")]
pub trim_final_newlines: Option<bool>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum FormattingProperty {
Bool(bool),
Number(i32),
String(String),
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentRangeFormattingParams {
/// The document to format.
pub text_document: TextDocumentIdentifier,
/// The range to format
pub range: Range,
/// The format options
pub options: FormattingOptions,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentOnTypeFormattingParams {
/// Text Document and Position fields.
#[serde(flatten)]
pub text_document_position: TextDocumentPositionParams,
/// The character that has been typed.
pub ch: String,
/// The format options.
pub options: FormattingOptions,
}
/// Extends TextDocumentRegistrationOptions
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentOnTypeFormattingRegistrationOptions {
/// A document selector to identify the scope of the registration. If set to null
/// the document selector provided on the client side will be used.
pub document_selector: Option<DocumentSelector>,
/// A character on which formatting should be triggered, like `}`.
pub first_trigger_character: String,
/// More trigger characters.
#[serde(skip_serializing_if = "Option::is_none")]
pub more_trigger_character: Option<Vec<String>>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::test_serialization;
#[test]
fn formatting_options() {
test_serialization(
&FormattingOptions {
tab_size: 123,
insert_spaces: true,
properties: HashMap::new(),
trim_trailing_whitespace: None,
insert_final_newline: None,
trim_final_newlines: None,
},
r#"{"tabSize":123,"insertSpaces":true}"#,
);
test_serialization(
&FormattingOptions {
tab_size: 123,
insert_spaces: true,
properties: vec![("prop".to_string(), FormattingProperty::Number(1))]
.into_iter()
.collect(),
trim_trailing_whitespace: None,
insert_final_newline: None,
trim_final_newlines: None,
},
r#"{"tabSize":123,"insertSpaces":true,"prop":1}"#,
);
}
}

@ -0,0 +1,86 @@
use serde::{Deserialize, Serialize};
use crate::{
MarkedString, MarkupContent, MarkupKind, Range, TextDocumentPositionParams,
TextDocumentRegistrationOptions, WorkDoneProgressOptions, WorkDoneProgressParams,
};
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct HoverClientCapabilities {
/// Whether completion supports dynamic registration.
#[serde(skip_serializing_if = "Option::is_none")]
pub dynamic_registration: Option<bool>,
/// Client supports the follow content formats for the content
/// property. The order describes the preferred format of the client.
#[serde(skip_serializing_if = "Option::is_none")]
pub content_format: Option<Vec<MarkupKind>>,
}
/// Hover options.
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct HoverOptions {
#[serde(flatten)]
pub work_done_progress_options: WorkDoneProgressOptions,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct HoverRegistrationOptions {
#[serde(flatten)]
pub text_document_registration_options: TextDocumentRegistrationOptions,
#[serde(flatten)]
pub hover_options: HoverOptions,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum HoverProviderCapability {
Simple(bool),
Options(HoverOptions),
}
impl From<HoverOptions> for HoverProviderCapability {
fn from(from: HoverOptions) -> Self {
Self::Options(from)
}
}
impl From<bool> for HoverProviderCapability {
fn from(from: bool) -> Self {
Self::Simple(from)
}
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct HoverParams {
#[serde(flatten)]
pub text_document_position_params: TextDocumentPositionParams,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
}
/// The result of a hover request.
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
pub struct Hover {
/// The hover's content
pub contents: HoverContents,
/// An optional range is a range inside a text document
/// that is used to visualize a hover, e.g. by changing the background color.
#[serde(skip_serializing_if = "Option::is_none")]
pub range: Option<Range>,
}
/// Hover contents could be single entry or multiple entries.
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum HoverContents {
Scalar(MarkedString),
Array(Vec<MarkedString>),
Markup(MarkupContent),
}

@ -0,0 +1,281 @@
use crate::{
Command, LSPAny, Location, MarkupContent, Position, Range, StaticRegistrationOptions,
TextDocumentIdentifier, TextDocumentRegistrationOptions, TextEdit, WorkDoneProgressOptions,
WorkDoneProgressParams,
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[serde(untagged)]
pub enum InlayHintServerCapabilities {
Options(InlayHintOptions),
RegistrationOptions(InlayHintRegistrationOptions),
}
/// Inlay hint client capabilities.
///
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InlayHintClientCapabilities {
/// Whether inlay hints support dynamic registration.
#[serde(skip_serializing_if = "Option::is_none")]
pub dynamic_registration: Option<bool>,
/// Indicates which properties a client can resolve lazily on a inlay
/// hint.
#[serde(skip_serializing_if = "Option::is_none")]
pub resolve_support: Option<InlayHintResolveClientCapabilities>,
}
/// Inlay hint options used during static registration.
///
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InlayHintOptions {
#[serde(flatten)]
pub work_done_progress_options: WorkDoneProgressOptions,
/// The server provides support to resolve additional
/// information for an inlay hint item.
#[serde(skip_serializing_if = "Option::is_none")]
pub resolve_provider: Option<bool>,
}
/// Inlay hint options used during static or dynamic registration.
///
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InlayHintRegistrationOptions {
#[serde(flatten)]
pub inlay_hint_options: InlayHintOptions,
#[serde(flatten)]
pub text_document_registration_options: TextDocumentRegistrationOptions,
#[serde(flatten)]
pub static_registration_options: StaticRegistrationOptions,
}
/// A parameter literal used in inlay hint requests.
///
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InlayHintParams {
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
/// The text document.
pub text_document: TextDocumentIdentifier,
/// The visible document range for which inlay hints should be computed.
pub range: Range,
}
/// Inlay hint information.
///
/// @since 3.17.0
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InlayHint {
/// The position of this hint.
pub position: Position,
/// The label of this hint. A human readable string or an array of
/// InlayHintLabelPart label parts.
///
/// *Note* that neither the string nor the label part can be empty.
pub label: InlayHintLabel,
/// The kind of this hint. Can be omitted in which case the client
/// should fall back to a reasonable default.
#[serde(skip_serializing_if = "Option::is_none")]
pub kind: Option<InlayHintKind>,
/// Optional text edits that are performed when accepting this inlay hint.
///
/// *Note* that edits are expected to change the document so that the inlay
/// hint (or its nearest variant) is now part of the document and the inlay
/// hint itself is now obsolete.
///
/// Depending on the client capability `inlayHint.resolveSupport` clients
/// might resolve this property late using the resolve request.
#[serde(skip_serializing_if = "Option::is_none")]
pub text_edits: Option<Vec<TextEdit>>,
/// The tooltip text when you hover over this item.
///
/// Depending on the client capability `inlayHint.resolveSupport` clients
/// might resolve this property late using the resolve request.
#[serde(skip_serializing_if = "Option::is_none")]
pub tooltip: Option<InlayHintTooltip>,
/// Render padding before the hint.
///
/// Note: Padding should use the editor's background color, not the
/// background color of the hint itself. That means padding can be used
/// to visually align/separate an inlay hint.
#[serde(skip_serializing_if = "Option::is_none")]
pub padding_left: Option<bool>,
/// Render padding after the hint.
///
/// Note: Padding should use the editor's background color, not the
/// background color of the hint itself. That means padding can be used
/// to visually align/separate an inlay hint.
#[serde(skip_serializing_if = "Option::is_none")]
pub padding_right: Option<bool>,
/// A data entry field that is preserved on a inlay hint between
/// a `textDocument/inlayHint` and a `inlayHint/resolve` request.
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<LSPAny>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum InlayHintLabel {
String(String),
LabelParts(Vec<InlayHintLabelPart>),
}
impl From<String> for InlayHintLabel {
#[inline]
fn from(from: String) -> Self {
Self::String(from)
}
}
impl From<Vec<InlayHintLabelPart>> for InlayHintLabel {
#[inline]
fn from(from: Vec<InlayHintLabelPart>) -> Self {
Self::LabelParts(from)
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum InlayHintTooltip {
String(String),
MarkupContent(MarkupContent),
}
impl From<String> for InlayHintTooltip {
#[inline]
fn from(from: String) -> Self {
Self::String(from)
}
}
impl From<MarkupContent> for InlayHintTooltip {
#[inline]
fn from(from: MarkupContent) -> Self {
Self::MarkupContent(from)
}
}
/// An inlay hint label part allows for interactive and composite labels
/// of inlay hints.
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InlayHintLabelPart {
/// The value of this label part.
pub value: String,
/// The tooltip text when you hover over this label part. Depending on
/// the client capability `inlayHint.resolveSupport` clients might resolve
/// this property late using the resolve request.
#[serde(skip_serializing_if = "Option::is_none")]
pub tooltip: Option<InlayHintLabelPartTooltip>,
/// An optional source code location that represents this
/// label part.
///
/// The editor will use this location for the hover and for code navigation
/// features: This part will become a clickable link that resolves to the
/// definition of the symbol at the given location (not necessarily the
/// location itself), it shows the hover that shows at the given location,
/// and it shows a context menu with further code navigation commands.
///
/// Depending on the client capability `inlayHint.resolveSupport` clients
/// might resolve this property late using the resolve request.
#[serde(skip_serializing_if = "Option::is_none")]
pub location: Option<Location>,
/// An optional command for this label part.
///
/// Depending on the client capability `inlayHint.resolveSupport` clients
/// might resolve this property late using the resolve request.
#[serde(skip_serializing_if = "Option::is_none")]
pub command: Option<Command>,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum InlayHintLabelPartTooltip {
String(String),
MarkupContent(MarkupContent),
}
impl From<String> for InlayHintLabelPartTooltip {
#[inline]
fn from(from: String) -> Self {
Self::String(from)
}
}
impl From<MarkupContent> for InlayHintLabelPartTooltip {
#[inline]
fn from(from: MarkupContent) -> Self {
Self::MarkupContent(from)
}
}
/// Inlay hint kinds.
///
/// @since 3.17.0
#[derive(Eq, PartialEq, Copy, Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct InlayHintKind(i32);
lsp_enum! {
impl InlayHintKind {
/// An inlay hint that for a type annotation.
pub const TYPE: InlayHintKind = InlayHintKind(1);
/// An inlay hint that is for a parameter.
pub const PARAMETER: InlayHintKind = InlayHintKind(2);
}
}
/// Inlay hint client capabilities.
///
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InlayHintResolveClientCapabilities {
/// The properties that a client can resolve lazily.
pub properties: Vec<String>,
}
/// Client workspace capabilities specific to inlay hints.
///
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InlayHintWorkspaceClientCapabilities {
/// Whether the client implementation supports a refresh request sent from
/// the server to the client.
///
/// Note that this event is global and will force the client to refresh all
/// inlay hints currently shown. It should be used with absolute care and
/// is useful for situation where a server for example detects a project wide
/// change that requires such a calculation.
#[serde(skip_serializing_if = "Option::is_none")]
pub refresh_support: Option<bool>,
}
// TODO(sno2): add tests once stabilized

@ -0,0 +1,162 @@
use crate::{
Command, InsertTextFormat, Range, StaticRegistrationOptions, TextDocumentPositionParams,
TextDocumentRegistrationOptions, WorkDoneProgressOptions, WorkDoneProgressParams,
};
use serde::{Deserialize, Serialize};
/// Client capabilities specific to inline completions.
///
/// @since 3.18.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InlineCompletionClientCapabilities {
/// Whether implementation supports dynamic registration for inline completion providers.
#[serde(skip_serializing_if = "Option::is_none")]
pub dynamic_registration: Option<bool>,
}
/// Inline completion options used during static registration.
///
/// @since 3.18.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
pub struct InlineCompletionOptions {
#[serde(flatten)]
pub work_done_progress_options: WorkDoneProgressOptions,
}
/// Inline completion options used during static or dynamic registration.
///
// @since 3.18.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
pub struct InlineCompletionRegistrationOptions {
#[serde(flatten)]
pub inline_completion_options: InlineCompletionOptions,
#[serde(flatten)]
pub text_document_registration_options: TextDocumentRegistrationOptions,
#[serde(flatten)]
pub static_registration_options: StaticRegistrationOptions,
}
/// A parameter literal used in inline completion requests.
///
/// @since 3.18.0
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InlineCompletionParams {
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub text_document_position: TextDocumentPositionParams,
/// Additional information about the context in which inline completions were requested.
pub context: InlineCompletionContext,
}
/// Describes how an [`InlineCompletionItemProvider`] was triggered.
///
/// @since 3.18.0
#[derive(Eq, PartialEq, Clone, Copy, Deserialize, Serialize)]
pub struct InlineCompletionTriggerKind(i32);
lsp_enum! {
impl InlineCompletionTriggerKind {
/// Completion was triggered explicitly by a user gesture.
/// Return multiple completion items to enable cycling through them.
pub const Invoked: InlineCompletionTriggerKind = InlineCompletionTriggerKind(1);
/// Completion was triggered automatically while editing.
/// It is sufficient to return a single completion item in this case.
pub const Automatic: InlineCompletionTriggerKind = InlineCompletionTriggerKind(2);
}
}
/// Describes the currently selected completion item.
///
/// @since 3.18.0
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
pub struct SelectedCompletionInfo {
/// The range that will be replaced if this completion item is accepted.
pub range: Range,
/// The text the range will be replaced with if this completion is
/// accepted.
pub text: String,
}
/// Provides information about the context in which an inline completion was
/// requested.
///
/// @since 3.18.0
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InlineCompletionContext {
/// Describes how the inline completion was triggered.
pub trigger_kind: InlineCompletionTriggerKind,
/// Provides information about the currently selected item in the
/// autocomplete widget if it is visible.
///
/// If set, provided inline completions must extend the text of the
/// selected item and use the same range, otherwise they are not shown as
/// preview.
/// As an example, if the document text is `console.` and the selected item
/// is `.log` replacing the `.` in the document, the inline completion must
/// also replace `.` and start with `.log`, for example `.log()`.
///
/// Inline completion providers are requested again whenever the selected
/// item changes.
#[serde(skip_serializing_if = "Option::is_none")]
pub selected_completion_info: Option<SelectedCompletionInfo>,
}
/// InlineCompletion response can be multiple completion items, or a list of completion items
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum InlineCompletionResponse {
Array(Vec<InlineCompletionItem>),
List(InlineCompletionList),
}
/// Represents a collection of [`InlineCompletionItem`] to be presented in the editor.
///
/// @since 3.18.0
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct InlineCompletionList {
/// The inline completion items
pub items: Vec<InlineCompletionItem>,
}
/// An inline completion item represents a text snippet that is proposed inline
/// to complete text that is being typed.
///
/// @since 3.18.0
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InlineCompletionItem {
/// The text to replace the range with. Must be set.
/// Is used both for the preview and the accept operation.
pub insert_text: String,
/// A text that is used to decide if this inline completion should be
/// shown. When `falsy` the [`InlineCompletionItem::insertText`] is
/// used.
///
/// An inline completion is shown if the text to replace is a prefix of the
/// filter text.
#[serde(skip_serializing_if = "Option::is_none")]
pub filter_text: Option<String>,
/// The range to replace.
/// Must begin and end on the same line.
///
/// Prefer replacements over insertions to provide a better experience when
/// the user deletes typed text.
#[serde(skip_serializing_if = "Option::is_none")]
pub range: Option<Range>,
/// An optional command that is executed *after* inserting this
/// completion.
#[serde(skip_serializing_if = "Option::is_none")]
pub command: Option<Command>,
/// The format of the insert text. The format applies to the `insertText`.
/// If omitted defaults to `InsertTextFormat.PlainText`.
#[serde(skip_serializing_if = "Option::is_none")]
pub insert_text_format: Option<InsertTextFormat>,
}

@ -0,0 +1,217 @@
use crate::{
DynamicRegistrationClientCapabilities, Range, StaticRegistrationOptions,
TextDocumentIdentifier, TextDocumentRegistrationOptions, WorkDoneProgressOptions,
WorkDoneProgressParams,
};
use serde::{Deserialize, Serialize};
pub type InlineValueClientCapabilities = DynamicRegistrationClientCapabilities;
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum InlineValueServerCapabilities {
Options(InlineValueOptions),
RegistrationOptions(InlineValueRegistrationOptions),
}
/// Inline value options used during static registration.
///
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
pub struct InlineValueOptions {
#[serde(flatten)]
pub work_done_progress_options: WorkDoneProgressOptions,
}
/// Inline value options used during static or dynamic registration.
///
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
pub struct InlineValueRegistrationOptions {
#[serde(flatten)]
pub inline_value_options: InlineValueOptions,
#[serde(flatten)]
pub text_document_registration_options: TextDocumentRegistrationOptions,
#[serde(flatten)]
pub static_registration_options: StaticRegistrationOptions,
}
/// A parameter literal used in inline value requests.
///
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InlineValueParams {
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
/// The text document.
pub text_document: TextDocumentIdentifier,
/// The document range for which inline values should be computed.
pub range: Range,
/// Additional information about the context in which inline values were
/// requested.
pub context: InlineValueContext,
}
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InlineValueContext {
/// The stack frame (as a DAP Id) where the execution has stopped.
pub frame_id: i32,
/// The document range where execution has stopped.
/// Typically the end position of the range denotes the line where the
/// inline values are shown.
pub stopped_location: Range,
}
/// Provide inline value as text.
///
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
pub struct InlineValueText {
/// The document range for which the inline value applies.
pub range: Range,
/// The text of the inline value.
pub text: String,
}
/// Provide inline value through a variable lookup.
///
/// If only a range is specified, the variable name will be extracted from
/// the underlying document.
///
/// An optional variable name can be used to override the extracted name.
///
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InlineValueVariableLookup {
/// The document range for which the inline value applies.
/// The range is used to extract the variable name from the underlying
/// document.
pub range: Range,
/// If specified the name of the variable to look up.
#[serde(skip_serializing_if = "Option::is_none")]
pub variable_name: Option<String>,
/// How to perform the lookup.
pub case_sensitive_lookup: bool,
}
/// Provide an inline value through an expression evaluation.
///
/// If only a range is specified, the expression will be extracted from the
/// underlying document.
///
/// An optional expression can be used to override the extracted expression.
///
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InlineValueEvaluatableExpression {
/// The document range for which the inline value applies.
/// The range is used to extract the evaluatable expression from the
/// underlying document.
pub range: Range,
/// If specified the expression overrides the extracted expression.
#[serde(skip_serializing_if = "Option::is_none")]
pub expression: Option<String>,
}
/// Inline value information can be provided by different means:
/// - directly as a text value (class InlineValueText).
/// - as a name to use for a variable lookup (class InlineValueVariableLookup)
/// - as an evaluatable expression (class InlineValueEvaluatableExpression)
/// The InlineValue types combines all inline value types into one type.
///
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum InlineValue {
Text(InlineValueText),
VariableLookup(InlineValueVariableLookup),
EvaluatableExpression(InlineValueEvaluatableExpression),
}
impl From<InlineValueText> for InlineValue {
#[inline]
fn from(from: InlineValueText) -> Self {
Self::Text(from)
}
}
impl From<InlineValueVariableLookup> for InlineValue {
#[inline]
fn from(from: InlineValueVariableLookup) -> Self {
Self::VariableLookup(from)
}
}
impl From<InlineValueEvaluatableExpression> for InlineValue {
#[inline]
fn from(from: InlineValueEvaluatableExpression) -> Self {
Self::EvaluatableExpression(from)
}
}
/// Client workspace capabilities specific to inline values.
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
///
/// @since 3.17.0
#[serde(rename_all = "camelCase")]
pub struct InlineValueWorkspaceClientCapabilities {
/// Whether the client implementation supports a refresh request sent from
/// the server to the client.
///
/// Note that this event is global and will force the client to refresh all
/// inline values currently shown. It should be used with absolute care and
/// is useful for situation where a server for example detect a project wide
/// change that requires such a calculation.
#[serde(skip_serializing_if = "Option::is_none")]
pub refresh_support: Option<bool>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::test_serialization;
use crate::Position;
#[test]
fn inline_values() {
test_serialization(
&InlineValueText {
range: Range::new(Position::new(0, 0), Position::new(0, 4)),
text: "one".to_owned(),
},
r#"{"range":{"start":{"line":0,"character":0},"end":{"line":0,"character":4}},"text":"one"}"#,
);
test_serialization(
&InlineValue::VariableLookup(InlineValueVariableLookup {
range: Range::new(Position::new(1, 0), Position::new(1, 4)),
variable_name: None,
case_sensitive_lookup: false,
}),
r#"{"range":{"start":{"line":1,"character":0},"end":{"line":1,"character":4}},"caseSensitiveLookup":false}"#,
);
test_serialization(
&InlineValue::EvaluatableExpression(InlineValueEvaluatableExpression {
range: Range::new(Position::new(2, 0), Position::new(2, 4)),
expression: None,
}),
r#"{"range":{"start":{"line":2,"character":0},"end":{"line":2,"character":4}}}"#,
);
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,61 @@
use serde::{Deserialize, Serialize};
use crate::{
DynamicRegistrationClientCapabilities, Range, StaticRegistrationOptions,
TextDocumentPositionParams, TextDocumentRegistrationOptions, WorkDoneProgressOptions,
WorkDoneProgressParams,
};
pub type LinkedEditingRangeClientCapabilities = DynamicRegistrationClientCapabilities;
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LinkedEditingRangeOptions {
#[serde(flatten)]
pub work_done_progress_options: WorkDoneProgressOptions,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LinkedEditingRangeRegistrationOptions {
#[serde(flatten)]
pub text_document_registration_options: TextDocumentRegistrationOptions,
#[serde(flatten)]
pub linked_editing_range_options: LinkedEditingRangeOptions,
#[serde(flatten)]
pub static_registration_options: StaticRegistrationOptions,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum LinkedEditingRangeServerCapabilities {
Simple(bool),
Options(LinkedEditingRangeOptions),
RegistrationOptions(LinkedEditingRangeRegistrationOptions),
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LinkedEditingRangeParams {
#[serde(flatten)]
pub text_document_position_params: TextDocumentPositionParams,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LinkedEditingRanges {
/// A list of ranges that can be renamed together. The ranges must have
/// identical length and contain identical text content. The ranges cannot overlap.
pub ranges: Vec<Range>,
/// An optional word pattern (regular expression) that describes valid contents for
/// the given ranges. If no pattern is provided, the client configuration's word
/// pattern will be used.
#[serde(skip_serializing_if = "Option::is_none")]
pub word_pattern: Option<String>,
}

@ -0,0 +1,336 @@
//! Types of Language Server Index Format (LSIF). LSIF is a standard format
//! for language servers or other programming tools to dump their knowledge
//! about a workspace.
//!
//! Based on <https://microsoft.github.io/language-server-protocol/specifications/lsif/0.6.0/specification/>
use crate::{Range, Url};
use serde::{Deserialize, Serialize};
pub type Id = crate::NumberOrString;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum LocationOrRangeId {
Location(crate::Location),
RangeId(Id),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Entry {
pub id: Id,
#[serde(flatten)]
pub data: Element,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "type")]
pub enum Element {
Vertex(Vertex),
Edge(Edge),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct ToolInfo {
pub name: String,
#[serde(default = "Default::default")]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub args: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Copy)]
pub enum Encoding {
/// Currently only 'utf-16' is supported due to the limitations in LSP.
#[serde(rename = "utf-16")]
Utf16,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct RangeBasedDocumentSymbol {
pub id: Id,
#[serde(default = "Default::default")]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub children: Vec<RangeBasedDocumentSymbol>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(untagged)]
pub enum DocumentSymbolOrRangeBasedVec {
DocumentSymbol(Vec<crate::DocumentSymbol>),
RangeBased(Vec<RangeBasedDocumentSymbol>),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DefinitionTag {
/// The text covered by the range
text: String,
/// The symbol kind.
kind: crate::SymbolKind,
/// Indicates if this symbol is deprecated.
#[serde(default)]
#[serde(skip_serializing_if = "std::ops::Not::not")]
deprecated: bool,
/// The full range of the definition not including leading/trailing whitespace but everything else, e.g comments and code.
/// The range must be included in fullRange.
full_range: Range,
/// Optional detail information for the definition.
#[serde(skip_serializing_if = "Option::is_none")]
detail: Option<String>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DeclarationTag {
/// The text covered by the range
text: String,
/// The symbol kind.
kind: crate::SymbolKind,
/// Indicates if this symbol is deprecated.
#[serde(default)]
deprecated: bool,
/// The full range of the definition not including leading/trailing whitespace but everything else, e.g comments and code.
/// The range must be included in fullRange.
full_range: Range,
/// Optional detail information for the definition.
#[serde(skip_serializing_if = "Option::is_none")]
detail: Option<String>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReferenceTag {
text: String,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UnknownTag {
text: String,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "type")]
pub enum RangeTag {
Definition(DefinitionTag),
Declaration(DeclarationTag),
Reference(ReferenceTag),
Unknown(UnknownTag),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "label")]
pub enum Vertex {
MetaData(MetaData),
/// <https://github.com/Microsoft/language-server-protocol/blob/master/indexFormat/specification.md#the-project-vertex>
Project(Project),
Document(Document),
/// <https://github.com/Microsoft/language-server-protocol/blob/master/indexFormat/specification.md#ranges>
Range {
#[serde(flatten)]
range: Range,
#[serde(skip_serializing_if = "Option::is_none")]
tag: Option<RangeTag>,
},
/// <https://github.com/Microsoft/language-server-protocol/blob/master/indexFormat/specification.md#result-set>
ResultSet(ResultSet),
Moniker(crate::Moniker),
PackageInformation(PackageInformation),
#[serde(rename = "$event")]
Event(Event),
DefinitionResult,
DeclarationResult,
TypeDefinitionResult,
ReferenceResult,
ImplementationResult,
FoldingRangeResult {
result: Vec<crate::FoldingRange>,
},
HoverResult {
result: crate::Hover,
},
DocumentSymbolResult {
result: DocumentSymbolOrRangeBasedVec,
},
DocumentLinkResult {
result: Vec<crate::DocumentLink>,
},
DiagnosticResult {
result: Vec<crate::Diagnostic>,
},
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum EventKind {
Begin,
End,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum EventScope {
Document,
Project,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Event {
pub kind: EventKind,
pub scope: EventScope,
pub data: Id,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "label")]
pub enum Edge {
Contains(EdgeDataMultiIn),
Moniker(EdgeData),
NextMoniker(EdgeData),
Next(EdgeData),
PackageInformation(EdgeData),
Item(Item),
// Methods
#[serde(rename = "textDocument/definition")]
Definition(EdgeData),
#[serde(rename = "textDocument/declaration")]
Declaration(EdgeData),
#[serde(rename = "textDocument/hover")]
Hover(EdgeData),
#[serde(rename = "textDocument/references")]
References(EdgeData),
#[serde(rename = "textDocument/implementation")]
Implementation(EdgeData),
#[serde(rename = "textDocument/typeDefinition")]
TypeDefinition(EdgeData),
#[serde(rename = "textDocument/foldingRange")]
FoldingRange(EdgeData),
#[serde(rename = "textDocument/documentLink")]
DocumentLink(EdgeData),
#[serde(rename = "textDocument/documentSymbol")]
DocumentSymbol(EdgeData),
#[serde(rename = "textDocument/diagnostic")]
Diagnostic(EdgeData),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EdgeData {
pub in_v: Id,
pub out_v: Id,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EdgeDataMultiIn {
pub in_vs: Vec<Id>,
pub out_v: Id,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum DefinitionResultType {
Scalar(LocationOrRangeId),
Array(LocationOrRangeId),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum ItemKind {
Declarations,
Definitions,
References,
ReferenceResults,
ImplementationResults,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Item {
pub document: Id,
#[serde(skip_serializing_if = "Option::is_none")]
pub property: Option<ItemKind>,
#[serde(flatten)]
pub edge_data: EdgeDataMultiIn,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Document {
pub uri: Url,
pub language_id: String,
}
/// <https://github.com/Microsoft/language-server-protocol/blob/master/indexFormat/specification.md#result-set>
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ResultSet {
#[serde(skip_serializing_if = "Option::is_none")]
pub key: Option<String>,
}
/// <https://github.com/Microsoft/language-server-protocol/blob/master/indexFormat/specification.md#the-project-vertex>
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Project {
#[serde(skip_serializing_if = "Option::is_none")]
pub resource: Option<Url>,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
pub kind: String,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MetaData {
/// The version of the LSIF format using semver notation. See <https://semver.org/>. Please note
/// the version numbers starting with 0 don't adhere to semver and adopters have to assume
/// that each new version is breaking.
pub version: String,
/// The project root (in form of an URI) used to compute this dump.
pub project_root: Url,
/// The string encoding used to compute line and character values in
/// positions and ranges.
pub position_encoding: Encoding,
/// Information about the tool that created the dump
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_info: Option<ToolInfo>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Repository {
pub r#type: String,
pub url: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub commit_id: Option<String>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PackageInformation {
pub name: String,
pub manager: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub uri: Option<Url>,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub repository: Option<Repository>,
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
}

@ -0,0 +1,92 @@
use serde::{Deserialize, Serialize};
use crate::{
DynamicRegistrationClientCapabilities, PartialResultParams, TextDocumentPositionParams,
TextDocumentRegistrationOptions, WorkDoneProgressOptions, WorkDoneProgressParams,
};
pub type MonikerClientCapabilities = DynamicRegistrationClientCapabilities;
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum MonikerServerCapabilities {
Options(MonikerOptions),
RegistrationOptions(MonikerRegistrationOptions),
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
pub struct MonikerOptions {
#[serde(flatten)]
pub work_done_progress_options: WorkDoneProgressOptions,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MonikerRegistrationOptions {
#[serde(flatten)]
pub text_document_registration_options: TextDocumentRegistrationOptions,
#[serde(flatten)]
pub moniker_options: MonikerOptions,
}
/// Moniker uniqueness level to define scope of the moniker.
#[derive(Debug, Eq, PartialEq, Deserialize, Serialize, Copy, Clone)]
#[serde(rename_all = "camelCase")]
pub enum UniquenessLevel {
/// The moniker is only unique inside a document
Document,
/// The moniker is unique inside a project for which a dump got created
Project,
/// The moniker is unique inside the group to which a project belongs
Group,
/// The moniker is unique inside the moniker scheme.
Scheme,
/// The moniker is globally unique
Global,
}
/// The moniker kind.
#[derive(Debug, Eq, PartialEq, Deserialize, Serialize, Copy, Clone)]
#[serde(rename_all = "camelCase")]
pub enum MonikerKind {
/// The moniker represent a symbol that is imported into a project
Import,
/// The moniker represent a symbol that is exported into a project
Export,
/// The moniker represents a symbol that is local to a project (e.g. a local
/// variable of a function, a class not visible outside the project, ...)
Local,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MonikerParams {
#[serde(flatten)]
pub text_document_position_params: TextDocumentPositionParams,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
}
/// Moniker definition to match LSIF 0.5 moniker definition.
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Moniker {
/// The scheme of the moniker. For example tsc or .Net
pub scheme: String,
/// The identifier of the moniker. The value is opaque in LSIF however
/// schema owners are allowed to define the structure if they want.
pub identifier: String,
/// The scope in which the moniker is unique
pub unique: UniquenessLevel,
/// The moniker kind if known.
#[serde(skip_serializing_if = "Option::is_none")]
pub kind: Option<MonikerKind>,
}

@ -0,0 +1,361 @@
use super::*;
use serde::{de::DeserializeOwned, Serialize};
pub trait Notification {
type Params: DeserializeOwned + Serialize + Send + Sync + 'static;
const METHOD: &'static str;
}
#[macro_export]
macro_rules! lsp_notification {
("$/cancelRequest") => {
$crate::notification::Cancel
};
("$/setTrace") => {
$crate::notification::SetTrace
};
("$/logTrace") => {
$crate::notification::LogTrace
};
("initialized") => {
$crate::notification::Initialized
};
("exit") => {
$crate::notification::Exit
};
("window/showMessage") => {
$crate::notification::ShowMessage
};
("window/logMessage") => {
$crate::notification::LogMessage
};
("window/workDoneProgress/cancel") => {
$crate::notification::WorkDoneProgressCancel
};
("telemetry/event") => {
$crate::notification::TelemetryEvent
};
("textDocument/didOpen") => {
$crate::notification::DidOpenTextDocument
};
("textDocument/didChange") => {
$crate::notification::DidChangeTextDocument
};
("textDocument/willSave") => {
$crate::notification::WillSaveTextDocument
};
("textDocument/didSave") => {
$crate::notification::DidSaveTextDocument
};
("textDocument/didClose") => {
$crate::notification::DidCloseTextDocument
};
("textDocument/publishDiagnostics") => {
$crate::notification::PublishDiagnostics
};
("workspace/didChangeConfiguration") => {
$crate::notification::DidChangeConfiguration
};
("workspace/didChangeWatchedFiles") => {
$crate::notification::DidChangeWatchedFiles
};
("workspace/didChangeWorkspaceFolders") => {
$crate::notification::DidChangeWorkspaceFolders
};
("$/progress") => {
$crate::notification::Progress
};
("workspace/didCreateFiles") => {
$crate::notification::DidCreateFiles
};
("workspace/didRenameFiles") => {
$crate::notification::DidRenameFiles
};
("workspace/didDeleteFiles") => {
$crate::notification::DidDeleteFiles
};
}
/// The base protocol now offers support for request cancellation. To cancel a request,
/// a notification message with the following properties is sent:
///
/// A request that got canceled still needs to return from the server and send a response back.
/// It can not be left open / hanging. This is in line with the JSON RPC protocol that requires
/// that every request sends a response back. In addition it allows for returning partial results on cancel.
#[derive(Debug)]
pub enum Cancel {}
impl Notification for Cancel {
type Params = CancelParams;
const METHOD: &'static str = "$/cancelRequest";
}
/// A notification that should be used by the client to modify the trace
/// setting of the server.
#[derive(Debug)]
pub enum SetTrace {}
impl Notification for SetTrace {
type Params = SetTraceParams;
const METHOD: &'static str = "$/setTrace";
}
/// A notification to log the trace of the servers execution.
/// The amount and content of these notifications depends on the current trace configuration.
///
/// `LogTrace` should be used for systematic trace reporting. For single debugging messages,
/// the server should send `LogMessage` notifications.
#[derive(Debug)]
pub enum LogTrace {}
impl Notification for LogTrace {
type Params = LogTraceParams;
const METHOD: &'static str = "$/logTrace";
}
/// The initialized notification is sent from the client to the server after the client received
/// the result of the initialize request but before the client is sending any other request or
/// notification to the server. The server can use the initialized notification for example to
/// dynamically register capabilities.
#[derive(Debug)]
pub enum Initialized {}
impl Notification for Initialized {
type Params = InitializedParams;
const METHOD: &'static str = "initialized";
}
/// A notification to ask the server to exit its process.
/// The server should exit with success code 0 if the shutdown request has been received before;
/// otherwise with error code 1.
#[derive(Debug)]
pub enum Exit {}
impl Notification for Exit {
type Params = ();
const METHOD: &'static str = "exit";
}
/// The show message notification is sent from a server to a client to ask the client to display a particular message
/// in the user interface.
#[derive(Debug)]
pub enum ShowMessage {}
impl Notification for ShowMessage {
type Params = ShowMessageParams;
const METHOD: &'static str = "window/showMessage";
}
/// The log message notification is sent from the server to the client to ask the client to log a particular message.
#[derive(Debug)]
pub enum LogMessage {}
impl Notification for LogMessage {
type Params = LogMessageParams;
const METHOD: &'static str = "window/logMessage";
}
/// The telemetry notification is sent from the server to the client to ask the client to log a telemetry event.
/// The protocol doesn't specify the payload since no interpretation of the data happens in the protocol. Most clients even don't handle
/// the event directly but forward them to the extensions owning the corresponding server issuing the event.
#[derive(Debug)]
pub enum TelemetryEvent {}
impl Notification for TelemetryEvent {
type Params = OneOf<LSPObject, LSPArray>;
const METHOD: &'static str = "telemetry/event";
}
/// A notification sent from the client to the server to signal the change of configuration settings.
#[derive(Debug)]
pub enum DidChangeConfiguration {}
impl Notification for DidChangeConfiguration {
type Params = DidChangeConfigurationParams;
const METHOD: &'static str = "workspace/didChangeConfiguration";
}
/// The document open notification is sent from the client to the server to signal newly opened text documents.
/// The document's truth is now managed by the client and the server must not try to read the document's truth
/// using the document's uri.
#[derive(Debug)]
pub enum DidOpenTextDocument {}
impl Notification for DidOpenTextDocument {
type Params = DidOpenTextDocumentParams;
const METHOD: &'static str = "textDocument/didOpen";
}
/// The document change notification is sent from the client to the server to signal changes to a text document.
/// In 2.0 the shape of the params has changed to include proper version numbers and language ids.
#[derive(Debug)]
pub enum DidChangeTextDocument {}
impl Notification for DidChangeTextDocument {
type Params = DidChangeTextDocumentParams;
const METHOD: &'static str = "textDocument/didChange";
}
/// The document will save notification is sent from the client to the server before the document
/// is actually saved.
#[derive(Debug)]
pub enum WillSaveTextDocument {}
impl Notification for WillSaveTextDocument {
type Params = WillSaveTextDocumentParams;
const METHOD: &'static str = "textDocument/willSave";
}
/// The document close notification is sent from the client to the server when the document got closed in the client.
/// The document's truth now exists where the document's uri points to (e.g. if the document's uri is a file uri
/// the truth now exists on disk).
#[derive(Debug)]
pub enum DidCloseTextDocument {}
impl Notification for DidCloseTextDocument {
type Params = DidCloseTextDocumentParams;
const METHOD: &'static str = "textDocument/didClose";
}
/// The document save notification is sent from the client to the server when the document was saved in the client.
#[derive(Debug)]
pub enum DidSaveTextDocument {}
impl Notification for DidSaveTextDocument {
type Params = DidSaveTextDocumentParams;
const METHOD: &'static str = "textDocument/didSave";
}
/// The watched files notification is sent from the client to the server when the client detects changes to files and folders
/// watched by the language client (note although the name suggest that only file events are sent it is about file system events which include folders as well).
/// It is recommended that servers register for these file system events using the registration mechanism.
/// In former implementations clients pushed file events without the server actively asking for it.
#[derive(Debug)]
pub enum DidChangeWatchedFiles {}
impl Notification for DidChangeWatchedFiles {
type Params = DidChangeWatchedFilesParams;
const METHOD: &'static str = "workspace/didChangeWatchedFiles";
}
/// The workspace/didChangeWorkspaceFolders notification is sent from the client to the server to inform the server
/// about workspace folder configuration changes
#[derive(Debug)]
pub enum DidChangeWorkspaceFolders {}
impl Notification for DidChangeWorkspaceFolders {
type Params = DidChangeWorkspaceFoldersParams;
const METHOD: &'static str = "workspace/didChangeWorkspaceFolders";
}
/// Diagnostics notification are sent from the server to the client to signal results of validation runs.
#[derive(Debug)]
pub enum PublishDiagnostics {}
impl Notification for PublishDiagnostics {
type Params = PublishDiagnosticsParams;
const METHOD: &'static str = "textDocument/publishDiagnostics";
}
/// The progress notification is sent from the server to the client to ask
/// the client to indicate progress.
#[derive(Debug)]
pub enum Progress {}
impl Notification for Progress {
type Params = ProgressParams;
const METHOD: &'static str = "$/progress";
}
/// The `window/workDoneProgress/cancel` notification is sent from the client
/// to the server to cancel a progress initiated on the server side using the `window/workDoneProgress/create`.
#[derive(Debug)]
pub enum WorkDoneProgressCancel {}
impl Notification for WorkDoneProgressCancel {
type Params = WorkDoneProgressCancelParams;
const METHOD: &'static str = "window/workDoneProgress/cancel";
}
/// The did create files notification is sent from the client to the server when files were created from within the client.
#[derive(Debug)]
pub enum DidCreateFiles {}
impl Notification for DidCreateFiles {
type Params = CreateFilesParams;
const METHOD: &'static str = "workspace/didCreateFiles";
}
/// The did rename files notification is sent from the client to the server when files were renamed from within the client.
#[derive(Debug)]
pub enum DidRenameFiles {}
impl Notification for DidRenameFiles {
type Params = RenameFilesParams;
const METHOD: &'static str = "workspace/didRenameFiles";
}
/// The did delete files notification is sent from the client to the server when files were deleted from within the client.
#[derive(Debug)]
pub enum DidDeleteFiles {}
impl Notification for DidDeleteFiles {
type Params = DeleteFilesParams;
const METHOD: &'static str = "workspace/didDeleteFiles";
}
#[cfg(test)]
mod test {
use super::*;
fn fake_call<N>()
where
N: Notification,
N::Params: serde::Serialize,
{
}
macro_rules! check_macro {
($name:tt) => {
// check whether the macro name matches the method
assert_eq!(<lsp_notification!($name) as Notification>::METHOD, $name);
// test whether type checking passes for each component
fake_call::<lsp_notification!($name)>();
};
}
#[test]
fn check_macro_definitions() {
check_macro!("$/cancelRequest");
check_macro!("$/progress");
check_macro!("$/logTrace");
check_macro!("$/setTrace");
check_macro!("initialized");
check_macro!("exit");
check_macro!("window/showMessage");
check_macro!("window/logMessage");
check_macro!("window/workDoneProgress/cancel");
check_macro!("telemetry/event");
check_macro!("textDocument/didOpen");
check_macro!("textDocument/didChange");
check_macro!("textDocument/willSave");
check_macro!("textDocument/didSave");
check_macro!("textDocument/didClose");
check_macro!("textDocument/publishDiagnostics");
check_macro!("workspace/didChangeConfiguration");
check_macro!("workspace/didChangeWatchedFiles");
check_macro!("workspace/didChangeWorkspaceFolders");
check_macro!("workspace/didCreateFiles");
check_macro!("workspace/didRenameFiles");
check_macro!("workspace/didDeleteFiles");
}
#[test]
#[cfg(feature = "proposed")]
fn check_proposed_macro_definitions() {}
}

@ -0,0 +1,134 @@
use serde::{Deserialize, Serialize};
use crate::NumberOrString;
pub type ProgressToken = NumberOrString;
/// The progress notification is sent from the server to the client to ask
/// the client to indicate progress.
#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ProgressParams {
/// The progress token provided by the client.
pub token: ProgressToken,
/// The progress data.
pub value: ProgressParamsValue,
}
#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
#[serde(untagged)]
pub enum ProgressParamsValue {
WorkDone(WorkDoneProgress),
}
/// The `window/workDoneProgress/create` request is sent
/// from the server to the client to ask the client to create a work done progress.
#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct WorkDoneProgressCreateParams {
/// The token to be used to report progress.
pub token: ProgressToken,
}
/// The `window/workDoneProgress/cancel` notification is sent from the client
/// to the server to cancel a progress initiated on the server side using the `window/workDoneProgress/create`.
#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct WorkDoneProgressCancelParams {
/// The token to be used to report progress.
pub token: ProgressToken,
}
/// Options to signal work done progress support in server capabilities.
#[derive(Debug, Eq, PartialEq, Default, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct WorkDoneProgressOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub work_done_progress: Option<bool>,
}
/// An optional token that a server can use to report work done progress
#[derive(Debug, Eq, PartialEq, Default, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct WorkDoneProgressParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub work_done_token: Option<ProgressToken>,
}
#[derive(Debug, PartialEq, Default, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct WorkDoneProgressBegin {
/// Mandatory title of the progress operation. Used to briefly inform
/// about the kind of operation being performed.
/// Examples: "Indexing" or "Linking dependencies".
pub title: String,
/// Controls if a cancel button should show to allow the user to cancel the
/// long running operation. Clients that don't support cancellation are allowed
/// to ignore the setting.
#[serde(skip_serializing_if = "Option::is_none")]
pub cancellable: Option<bool>,
/// Optional, more detailed associated progress message. Contains
/// complementary information to the `title`.
///
/// Examples: "3/25 files", "project/src/module2", "node_modules/some_dep".
/// If unset, the previous progress message (if any) is still valid.
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
/// Optional progress percentage to display (value 100 is considered 100%).
/// If not provided infinite progress is assumed and clients are allowed
/// to ignore the `percentage` value in subsequent in report notifications.
///
/// The value should be steadily rising. Clients are free to ignore values
/// that are not following this rule. The value range is [0, 100]
#[serde(skip_serializing_if = "Option::is_none")]
pub percentage: Option<u32>,
}
#[derive(Debug, PartialEq, Default, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct WorkDoneProgressReport {
/// Controls if a cancel button should show to allow the user to cancel the
/// long running operation. Clients that don't support cancellation are allowed
/// to ignore the setting.
#[serde(skip_serializing_if = "Option::is_none")]
pub cancellable: Option<bool>,
/// Optional, more detailed associated progress message. Contains
/// complementary information to the `title`.
/// Examples: "3/25 files", "project/src/module2", "node_modules/some_dep".
/// If unset, the previous progress message (if any) is still valid.
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
/// Optional progress percentage to display (value 100 is considered 100%).
/// If not provided infinite progress is assumed and clients are allowed
/// to ignore the `percentage` value in subsequent in report notifications.
///
/// The value should be steadily rising. Clients are free to ignore values
/// that are not following this rule. The value range is [0, 100]
#[serde(skip_serializing_if = "Option::is_none")]
pub percentage: Option<u32>,
}
#[derive(Debug, PartialEq, Default, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct WorkDoneProgressEnd {
/// Optional, more detailed associated progress message. Contains
/// complementary information to the `title`.
/// Examples: "3/25 files", "project/src/module2", "node_modules/some_dep".
/// If unset, the previous progress message (if any) is still valid.
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum WorkDoneProgress {
Begin(WorkDoneProgressBegin),
Report(WorkDoneProgressReport),
End(WorkDoneProgressEnd),
}

@ -0,0 +1,30 @@
use crate::{
DynamicRegistrationClientCapabilities, PartialResultParams, TextDocumentPositionParams,
WorkDoneProgressParams,
};
use serde::{Deserialize, Serialize};
pub type ReferenceClientCapabilities = DynamicRegistrationClientCapabilities;
#[derive(Debug, Eq, PartialEq, Clone, Copy, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ReferenceContext {
/// Include the declaration of the current symbol.
pub include_declaration: bool,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ReferenceParams {
// Text Document and Position fields
#[serde(flatten)]
pub text_document_position: TextDocumentPositionParams,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
// ReferenceParams properties:
pub context: ReferenceContext,
}

@ -0,0 +1,88 @@
use crate::{Range, TextDocumentPositionParams, WorkDoneProgressOptions, WorkDoneProgressParams};
use serde::{Deserialize, Serialize};
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RenameParams {
/// Text Document and Position fields
#[serde(flatten)]
pub text_document_position: TextDocumentPositionParams,
/// The new name of the symbol. If the given name is not valid the
/// request must return a [ResponseError](#ResponseError) with an
/// appropriate message set.
pub new_name: String,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RenameOptions {
/// Renames should be checked and tested before being executed.
#[serde(skip_serializing_if = "Option::is_none")]
pub prepare_provider: Option<bool>,
#[serde(flatten)]
pub work_done_progress_options: WorkDoneProgressOptions,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RenameClientCapabilities {
/// Whether rename supports dynamic registration.
#[serde(skip_serializing_if = "Option::is_none")]
pub dynamic_registration: Option<bool>,
/// Client supports testing for validity of rename operations before execution.
///
/// @since 3.12.0
#[serde(skip_serializing_if = "Option::is_none")]
pub prepare_support: Option<bool>,
/// Client supports the default behavior result.
///
/// The value indicates the default behavior used by the
/// client.
///
/// @since 3.16.0
#[serde(skip_serializing_if = "Option::is_none")]
pub prepare_support_default_behavior: Option<PrepareSupportDefaultBehavior>,
/// Whether the client honors the change annotations in
/// text edits and resource operations returned via the
/// rename request's workspace edit by for example presenting
/// the workspace edit in the user interface and asking
/// for confirmation.
///
/// @since 3.16.0
#[serde(skip_serializing_if = "Option::is_none")]
pub honors_change_annotations: Option<bool>,
}
#[derive(Eq, PartialEq, Copy, Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct PrepareSupportDefaultBehavior(i32);
lsp_enum! {
impl PrepareSupportDefaultBehavior {
/// The client's default behavior is to select the identifier
/// according the to language's syntax rule
pub const IDENTIFIER: PrepareSupportDefaultBehavior = PrepareSupportDefaultBehavior(1);
}
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
#[serde(rename_all = "camelCase")]
pub enum PrepareRenameResponse {
Range(Range),
RangeWithPlaceholder {
range: Range,
placeholder: String,
},
#[serde(rename_all = "camelCase")]
DefaultBehavior {
default_behavior: bool,
},
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,86 @@
use serde::{Deserialize, Serialize};
use crate::{
PartialResultParams, Position, Range, StaticTextDocumentRegistrationOptions,
TextDocumentIdentifier, WorkDoneProgressOptions, WorkDoneProgressParams,
};
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SelectionRangeClientCapabilities {
/// Whether implementation supports dynamic registration for selection range
/// providers. If this is set to `true` the client supports the new
/// `SelectionRangeRegistrationOptions` return value for the corresponding
/// server capability as well.
#[serde(skip_serializing_if = "Option::is_none")]
pub dynamic_registration: Option<bool>,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
pub struct SelectionRangeOptions {
#[serde(flatten)]
pub work_done_progress_options: WorkDoneProgressOptions,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
pub struct SelectionRangeRegistrationOptions {
#[serde(flatten)]
pub selection_range_options: SelectionRangeOptions,
#[serde(flatten)]
pub registration_options: StaticTextDocumentRegistrationOptions,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum SelectionRangeProviderCapability {
Simple(bool),
Options(SelectionRangeOptions),
RegistrationOptions(SelectionRangeRegistrationOptions),
}
impl From<SelectionRangeRegistrationOptions> for SelectionRangeProviderCapability {
fn from(from: SelectionRangeRegistrationOptions) -> Self {
Self::RegistrationOptions(from)
}
}
impl From<SelectionRangeOptions> for SelectionRangeProviderCapability {
fn from(from: SelectionRangeOptions) -> Self {
Self::Options(from)
}
}
impl From<bool> for SelectionRangeProviderCapability {
fn from(from: bool) -> Self {
Self::Simple(from)
}
}
/// A parameter literal used in selection range requests.
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SelectionRangeParams {
/// The text document.
pub text_document: TextDocumentIdentifier,
/// The positions inside the text document.
pub positions: Vec<Position>,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
}
/// Represents a selection range.
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SelectionRange {
/// Range of the selection.
pub range: Range,
/// The parent selection range containing this range.
#[serde(skip_serializing_if = "Option::is_none")]
pub parent: Option<Box<SelectionRange>>,
}

@ -0,0 +1,734 @@
use std::borrow::Cow;
use serde::ser::SerializeSeq;
use serde::{Deserialize, Serialize};
use crate::{
PartialResultParams, Range, StaticRegistrationOptions, TextDocumentIdentifier,
TextDocumentRegistrationOptions, WorkDoneProgressOptions, WorkDoneProgressParams,
};
/// A set of predefined token types. This set is not fixed
/// and clients can specify additional token types via the
/// corresponding client capabilities.
///
/// @since 3.16.0
#[derive(Debug, Eq, PartialEq, Hash, PartialOrd, Clone, Deserialize, Serialize)]
pub struct SemanticTokenType(Cow<'static, str>);
impl SemanticTokenType {
pub const NAMESPACE: SemanticTokenType = SemanticTokenType::new("namespace");
pub const TYPE: SemanticTokenType = SemanticTokenType::new("type");
pub const CLASS: SemanticTokenType = SemanticTokenType::new("class");
pub const ENUM: SemanticTokenType = SemanticTokenType::new("enum");
pub const INTERFACE: SemanticTokenType = SemanticTokenType::new("interface");
pub const STRUCT: SemanticTokenType = SemanticTokenType::new("struct");
pub const TYPE_PARAMETER: SemanticTokenType = SemanticTokenType::new("typeParameter");
pub const PARAMETER: SemanticTokenType = SemanticTokenType::new("parameter");
pub const VARIABLE: SemanticTokenType = SemanticTokenType::new("variable");
pub const PROPERTY: SemanticTokenType = SemanticTokenType::new("property");
pub const ENUM_MEMBER: SemanticTokenType = SemanticTokenType::new("enumMember");
pub const EVENT: SemanticTokenType = SemanticTokenType::new("event");
pub const FUNCTION: SemanticTokenType = SemanticTokenType::new("function");
pub const METHOD: SemanticTokenType = SemanticTokenType::new("method");
pub const MACRO: SemanticTokenType = SemanticTokenType::new("macro");
pub const KEYWORD: SemanticTokenType = SemanticTokenType::new("keyword");
pub const MODIFIER: SemanticTokenType = SemanticTokenType::new("modifier");
pub const COMMENT: SemanticTokenType = SemanticTokenType::new("comment");
pub const STRING: SemanticTokenType = SemanticTokenType::new("string");
pub const NUMBER: SemanticTokenType = SemanticTokenType::new("number");
pub const REGEXP: SemanticTokenType = SemanticTokenType::new("regexp");
pub const OPERATOR: SemanticTokenType = SemanticTokenType::new("operator");
/// @since 3.17.0
pub const DECORATOR: SemanticTokenType = SemanticTokenType::new("decorator");
pub const fn new(tag: &'static str) -> Self {
SemanticTokenType(Cow::Borrowed(tag))
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<String> for SemanticTokenType {
fn from(from: String) -> Self {
SemanticTokenType(Cow::from(from))
}
}
impl From<&'static str> for SemanticTokenType {
fn from(from: &'static str) -> Self {
SemanticTokenType::new(from)
}
}
/// A set of predefined token modifiers. This set is not fixed
/// and clients can specify additional token types via the
/// corresponding client capabilities.
///
/// @since 3.16.0
#[derive(Debug, Eq, PartialEq, Hash, PartialOrd, Clone, Deserialize, Serialize)]
pub struct SemanticTokenModifier(Cow<'static, str>);
impl SemanticTokenModifier {
pub const DECLARATION: SemanticTokenModifier = SemanticTokenModifier::new("declaration");
pub const DEFINITION: SemanticTokenModifier = SemanticTokenModifier::new("definition");
pub const READONLY: SemanticTokenModifier = SemanticTokenModifier::new("readonly");
pub const STATIC: SemanticTokenModifier = SemanticTokenModifier::new("static");
pub const DEPRECATED: SemanticTokenModifier = SemanticTokenModifier::new("deprecated");
pub const ABSTRACT: SemanticTokenModifier = SemanticTokenModifier::new("abstract");
pub const ASYNC: SemanticTokenModifier = SemanticTokenModifier::new("async");
pub const MODIFICATION: SemanticTokenModifier = SemanticTokenModifier::new("modification");
pub const DOCUMENTATION: SemanticTokenModifier = SemanticTokenModifier::new("documentation");
pub const DEFAULT_LIBRARY: SemanticTokenModifier = SemanticTokenModifier::new("defaultLibrary");
pub const fn new(tag: &'static str) -> Self {
SemanticTokenModifier(Cow::Borrowed(tag))
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<String> for SemanticTokenModifier {
fn from(from: String) -> Self {
SemanticTokenModifier(Cow::from(from))
}
}
impl From<&'static str> for SemanticTokenModifier {
fn from(from: &'static str) -> Self {
SemanticTokenModifier::new(from)
}
}
#[derive(Debug, Eq, PartialEq, Hash, PartialOrd, Clone, Deserialize, Serialize)]
pub struct TokenFormat(Cow<'static, str>);
impl TokenFormat {
pub const RELATIVE: TokenFormat = TokenFormat::new("relative");
pub const fn new(tag: &'static str) -> Self {
TokenFormat(Cow::Borrowed(tag))
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<String> for TokenFormat {
fn from(from: String) -> Self {
TokenFormat(Cow::from(from))
}
}
impl From<&'static str> for TokenFormat {
fn from(from: &'static str) -> Self {
TokenFormat::new(from)
}
}
/// @since 3.16.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SemanticTokensLegend {
/// The token types a server uses.
pub token_types: Vec<SemanticTokenType>,
/// The token modifiers a server uses.
pub token_modifiers: Vec<SemanticTokenModifier>,
}
/// The actual tokens.
#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
pub struct SemanticToken {
pub delta_line: u32,
pub delta_start: u32,
pub length: u32,
pub token_type: u32,
pub token_modifiers_bitset: u32,
}
impl SemanticToken {
fn deserialize_tokens<'de, D>(deserializer: D) -> Result<Vec<SemanticToken>, D::Error>
where
D: serde::Deserializer<'de>,
{
let data = Vec::<u32>::deserialize(deserializer)?;
let chunks = data.chunks_exact(5);
if !chunks.remainder().is_empty() {
return Result::Err(serde::de::Error::custom("Length is not divisible by 5"));
}
Result::Ok(
chunks
.map(|chunk| SemanticToken {
delta_line: chunk[0],
delta_start: chunk[1],
length: chunk[2],
token_type: chunk[3],
token_modifiers_bitset: chunk[4],
})
.collect(),
)
}
fn serialize_tokens<S>(tokens: &[SemanticToken], serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut seq = serializer.serialize_seq(Some(tokens.len() * 5))?;
for token in tokens.iter() {
seq.serialize_element(&token.delta_line)?;
seq.serialize_element(&token.delta_start)?;
seq.serialize_element(&token.length)?;
seq.serialize_element(&token.token_type)?;
seq.serialize_element(&token.token_modifiers_bitset)?;
}
seq.end()
}
fn deserialize_tokens_opt<'de, D>(
deserializer: D,
) -> Result<Option<Vec<SemanticToken>>, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(transparent)]
struct Wrapper {
#[serde(deserialize_with = "SemanticToken::deserialize_tokens")]
tokens: Vec<SemanticToken>,
}
Ok(Option::<Wrapper>::deserialize(deserializer)?.map(|wrapper| wrapper.tokens))
}
fn serialize_tokens_opt<S>(
data: &Option<Vec<SemanticToken>>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
#[derive(Serialize)]
#[serde(transparent)]
struct Wrapper {
#[serde(serialize_with = "SemanticToken::serialize_tokens")]
tokens: Vec<SemanticToken>,
}
let opt = data.as_ref().map(|t| Wrapper { tokens: t.to_vec() });
opt.serialize(serializer)
}
}
/// @since 3.16.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SemanticTokens {
/// An optional result id. If provided and clients support delta updating
/// the client will include the result id in the next semantic token request.
/// A server can then instead of computing all semantic tokens again simply
/// send a delta.
#[serde(skip_serializing_if = "Option::is_none")]
pub result_id: Option<String>,
/// The actual tokens. For a detailed description about how the data is
/// structured please see
/// <https://github.com/microsoft/vscode-extension-samples/blob/5ae1f7787122812dcc84e37427ca90af5ee09f14/semantic-tokens-sample/vscode.proposed.d.ts#L71>
#[serde(
deserialize_with = "SemanticToken::deserialize_tokens",
serialize_with = "SemanticToken::serialize_tokens"
)]
pub data: Vec<SemanticToken>,
}
/// @since 3.16.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SemanticTokensPartialResult {
#[serde(
deserialize_with = "SemanticToken::deserialize_tokens",
serialize_with = "SemanticToken::serialize_tokens"
)]
pub data: Vec<SemanticToken>,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[serde(untagged)]
pub enum SemanticTokensResult {
Tokens(SemanticTokens),
Partial(SemanticTokensPartialResult),
}
impl From<SemanticTokens> for SemanticTokensResult {
fn from(from: SemanticTokens) -> Self {
SemanticTokensResult::Tokens(from)
}
}
impl From<SemanticTokensPartialResult> for SemanticTokensResult {
fn from(from: SemanticTokensPartialResult) -> Self {
SemanticTokensResult::Partial(from)
}
}
/// @since 3.16.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SemanticTokensEdit {
pub start: u32,
pub delete_count: u32,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "SemanticToken::deserialize_tokens_opt",
serialize_with = "SemanticToken::serialize_tokens_opt"
)]
pub data: Option<Vec<SemanticToken>>,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[serde(untagged)]
pub enum SemanticTokensFullDeltaResult {
Tokens(SemanticTokens),
TokensDelta(SemanticTokensDelta),
PartialTokensDelta { edits: Vec<SemanticTokensEdit> },
}
impl From<SemanticTokens> for SemanticTokensFullDeltaResult {
fn from(from: SemanticTokens) -> Self {
SemanticTokensFullDeltaResult::Tokens(from)
}
}
impl From<SemanticTokensDelta> for SemanticTokensFullDeltaResult {
fn from(from: SemanticTokensDelta) -> Self {
SemanticTokensFullDeltaResult::TokensDelta(from)
}
}
/// @since 3.16.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SemanticTokensDelta {
#[serde(skip_serializing_if = "Option::is_none")]
pub result_id: Option<String>,
/// For a detailed description how these edits are structured please see
/// <https://github.com/microsoft/vscode-extension-samples/blob/5ae1f7787122812dcc84e37427ca90af5ee09f14/semantic-tokens-sample/vscode.proposed.d.ts#L131>
pub edits: Vec<SemanticTokensEdit>,
}
/// Capabilities specific to the `textDocument/semanticTokens/*` requests.
///
/// @since 3.16.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SemanticTokensClientCapabilities {
/// Whether implementation supports dynamic registration. If this is set to `true`
/// the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)`
/// return value for the corresponding server capability as well.
#[serde(skip_serializing_if = "Option::is_none")]
pub dynamic_registration: Option<bool>,
/// Which requests the client supports and might send to the server
/// depending on the server's capability. Please note that clients might not
/// show semantic tokens or degrade some of the user experience if a range
/// or full request is advertised by the client but not provided by the
/// server. If for example the client capability `requests.full` and
/// `request.range` are both set to true but the server only provides a
/// range provider the client might not render a minimap correctly or might
/// even decide to not show any semantic tokens at all.
pub requests: SemanticTokensClientCapabilitiesRequests,
/// The token types that the client supports.
pub token_types: Vec<SemanticTokenType>,
/// The token modifiers that the client supports.
pub token_modifiers: Vec<SemanticTokenModifier>,
/// The token formats the clients supports.
pub formats: Vec<TokenFormat>,
/// Whether the client supports tokens that can overlap each other.
#[serde(skip_serializing_if = "Option::is_none")]
pub overlapping_token_support: Option<bool>,
/// Whether the client supports tokens that can span multiple lines.
#[serde(skip_serializing_if = "Option::is_none")]
pub multiline_token_support: Option<bool>,
/// Whether the client allows the server to actively cancel a
/// semantic token request, e.g. supports returning
/// ErrorCodes.ServerCancelled. If a server does the client
/// needs to retrigger the request.
///
/// @since 3.17.0
#[serde(skip_serializing_if = "Option::is_none")]
pub server_cancel_support: Option<bool>,
/// Whether the client uses semantic tokens to augment existing
/// syntax tokens. If set to `true` client side created syntax
/// tokens and semantic tokens are both used for colorization. If
/// set to `false` the client only uses the returned semantic tokens
/// for colorization.
///
/// If the value is `undefined` then the client behavior is not
/// specified.
///
/// @since 3.17.0
#[serde(skip_serializing_if = "Option::is_none")]
pub augments_syntax_tokens: Option<bool>,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SemanticTokensClientCapabilitiesRequests {
/// The client will send the `textDocument/semanticTokens/range` request if the server provides a corresponding handler.
#[serde(skip_serializing_if = "Option::is_none")]
pub range: Option<bool>,
/// The client will send the `textDocument/semanticTokens/full` request if the server provides a corresponding handler.
#[serde(skip_serializing_if = "Option::is_none")]
pub full: Option<SemanticTokensFullOptions>,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[serde(untagged)]
pub enum SemanticTokensFullOptions {
Bool(bool),
Delta {
/// The client will send the `textDocument/semanticTokens/full/delta` request if the server provides a corresponding handler.
/// The server supports deltas for full documents.
#[serde(skip_serializing_if = "Option::is_none")]
delta: Option<bool>,
},
}
/// @since 3.16.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SemanticTokensOptions {
#[serde(flatten)]
pub work_done_progress_options: WorkDoneProgressOptions,
/// The legend used by the server
pub legend: SemanticTokensLegend,
/// Server supports providing semantic tokens for a specific range
/// of a document.
#[serde(skip_serializing_if = "Option::is_none")]
pub range: Option<bool>,
/// Server supports providing semantic tokens for a full document.
#[serde(skip_serializing_if = "Option::is_none")]
pub full: Option<SemanticTokensFullOptions>,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SemanticTokensRegistrationOptions {
#[serde(flatten)]
pub text_document_registration_options: TextDocumentRegistrationOptions,
#[serde(flatten)]
pub semantic_tokens_options: SemanticTokensOptions,
#[serde(flatten)]
pub static_registration_options: StaticRegistrationOptions,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[serde(untagged)]
pub enum SemanticTokensServerCapabilities {
SemanticTokensOptions(SemanticTokensOptions),
SemanticTokensRegistrationOptions(SemanticTokensRegistrationOptions),
}
impl From<SemanticTokensOptions> for SemanticTokensServerCapabilities {
fn from(from: SemanticTokensOptions) -> Self {
SemanticTokensServerCapabilities::SemanticTokensOptions(from)
}
}
impl From<SemanticTokensRegistrationOptions> for SemanticTokensServerCapabilities {
fn from(from: SemanticTokensRegistrationOptions) -> Self {
SemanticTokensServerCapabilities::SemanticTokensRegistrationOptions(from)
}
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SemanticTokensWorkspaceClientCapabilities {
/// Whether the client implementation supports a refresh request sent from
/// the server to the client.
///
/// Note that this event is global and will force the client to refresh all
/// semantic tokens currently shown. It should be used with absolute care
/// and is useful for situation where a server for example detect a project
/// wide change that requires such a calculation.
pub refresh_support: Option<bool>,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SemanticTokensParams {
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
/// The text document.
pub text_document: TextDocumentIdentifier,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SemanticTokensDeltaParams {
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
/// The text document.
pub text_document: TextDocumentIdentifier,
/// The result id of a previous response. The result Id can either point to a full response
/// or a delta response depending on what was received last.
pub previous_result_id: String,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SemanticTokensRangeParams {
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
/// The text document.
pub text_document: TextDocumentIdentifier,
/// The range the semantic tokens are requested for.
pub range: Range,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[serde(untagged)]
pub enum SemanticTokensRangeResult {
Tokens(SemanticTokens),
Partial(SemanticTokensPartialResult),
}
impl From<SemanticTokens> for SemanticTokensRangeResult {
fn from(tokens: SemanticTokens) -> Self {
SemanticTokensRangeResult::Tokens(tokens)
}
}
impl From<SemanticTokensPartialResult> for SemanticTokensRangeResult {
fn from(partial: SemanticTokensPartialResult) -> Self {
SemanticTokensRangeResult::Partial(partial)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::{test_deserialization, test_serialization};
#[test]
fn test_semantic_tokens_support_serialization() {
test_serialization(
&SemanticTokens {
result_id: None,
data: vec![],
},
r#"{"data":[]}"#,
);
test_serialization(
&SemanticTokens {
result_id: None,
data: vec![SemanticToken {
delta_line: 2,
delta_start: 5,
length: 3,
token_type: 0,
token_modifiers_bitset: 3,
}],
},
r#"{"data":[2,5,3,0,3]}"#,
);
test_serialization(
&SemanticTokens {
result_id: None,
data: vec![
SemanticToken {
delta_line: 2,
delta_start: 5,
length: 3,
token_type: 0,
token_modifiers_bitset: 3,
},
SemanticToken {
delta_line: 0,
delta_start: 5,
length: 4,
token_type: 1,
token_modifiers_bitset: 0,
},
],
},
r#"{"data":[2,5,3,0,3,0,5,4,1,0]}"#,
);
}
#[test]
fn test_semantic_tokens_support_deserialization() {
test_deserialization(
r#"{"data":[]}"#,
&SemanticTokens {
result_id: None,
data: vec![],
},
);
test_deserialization(
r#"{"data":[2,5,3,0,3]}"#,
&SemanticTokens {
result_id: None,
data: vec![SemanticToken {
delta_line: 2,
delta_start: 5,
length: 3,
token_type: 0,
token_modifiers_bitset: 3,
}],
},
);
test_deserialization(
r#"{"data":[2,5,3,0,3,0,5,4,1,0]}"#,
&SemanticTokens {
result_id: None,
data: vec![
SemanticToken {
delta_line: 2,
delta_start: 5,
length: 3,
token_type: 0,
token_modifiers_bitset: 3,
},
SemanticToken {
delta_line: 0,
delta_start: 5,
length: 4,
token_type: 1,
token_modifiers_bitset: 0,
},
],
},
);
}
#[test]
#[should_panic]
fn test_semantic_tokens_support_deserialization_err() {
test_deserialization(
r#"{"data":[1]}"#,
&SemanticTokens {
result_id: None,
data: vec![],
},
);
}
#[test]
fn test_semantic_tokens_edit_support_deserialization() {
test_deserialization(
r#"{"start":0,"deleteCount":1,"data":[2,5,3,0,3,0,5,4,1,0]}"#,
&SemanticTokensEdit {
start: 0,
delete_count: 1,
data: Some(vec![
SemanticToken {
delta_line: 2,
delta_start: 5,
length: 3,
token_type: 0,
token_modifiers_bitset: 3,
},
SemanticToken {
delta_line: 0,
delta_start: 5,
length: 4,
token_type: 1,
token_modifiers_bitset: 0,
},
]),
},
);
test_deserialization(
r#"{"start":0,"deleteCount":1}"#,
&SemanticTokensEdit {
start: 0,
delete_count: 1,
data: None,
},
);
}
#[test]
fn test_semantic_tokens_edit_support_serialization() {
test_serialization(
&SemanticTokensEdit {
start: 0,
delete_count: 1,
data: Some(vec![
SemanticToken {
delta_line: 2,
delta_start: 5,
length: 3,
token_type: 0,
token_modifiers_bitset: 3,
},
SemanticToken {
delta_line: 0,
delta_start: 5,
length: 4,
token_type: 1,
token_modifiers_bitset: 0,
},
]),
},
r#"{"start":0,"deleteCount":1,"data":[2,5,3,0,3,0,5,4,1,0]}"#,
);
test_serialization(
&SemanticTokensEdit {
start: 0,
delete_count: 1,
data: None,
},
r#"{"start":0,"deleteCount":1}"#,
);
}
}

@ -0,0 +1,207 @@
use serde::{Deserialize, Serialize};
use crate::{
Documentation, MarkupKind, TextDocumentPositionParams, TextDocumentRegistrationOptions,
WorkDoneProgressOptions, WorkDoneProgressParams,
};
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SignatureInformationSettings {
/// Client supports the follow content formats for the documentation
/// property. The order describes the preferred format of the client.
#[serde(skip_serializing_if = "Option::is_none")]
pub documentation_format: Option<Vec<MarkupKind>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parameter_information: Option<ParameterInformationSettings>,
/// The client support the `activeParameter` property on `SignatureInformation`
/// literal.
///
/// @since 3.16.0
#[serde(skip_serializing_if = "Option::is_none")]
pub active_parameter_support: Option<bool>,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ParameterInformationSettings {
/// The client supports processing label offsets instead of a
/// simple label string.
///
/// @since 3.14.0
#[serde(skip_serializing_if = "Option::is_none")]
pub label_offset_support: Option<bool>,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SignatureHelpClientCapabilities {
/// Whether completion supports dynamic registration.
#[serde(skip_serializing_if = "Option::is_none")]
pub dynamic_registration: Option<bool>,
/// The client supports the following `SignatureInformation`
/// specific properties.
#[serde(skip_serializing_if = "Option::is_none")]
pub signature_information: Option<SignatureInformationSettings>,
/// The client supports to send additional context information for a
/// `textDocument/signatureHelp` request. A client that opts into
/// contextSupport will also support the `retriggerCharacters` on
/// `SignatureHelpOptions`.
///
/// @since 3.15.0
#[serde(skip_serializing_if = "Option::is_none")]
pub context_support: Option<bool>,
}
/// Signature help options.
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SignatureHelpOptions {
/// The characters that trigger signature help automatically.
#[serde(skip_serializing_if = "Option::is_none")]
pub trigger_characters: Option<Vec<String>>,
/// List of characters that re-trigger signature help.
/// These trigger characters are only active when signature help is already showing. All trigger characters
/// are also counted as re-trigger characters.
#[serde(skip_serializing_if = "Option::is_none")]
pub retrigger_characters: Option<Vec<String>>,
#[serde(flatten)]
pub work_done_progress_options: WorkDoneProgressOptions,
}
/// Signature help options.
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
pub struct SignatureHelpRegistrationOptions {
#[serde(flatten)]
pub text_document_registration_options: TextDocumentRegistrationOptions,
}
/// Signature help options.
#[derive(Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(transparent)]
pub struct SignatureHelpTriggerKind(i32);
lsp_enum! {
impl SignatureHelpTriggerKind {
/// Signature help was invoked manually by the user or by a command.
pub const INVOKED: SignatureHelpTriggerKind = SignatureHelpTriggerKind(1);
/// Signature help was triggered by a trigger character.
pub const TRIGGER_CHARACTER: SignatureHelpTriggerKind = SignatureHelpTriggerKind(2);
/// Signature help was triggered by the cursor moving or by the document content changing.
pub const CONTENT_CHANGE: SignatureHelpTriggerKind = SignatureHelpTriggerKind(3);
}
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SignatureHelpParams {
/// The signature help context. This is only available if the client specifies
/// to send this using the client capability `textDocument.signatureHelp.contextSupport === true`
#[serde(skip_serializing_if = "Option::is_none")]
pub context: Option<SignatureHelpContext>,
#[serde(flatten)]
pub text_document_position_params: TextDocumentPositionParams,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SignatureHelpContext {
/// Action that caused signature help to be triggered.
pub trigger_kind: SignatureHelpTriggerKind,
/// Character that caused signature help to be triggered.
/// This is undefined when `triggerKind !== SignatureHelpTriggerKind.TriggerCharacter`
#[serde(skip_serializing_if = "Option::is_none")]
pub trigger_character: Option<String>,
/// `true` if signature help was already showing when it was triggered.
/// Retriggers occur when the signature help is already active and can be caused by actions such as
/// typing a trigger character, a cursor move, or document content changes.
pub is_retrigger: bool,
/// The currently active `SignatureHelp`.
/// The `activeSignatureHelp` has its `SignatureHelp.activeSignature` field updated based on
/// the user navigating through available signatures.
#[serde(skip_serializing_if = "Option::is_none")]
pub active_signature_help: Option<SignatureHelp>,
}
/// Signature help represents the signature of something
/// callable. There can be multiple signature but only one
/// active and only one active parameter.
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SignatureHelp {
/// One or more signatures.
pub signatures: Vec<SignatureInformation>,
/// The active signature.
#[serde(skip_serializing_if = "Option::is_none")]
pub active_signature: Option<u32>,
/// The active parameter of the active signature.
#[serde(skip_serializing_if = "Option::is_none")]
pub active_parameter: Option<u32>,
}
/// Represents the signature of something callable. A signature
/// can have a label, like a function-name, a doc-comment, and
/// a set of parameters.
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SignatureInformation {
/// The label of this signature. Will be shown in
/// the UI.
pub label: String,
/// The human-readable doc-comment of this signature. Will be shown
/// in the UI but can be omitted.
#[serde(skip_serializing_if = "Option::is_none")]
pub documentation: Option<Documentation>,
/// The parameters of this signature.
#[serde(skip_serializing_if = "Option::is_none")]
pub parameters: Option<Vec<ParameterInformation>>,
/// The index of the active parameter.
///
/// If provided, this is used in place of `SignatureHelp.activeParameter`.
///
/// @since 3.16.0
#[serde(skip_serializing_if = "Option::is_none")]
pub active_parameter: Option<u32>,
}
/// Represents a parameter of a callable-signature. A parameter can
/// have a label and a doc-comment.
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ParameterInformation {
/// The label of this parameter information.
///
/// Either a string or an inclusive start and exclusive end offsets within its containing
/// signature label. (see SignatureInformation.label). *Note*: A label of type string must be
/// a substring of its containing signature label.
pub label: ParameterLabel,
/// The human-readable doc-comment of this parameter. Will be shown
/// in the UI but can be omitted.
#[serde(skip_serializing_if = "Option::is_none")]
pub documentation: Option<Documentation>,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum ParameterLabel {
Simple(String),
LabelOffsets([u32; 2]),
}

@ -0,0 +1,77 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
pub struct SetTraceParams {
/// The new value that should be assigned to the trace setting.
pub value: TraceValue,
}
/// A TraceValue represents the level of verbosity with which the server systematically
/// reports its execution trace using `LogTrace` notifications.
///
/// The initial trace value is set by the client at initialization and can be modified
/// later using the `SetTrace` notification.
#[derive(Debug, Eq, PartialEq, Clone, Copy, Deserialize, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub enum TraceValue {
/// The server should not send any `$/logTrace` notification
#[default]
Off,
/// The server should not add the 'verbose' field in the `LogTraceParams`
Messages,
Verbose,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LogTraceParams {
/// The message to be logged.
pub message: String,
/// Additional information that can be computed if the `trace` configuration
/// is set to `'verbose'`
#[serde(skip_serializing_if = "Option::is_none")]
pub verbose: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::test_serialization;
#[test]
fn test_set_trace_params() {
test_serialization(
&SetTraceParams {
value: TraceValue::Off,
},
r#"{"value":"off"}"#,
);
}
#[test]
fn test_log_trace_params() {
test_serialization(
&LogTraceParams {
message: "message".into(),
verbose: None,
},
r#"{"message":"message"}"#,
);
test_serialization(
&LogTraceParams {
message: "message".into(),
verbose: Some("verbose".into()),
},
r#"{"message":"message","verbose":"verbose"}"#,
);
}
#[test]
fn test_trace_value() {
test_serialization(
&vec![TraceValue::Off, TraceValue::Messages, TraceValue::Verbose],
r#"["off","messages","verbose"]"#,
);
}
}

@ -0,0 +1,90 @@
use crate::{
DynamicRegistrationClientCapabilities, LSPAny, PartialResultParams, Range,
StaticRegistrationOptions, SymbolKind, SymbolTag, TextDocumentPositionParams,
TextDocumentRegistrationOptions, Url, WorkDoneProgressOptions, WorkDoneProgressParams,
};
use serde::{Deserialize, Serialize};
pub type TypeHierarchyClientCapabilities = DynamicRegistrationClientCapabilities;
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
pub struct TypeHierarchyOptions {
#[serde(flatten)]
pub work_done_progress_options: WorkDoneProgressOptions,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
pub struct TypeHierarchyRegistrationOptions {
#[serde(flatten)]
pub text_document_registration_options: TextDocumentRegistrationOptions,
#[serde(flatten)]
pub type_hierarchy_options: TypeHierarchyOptions,
#[serde(flatten)]
pub static_registration_options: StaticRegistrationOptions,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
pub struct TypeHierarchyPrepareParams {
#[serde(flatten)]
pub text_document_position_params: TextDocumentPositionParams,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
pub struct TypeHierarchySupertypesParams {
pub item: TypeHierarchyItem,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
pub struct TypeHierarchySubtypesParams {
pub item: TypeHierarchyItem,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TypeHierarchyItem {
/// The name of this item.
pub name: String,
/// The kind of this item.
pub kind: SymbolKind,
/// Tags for this item.
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<SymbolTag>,
/// More detail for this item, e.g. the signature of a function.
#[serde(skip_serializing_if = "Option::is_none")]
pub detail: Option<String>,
/// The resource identifier of this item.
pub uri: Url,
/// The range enclosing this symbol not including leading/trailing whitespace
/// but everything else, e.g. comments and code.
pub range: Range,
/// The range that should be selected and revealed when this symbol is being
/// picked, e.g. the name of a function. Must be contained by the
/// [`range`](#TypeHierarchyItem.range).
pub selection_range: Range,
/// A data entry field that is preserved between a type hierarchy prepare and
/// supertypes or subtypes requests. It could also be used to identify the
/// type hierarchy in the server, helping improve the performance on
/// resolving supertypes and subtypes.
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<LSPAny>,
}

@ -0,0 +1,173 @@
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use url::Url;
use crate::Range;
#[derive(Eq, PartialEq, Clone, Copy, Deserialize, Serialize)]
#[serde(transparent)]
pub struct MessageType(i32);
lsp_enum! {
impl MessageType {
/// An error message.
pub const ERROR: MessageType = MessageType(1);
/// A warning message.
pub const WARNING: MessageType = MessageType(2);
/// An information message;
pub const INFO: MessageType = MessageType(3);
/// A log message.
pub const LOG: MessageType = MessageType(4);
}
}
/// Window specific client capabilities.
#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WindowClientCapabilities {
/// Whether client supports handling progress notifications. If set
/// servers are allowed to report in `workDoneProgress` property in the
/// request specific server capabilities.
///
/// @since 3.15.0
#[serde(skip_serializing_if = "Option::is_none")]
pub work_done_progress: Option<bool>,
/// Capabilities specific to the showMessage request.
///
/// @since 3.16.0
#[serde(skip_serializing_if = "Option::is_none")]
pub show_message: Option<ShowMessageRequestClientCapabilities>,
/// Client capabilities for the show document request.
///
/// @since 3.16.0
#[serde(skip_serializing_if = "Option::is_none")]
pub show_document: Option<ShowDocumentClientCapabilities>,
}
/// Show message request client capabilities
#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ShowMessageRequestClientCapabilities {
/// Capabilities specific to the `MessageActionItem` type.
#[serde(skip_serializing_if = "Option::is_none")]
pub message_action_item: Option<MessageActionItemCapabilities>,
}
#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MessageActionItemCapabilities {
/// Whether the client supports additional attributes which
/// are preserved and send back to the server in the
/// request's response.
#[serde(skip_serializing_if = "Option::is_none")]
pub additional_properties_support: Option<bool>,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MessageActionItem {
/// A short title like 'Retry', 'Open Log' etc.
pub title: String,
/// Additional attributes that the client preserves and
/// sends back to the server. This depends on the client
/// capability window.messageActionItem.additionalPropertiesSupport
#[serde(flatten)]
pub properties: HashMap<String, MessageActionItemProperty>,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum MessageActionItemProperty {
String(String),
Boolean(bool),
Integer(i32),
Object(Value),
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
pub struct LogMessageParams {
/// The message type. See {@link MessageType}
#[serde(rename = "type")]
pub typ: MessageType,
/// The actual message
pub message: String,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
pub struct ShowMessageParams {
/// The message type. See {@link MessageType}.
#[serde(rename = "type")]
pub typ: MessageType,
/// The actual message.
pub message: String,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
pub struct ShowMessageRequestParams {
/// The message type. See {@link MessageType}
#[serde(rename = "type")]
pub typ: MessageType,
/// The actual message
pub message: String,
/// The message action items to present.
#[serde(skip_serializing_if = "Option::is_none")]
pub actions: Option<Vec<MessageActionItem>>,
}
/// Client capabilities for the show document request.
#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ShowDocumentClientCapabilities {
/// The client has support for the show document request.
pub support: bool,
}
/// Params to show a document.
///
/// @since 3.16.0
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ShowDocumentParams {
/// The document uri to show.
pub uri: Url,
/// Indicates to show the resource in an external program.
/// To show for example `https://code.visualstudio.com/`
/// in the default WEB browser set `external` to `true`.
#[serde(skip_serializing_if = "Option::is_none")]
pub external: Option<bool>,
/// An optional property to indicate whether the editor
/// showing the document should take focus or not.
/// Clients might ignore this property if an external
/// program in started.
#[serde(skip_serializing_if = "Option::is_none")]
pub take_focus: Option<bool>,
/// An optional selection range if the document is a text
/// document. Clients might ignore the property if an
/// external program is started or the file is not a text
/// file.
#[serde(skip_serializing_if = "Option::is_none")]
pub selection: Option<Range>,
}
/// The result of an show document request.
///
/// @since 3.16.0
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ShowDocumentResult {
/// A boolean indicating if the show was successful.
pub success: bool,
}

@ -0,0 +1,149 @@
use serde::{Deserialize, Serialize};
use url::Url;
use crate::{
FullDocumentDiagnosticReport, PartialResultParams, UnchangedDocumentDiagnosticReport,
WorkDoneProgressParams,
};
/// Workspace client capabilities specific to diagnostic pull requests.
///
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DiagnosticWorkspaceClientCapabilities {
/// Whether the client implementation supports a refresh request sent from
/// the server to the client.
///
/// Note that this event is global and will force the client to refresh all
/// pulled diagnostics currently shown. It should be used with absolute care
/// and is useful for situation where a server for example detects a project
/// wide change that requires such a calculation.
#[serde(skip_serializing_if = "Option::is_none")]
pub refresh_support: Option<bool>,
}
/// A previous result ID in a workspace pull request.
///
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
pub struct PreviousResultId {
/// The URI for which the client knows a result ID.
pub uri: Url,
/// The value of the previous result ID.
pub value: String,
}
/// Parameters of the workspace diagnostic request.
///
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WorkspaceDiagnosticParams {
/// The additional identifier provided during registration.
pub identifier: Option<String>,
/// The currently known diagnostic reports with their
/// previous result ids.
pub previous_result_ids: Vec<PreviousResultId>,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
}
/// A full document diagnostic report for a workspace diagnostic result.
///
/// @since 3.17.0
#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct WorkspaceFullDocumentDiagnosticReport {
/// The URI for which diagnostic information is reported.
pub uri: Url,
/// The version number for which the diagnostics are reported.
///
/// If the document is not marked as open, `None` can be provided.
pub version: Option<i64>,
#[serde(flatten)]
pub full_document_diagnostic_report: FullDocumentDiagnosticReport,
}
/// An unchanged document diagnostic report for a workspace diagnostic result.
///
/// @since 3.17.0
#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct WorkspaceUnchangedDocumentDiagnosticReport {
/// The URI for which diagnostic information is reported.
pub uri: Url,
/// The version number for which the diagnostics are reported.
///
/// If the document is not marked as open, `None` can be provided.
pub version: Option<i64>,
#[serde(flatten)]
pub unchanged_document_diagnostic_report: UnchangedDocumentDiagnosticReport,
}
/// A workspace diagnostic document report.
///
/// @since 3.17.0
#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum WorkspaceDocumentDiagnosticReport {
Full(WorkspaceFullDocumentDiagnosticReport),
Unchanged(WorkspaceUnchangedDocumentDiagnosticReport),
}
impl From<WorkspaceFullDocumentDiagnosticReport> for WorkspaceDocumentDiagnosticReport {
fn from(from: WorkspaceFullDocumentDiagnosticReport) -> Self {
WorkspaceDocumentDiagnosticReport::Full(from)
}
}
impl From<WorkspaceUnchangedDocumentDiagnosticReport> for WorkspaceDocumentDiagnosticReport {
fn from(from: WorkspaceUnchangedDocumentDiagnosticReport) -> Self {
WorkspaceDocumentDiagnosticReport::Unchanged(from)
}
}
/// A workspace diagnostic report.
///
/// @since 3.17.0
#[derive(Debug, PartialEq, Default, Deserialize, Serialize, Clone)]
pub struct WorkspaceDiagnosticReport {
pub items: Vec<WorkspaceDocumentDiagnosticReport>,
}
/// A partial result for a workspace diagnostic report.
///
/// @since 3.17.0
#[derive(Debug, PartialEq, Default, Deserialize, Serialize, Clone)]
pub struct WorkspaceDiagnosticReportPartialResult {
pub items: Vec<WorkspaceDocumentDiagnosticReport>,
}
#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
#[serde(untagged)]
pub enum WorkspaceDiagnosticReportResult {
Report(WorkspaceDiagnosticReport),
Partial(WorkspaceDiagnosticReportPartialResult),
}
impl From<WorkspaceDiagnosticReport> for WorkspaceDiagnosticReportResult {
fn from(from: WorkspaceDiagnosticReport) -> Self {
WorkspaceDiagnosticReportResult::Report(from)
}
}
impl From<WorkspaceDiagnosticReportPartialResult> for WorkspaceDiagnosticReportResult {
fn from(from: WorkspaceDiagnosticReportPartialResult) -> Self {
WorkspaceDiagnosticReportResult::Partial(from)
}
}

@ -0,0 +1,49 @@
use serde::{Deserialize, Serialize};
use url::Url;
use crate::OneOf;
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WorkspaceFoldersServerCapabilities {
/// The server has support for workspace folders
#[serde(skip_serializing_if = "Option::is_none")]
pub supported: Option<bool>,
/// Whether the server wants to receive workspace folder
/// change notifications.
///
/// If a string is provided, the string is treated as an ID
/// under which the notification is registered on the client
/// side. The ID can be used to unregister for these events
/// using the `client/unregisterCapability` request.
#[serde(skip_serializing_if = "Option::is_none")]
pub change_notifications: Option<OneOf<bool, String>>,
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WorkspaceFolder {
/// The associated URI for this workspace folder.
pub uri: Url,
/// The name of the workspace folder. Defaults to the uri's basename.
pub name: String,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DidChangeWorkspaceFoldersParams {
/// The actual workspace folder change event.
pub event: WorkspaceFoldersChangeEvent,
}
/// The workspace folder change event.
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WorkspaceFoldersChangeEvent {
/// The array of added workspace folders
pub added: Vec<WorkspaceFolder>,
/// The array of the removed workspace folders
pub removed: Vec<WorkspaceFolder>,
}

@ -0,0 +1,105 @@
use crate::{
LSPAny, Location, OneOf, PartialResultParams, SymbolInformation, SymbolKind,
SymbolKindCapability, SymbolTag, TagSupport, Url, WorkDoneProgressParams,
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WorkspaceSymbolClientCapabilities {
/// This capability supports dynamic registration.
#[serde(skip_serializing_if = "Option::is_none")]
pub dynamic_registration: Option<bool>,
/// Specific capabilities for the `SymbolKind` in the `workspace/symbol` request.
#[serde(skip_serializing_if = "Option::is_none")]
pub symbol_kind: Option<SymbolKindCapability>,
/// The client supports tags on `SymbolInformation`.
/// Clients supporting tags have to handle unknown tags gracefully.
///
/// @since 3.16.0
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "TagSupport::deserialize_compat"
)]
pub tag_support: Option<TagSupport<SymbolTag>>,
/// The client support partial workspace symbols. The client will send the
/// request `workspaceSymbol/resolve` to the server to resolve additional
/// properties.
///
/// @since 3.17.0
#[serde(skip_serializing_if = "Option::is_none")]
pub resolve_support: Option<WorkspaceSymbolResolveSupportCapability>,
}
/// The parameters of a Workspace Symbol Request.
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
pub struct WorkspaceSymbolParams {
#[serde(flatten)]
pub partial_result_params: PartialResultParams,
#[serde(flatten)]
pub work_done_progress_params: WorkDoneProgressParams,
/// A non-empty query string
pub query: String,
}
#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
pub struct WorkspaceSymbolResolveSupportCapability {
/// The properties that a client can resolve lazily. Usually
/// `location.range`
pub properties: Vec<String>,
}
/// A special workspace symbol that supports locations without a range
///
/// @since 3.17.0
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WorkspaceSymbol {
/// The name of this symbol.
pub name: String,
/// The kind of this symbol.
pub kind: SymbolKind,
/// Tags for this completion item.
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<SymbolTag>>,
/// The name of the symbol containing this symbol. This information is for
/// user interface purposes (e.g. to render a qualifier in the user interface
/// if necessary). It can't be used to re-infer a hierarchy for the document
/// symbols.
#[serde(skip_serializing_if = "Option::is_none")]
pub container_name: Option<String>,
/// The location of this symbol. Whether a server is allowed to
/// return a location without a range depends on the client
/// capability `workspace.symbol.resolveSupport`.
///
/// See also `SymbolInformation.location`.
pub location: OneOf<Location, WorkspaceLocation>,
/// A data entry field that is preserved on a workspace symbol between a
/// workspace symbol request and a workspace symbol resolve request.
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<LSPAny>,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
pub struct WorkspaceLocation {
pub uri: Url,
}
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum WorkspaceSymbolResponse {
Flat(Vec<SymbolInformation>),
Nested(Vec<WorkspaceSymbol>),
}

@ -17,16 +17,16 @@ helix-stdx = { path = "../helix-stdx" }
helix-core = { path = "../helix-core" } helix-core = { path = "../helix-core" }
helix-loader = { path = "../helix-loader" } helix-loader = { path = "../helix-loader" }
helix-parsec = { path = "../helix-parsec" } helix-parsec = { path = "../helix-parsec" }
helix-lsp-types = { path = "../helix-lsp-types" }
anyhow = "1.0" anyhow = "1.0"
futures-executor = "0.3" futures-executor = "0.3"
futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false } futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false }
globset = "0.4.14" globset = "0.4.14"
log = "0.4" log = "0.4"
lsp-types = { version = "0.95" }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
tokio = { version = "1.38", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] } tokio = { version = "1.39", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] }
tokio-stream = "0.1.15" tokio-stream = "0.1.15"
parking_lot = "0.12.3" parking_lot = "0.12.3"
arc-swap = "1" arc-swap = "1"

@ -5,15 +5,14 @@ use crate::{
Call, Error, LanguageServerId, OffsetEncoding, Result, Call, Error, LanguageServerId, OffsetEncoding, Result,
}; };
use helix_core::{find_workspace, syntax::LanguageServerFeature, ChangeSet, Rope}; use crate::lsp::{
use helix_loader::VERSION_AND_GIT_HASH; self, notification::DidChangeWorkspaceFolders, CodeActionCapabilityResolveSupport,
use helix_stdx::path;
use lsp::{
notification::DidChangeWorkspaceFolders, CodeActionCapabilityResolveSupport,
DidChangeWorkspaceFoldersParams, OneOf, PositionEncodingKind, SignatureHelp, Url, DidChangeWorkspaceFoldersParams, OneOf, PositionEncodingKind, SignatureHelp, Url,
WorkspaceFolder, WorkspaceFoldersChangeEvent, WorkspaceFolder, WorkspaceFoldersChangeEvent,
}; };
use lsp_types as lsp; use helix_core::{find_workspace, syntax::LanguageServerFeature, ChangeSet, Rope};
use helix_loader::VERSION_AND_GIT_HASH;
use helix_stdx::path;
use parking_lot::Mutex; use parking_lot::Mutex;
use serde::Deserialize; use serde::Deserialize;
use serde_json::Value; use serde_json::Value;
@ -999,7 +998,7 @@ impl Client {
.. ..
}) => match options.as_ref()? { }) => match options.as_ref()? {
lsp::TextDocumentSyncSaveOptions::Supported(true) => false, lsp::TextDocumentSyncSaveOptions::Supported(true) => false,
lsp::TextDocumentSyncSaveOptions::SaveOptions(lsp_types::SaveOptions { lsp::TextDocumentSyncSaveOptions::SaveOptions(lsp::SaveOptions {
include_text, include_text,
}) => include_text.unwrap_or(false), }) => include_text.unwrap_or(false),
lsp::TextDocumentSyncSaveOptions::Supported(false) => return None, lsp::TextDocumentSyncSaveOptions::Supported(false) => return None,

@ -8,9 +8,9 @@ mod transport;
use arc_swap::ArcSwap; use arc_swap::ArcSwap;
pub use client::Client; pub use client::Client;
pub use futures_executor::block_on; pub use futures_executor::block_on;
pub use helix_lsp_types as lsp;
pub use jsonrpc::Call; pub use jsonrpc::Call;
pub use lsp::{Position, Url}; pub use lsp::{Position, Url};
pub use lsp_types as lsp;
use futures_util::stream::select_all::SelectAll; use futures_util::stream::select_all::SelectAll;
use helix_core::syntax::{ use helix_core::syntax::{
@ -284,10 +284,8 @@ pub mod util {
if replace_mode { if replace_mode {
end += text end += text
.chars_at(cursor) .chars_at(cursor)
.skip(1)
.take_while(|ch| chars::char_is_word(*ch)) .take_while(|ch| chars::char_is_word(*ch))
.count() .count();
+ 1;
} }
(start, end) (start, end)
} }
@ -505,7 +503,7 @@ pub mod util {
) -> Transaction { ) -> Transaction {
// Sort edits by start range, since some LSPs (Omnisharp) send them // Sort edits by start range, since some LSPs (Omnisharp) send them
// in reverse order. // in reverse order.
edits.sort_unstable_by_key(|edit| edit.range.start); edits.sort_by_key(|edit| edit.range.start);
// Generate a diff if the edit is a full document replacement. // Generate a diff if the edit is a full document replacement.
#[allow(clippy::collapsible_if)] #[allow(clippy::collapsible_if)]
@ -677,7 +675,7 @@ impl Registry {
pub fn remove_by_id(&mut self, id: LanguageServerId) { pub fn remove_by_id(&mut self, id: LanguageServerId) {
let Some(client) = self.inner.remove(id) else { let Some(client) = self.inner.remove(id) else {
log::error!("client was already removed"); log::debug!("client was already removed");
return; return;
}; };
self.file_event_handler.remove_client(id); self.file_event_handler.remove_client(id);
@ -737,6 +735,11 @@ impl Registry {
.iter() .iter()
.filter_map(|LanguageServerFeatures { name, .. }| { .filter_map(|LanguageServerFeatures { name, .. }| {
if let Some(old_clients) = self.inner_by_name.remove(name) { if let Some(old_clients) = self.inner_by_name.remove(name) {
if old_clients.is_empty() {
log::info!("restarting client for '{name}' which was manually stopped");
} else {
log::info!("stopping existing clients for '{name}'");
}
for old_client in old_clients { for old_client in old_clients {
self.file_event_handler.remove_client(old_client.id()); self.file_event_handler.remove_client(old_client.id());
self.inner.remove(old_client.id()); self.inner.remove(old_client.id());
@ -765,8 +768,13 @@ impl Registry {
} }
pub fn stop(&mut self, name: &str) { pub fn stop(&mut self, name: &str) {
if let Some(clients) = self.inner_by_name.remove(name) { if let Some(clients) = self.inner_by_name.get_mut(name) {
for client in clients { // Drain the clients vec so that the entry in `inner_by_name` remains
// empty. We use the empty vec as a "tombstone" to mean that a server
// has been manually stopped with :lsp-stop and shouldn't be automatically
// restarted by `get`. :lsp-restart can be used to restart the server
// manually.
for client in clients.drain(..) {
self.file_event_handler.remove_client(client.id()); self.file_event_handler.remove_client(client.id());
self.inner.remove(client.id()); self.inner.remove(client.id());
tokio::spawn(async move { tokio::spawn(async move {
@ -786,6 +794,14 @@ impl Registry {
language_config.language_servers.iter().filter_map( language_config.language_servers.iter().filter_map(
move |LanguageServerFeatures { name, .. }| { move |LanguageServerFeatures { name, .. }| {
if let Some(clients) = self.inner_by_name.get(name) { if let Some(clients) = self.inner_by_name.get(name) {
// If the clients vec is empty, do not automatically start a client
// for this server. The empty vec is a tombstone left to mean that a
// server has been manually stopped and shouldn't be started automatically.
// See `stop`.
if clients.is_empty() {
return None;
}
if let Some((_, client)) = clients.iter().enumerate().find(|(i, client)| { if let Some((_, client)) = clients.iter().enumerate().find(|(i, client)| {
client.try_add_doc(&language_config.roots, root_dirs, doc_path, *i == 0) client.try_add_doc(&language_config.roots, root_dirs, doc_path, *i == 0)
}) { }) {
@ -1097,7 +1113,7 @@ mod tests {
#[test] #[test]
fn emoji_format_gh_4791() { fn emoji_format_gh_4791() {
use lsp_types::{Position, Range, TextEdit}; use lsp::{Position, Range, TextEdit};
let edits = vec![ let edits = vec![
TextEdit { TextEdit {

@ -1,4 +1,8 @@
use crate::{jsonrpc, Error, LanguageServerId, Result}; use crate::{
jsonrpc,
lsp::{self, notification::Notification as _},
Error, LanguageServerId, Result,
};
use anyhow::Context; use anyhow::Context;
use log::{error, info}; use log::{error, info};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -289,11 +293,10 @@ impl Transport {
} }
// Hack: inject a terminated notification so we trigger code that needs to happen after exit // Hack: inject a terminated notification so we trigger code that needs to happen after exit
use lsp_types::notification::Notification as _;
let notification = let notification =
ServerMessage::Call(jsonrpc::Call::Notification(jsonrpc::Notification { ServerMessage::Call(jsonrpc::Call::Notification(jsonrpc::Notification {
jsonrpc: None, jsonrpc: None,
method: lsp_types::notification::Exit::METHOD.to_string(), method: lsp::notification::Exit::METHOD.to_string(),
params: jsonrpc::Params::None, params: jsonrpc::Params::None,
})); }));
match transport match transport
@ -338,8 +341,8 @@ impl Transport {
// Determine if a message is allowed to be sent early // Determine if a message is allowed to be sent early
fn is_initialize(payload: &Payload) -> bool { fn is_initialize(payload: &Payload) -> bool {
use lsp_types::{ use lsp::{
notification::{Initialized, Notification}, notification::Initialized,
request::{Initialize, Request}, request::{Initialize, Request},
}; };
match payload { match payload {
@ -357,7 +360,7 @@ impl Transport {
} }
fn is_shutdown(payload: &Payload) -> bool { fn is_shutdown(payload: &Payload) -> bool {
use lsp_types::request::{Request, Shutdown}; use lsp::request::{Request, Shutdown};
matches!(payload, Payload::Request { value: jsonrpc::MethodCall { method, .. }, .. } if method == Shutdown::METHOD) matches!(payload, Payload::Request { value: jsonrpc::MethodCall { method, .. }, .. } if method == Shutdown::METHOD)
} }
@ -370,12 +373,11 @@ impl Transport {
// server successfully initialized // server successfully initialized
is_pending = false; is_pending = false;
use lsp_types::notification::Notification;
// Hack: inject an initialized notification so we trigger code that needs to happen after init // Hack: inject an initialized notification so we trigger code that needs to happen after init
let notification = ServerMessage::Call(jsonrpc::Call::Notification(jsonrpc::Notification { let notification = ServerMessage::Call(jsonrpc::Call::Notification(jsonrpc::Notification {
jsonrpc: None, jsonrpc: None,
method: lsp_types::notification::Initialized::METHOD.to_string(), method: lsp::notification::Initialized::METHOD.to_string(),
params: jsonrpc::Params::None, params: jsonrpc::Params::None,
})); }));
let language_server_name = &transport.name; let language_server_name = &transport.name;

@ -74,6 +74,11 @@ mod imp {
Ok(()) Ok(())
} }
pub fn hardlink_count(p: &Path) -> std::io::Result<u64> {
let metadata = p.metadata()?;
Ok(metadata.nlink())
}
} }
// Licensed under MIT from faccess except for `chown`, `copy_metadata` and `is_acl_inherited` // Licensed under MIT from faccess except for `chown`, `copy_metadata` and `is_acl_inherited`
@ -94,8 +99,8 @@ mod imp {
SID_IDENTIFIER_AUTHORITY, TOKEN_DUPLICATE, TOKEN_QUERY, SID_IDENTIFIER_AUTHORITY, TOKEN_DUPLICATE, TOKEN_QUERY,
}; };
use windows_sys::Win32::Storage::FileSystem::{ use windows_sys::Win32::Storage::FileSystem::{
FILE_ACCESS_RIGHTS, FILE_ALL_ACCESS, FILE_GENERIC_EXECUTE, FILE_GENERIC_READ, GetFileInformationByHandle, BY_HANDLE_FILE_INFORMATION, FILE_ACCESS_RIGHTS,
FILE_GENERIC_WRITE, FILE_ALL_ACCESS, FILE_GENERIC_EXECUTE, FILE_GENERIC_READ, FILE_GENERIC_WRITE,
}; };
use windows_sys::Win32::System::Threading::{GetCurrentThread, OpenThreadToken}; use windows_sys::Win32::System::Threading::{GetCurrentThread, OpenThreadToken};
@ -103,7 +108,7 @@ mod imp {
use std::ffi::c_void; use std::ffi::c_void;
use std::os::windows::{ffi::OsStrExt, fs::OpenOptionsExt}; use std::os::windows::{ffi::OsStrExt, fs::OpenOptionsExt, io::AsRawHandle};
struct SecurityDescriptor { struct SecurityDescriptor {
sd: PSECURITY_DESCRIPTOR, sd: PSECURITY_DESCRIPTOR,
@ -290,21 +295,21 @@ mod imp {
let mut privileges_length = std::mem::size_of::<PRIVILEGE_SET>() as u32; let mut privileges_length = std::mem::size_of::<PRIVILEGE_SET>() as u32;
let mut result = 0; let mut result = 0;
let mut mapping = GENERIC_MAPPING { let mapping = GENERIC_MAPPING {
GenericRead: FILE_GENERIC_READ, GenericRead: FILE_GENERIC_READ,
GenericWrite: FILE_GENERIC_WRITE, GenericWrite: FILE_GENERIC_WRITE,
GenericExecute: FILE_GENERIC_EXECUTE, GenericExecute: FILE_GENERIC_EXECUTE,
GenericAll: FILE_ALL_ACCESS, GenericAll: FILE_ALL_ACCESS,
}; };
unsafe { MapGenericMask(&mut mode, &mut mapping) }; unsafe { MapGenericMask(&mut mode, &mapping) };
if unsafe { if unsafe {
AccessCheck( AccessCheck(
*sd.descriptor(), *sd.descriptor(),
*token.as_handle(), *token.as_handle(),
mode, mode,
&mut mapping, &mapping,
&mut privileges, &mut privileges,
&mut privileges_length, &mut privileges_length,
&mut granted_access, &mut granted_access,
@ -411,6 +416,18 @@ mod imp {
Ok(()) Ok(())
} }
pub fn hardlink_count(p: &Path) -> std::io::Result<u64> {
let file = std::fs::File::open(p)?;
let handle = file.as_raw_handle() as isize;
let mut info: BY_HANDLE_FILE_INFORMATION = unsafe { std::mem::zeroed() };
if unsafe { GetFileInformationByHandle(handle, &mut info) } == 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(info.nNumberOfLinks as u64)
}
}
} }
// Licensed under MIT from faccess except for `copy_metadata` // Licensed under MIT from faccess except for `copy_metadata`
@ -457,3 +474,7 @@ pub fn readonly(p: &Path) -> bool {
pub fn copy_metadata(from: &Path, to: &Path) -> io::Result<()> { pub fn copy_metadata(from: &Path, to: &Path) -> io::Result<()> {
imp::copy_metadata(from, to) imp::copy_metadata(from, to)
} }
pub fn hardlink_count(p: &Path) -> io::Result<u64> {
imp::hardlink_count(p)
}

@ -86,3 +86,4 @@ helix-loader = { path = "../helix-loader" }
smallvec = "1.13" smallvec = "1.13"
indoc = "2.0.5" indoc = "2.0.5"
tempfile = "3.10.1" tempfile = "3.10.1"
same-file = "1.0.1"

@ -66,18 +66,16 @@ mod windows_rc {
.output(); .output();
match find_reg_key { match find_reg_key {
Err(find_reg_key) => { Err(find_reg_key) => Err(io::Error::new(
return Err(io::Error::new( io::ErrorKind::Other,
io::ErrorKind::Other, format!("Failed to run registry query: {}", find_reg_key),
format!("Failed to run registry query: {}", find_reg_key), )),
))
}
Ok(find_reg_key) => { Ok(find_reg_key) => {
if find_reg_key.status.code().unwrap() != 0 { if find_reg_key.status.code().unwrap() != 0 {
return Err(io::Error::new( Err(io::Error::new(
io::ErrorKind::Other, io::ErrorKind::Other,
"Can not find Windows SDK", "Can not find Windows SDK",
)); ))
} else { } else {
let lines = String::from_utf8(find_reg_key.stdout) let lines = String::from_utf8(find_reg_key.stdout)
.expect("Should be able to parse the output"); .expect("Should be able to parse the output");

@ -35,7 +35,9 @@ use log::{debug, error, info, warn};
use std::io::stdout; use std::io::stdout;
use std::{collections::btree_map::Entry, io::stdin, path::Path, sync::Arc}; use std::{collections::btree_map::Entry, io::stdin, path::Path, sync::Arc};
use anyhow::{Context, Error}; #[cfg(not(windows))]
use anyhow::Context;
use anyhow::Error;
use crossterm::{event::Event as CrosstermEvent, tty::IsTty}; use crossterm::{event::Event as CrosstermEvent, tty::IsTty};
#[cfg(not(windows))] #[cfg(not(windows))]
@ -575,7 +577,7 @@ impl Application {
doc_save_event.revision doc_save_event.revision
); );
doc.set_last_saved_revision(doc_save_event.revision); doc.set_last_saved_revision(doc_save_event.revision, doc_save_event.save_time);
let lines = doc_save_event.text.len_lines(); let lines = doc_save_event.text.len_lines();
let bytes = doc_save_event.text.len_bytes(); let bytes = doc_save_event.text.len_bytes();

@ -1939,6 +1939,8 @@ fn select_regex(cx: &mut Context) {
selection::select_on_matches(text, doc.selection(view.id), &regex) selection::select_on_matches(text, doc.selection(view.id), &regex)
{ {
doc.set_selection(view.id, selection); doc.set_selection(view.id, selection);
} else {
cx.editor.set_error("nothing selected");
} }
}, },
); );
@ -4624,6 +4626,8 @@ fn keep_or_remove_selections_impl(cx: &mut Context, remove: bool) {
selection::keep_or_remove_matches(text, doc.selection(view.id), &regex, remove) selection::keep_or_remove_matches(text, doc.selection(view.id), &regex, remove)
{ {
doc.set_selection(view.id, selection); doc.set_selection(view.id, selection);
} else {
cx.editor.set_error("no selections remaining");
} }
}, },
) )
@ -5716,27 +5720,24 @@ async fn shell_impl_async(
process.wait_with_output().await? process.wait_with_output().await?
}; };
if !output.status.success() { let output = if !output.status.success() {
if !output.stderr.is_empty() { if output.stderr.is_empty() {
let err = String::from_utf8_lossy(&output.stderr).to_string(); match output.status.code() {
log::error!("Shell error: {}", err); Some(exit_code) => bail!("Shell command failed: status {}", exit_code),
bail!("Shell error: {}", err); None => bail!("Shell command failed"),
} }
match output.status.code() {
Some(exit_code) => bail!("Shell command failed: status {}", exit_code),
None => bail!("Shell command failed"),
} }
String::from_utf8_lossy(&output.stderr)
// Prioritize `stderr` output over `stdout`
} else if !output.stderr.is_empty() { } else if !output.stderr.is_empty() {
log::debug!( let stderr = String::from_utf8_lossy(&output.stderr);
"Command printed to stderr: {}", log::debug!("Command printed to stderr: {stderr}");
String::from_utf8_lossy(&output.stderr).to_string() stderr
); } else {
} String::from_utf8_lossy(&output.stdout)
};
let str = std::str::from_utf8(&output.stdout) Ok(Tendril::from(output))
.map_err(|_| anyhow!("Process did not output valid UTF-8"))?;
let tendril = Tendril::from(str);
Ok(tendril)
} }
fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) { fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) {

@ -34,7 +34,7 @@ use crate::{
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
collections::{btree_map::Entry, BTreeMap, HashMap, HashSet}, collections::{btree_map::Entry, BTreeMap, HashMap, HashSet},
fmt::Write, fmt::{Display, Write},
future::Future, future::Future,
path::Path, path::Path,
}; };
@ -832,13 +832,13 @@ pub enum ApplyEditErrorKind {
// InvalidEdit, // InvalidEdit,
} }
impl ToString for ApplyEditErrorKind { impl Display for ApplyEditErrorKind {
fn to_string(&self) -> String { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
ApplyEditErrorKind::DocumentChanged => "document has changed".to_string(), ApplyEditErrorKind::DocumentChanged => f.write_str("document has changed"),
ApplyEditErrorKind::FileNotFound => "file not found".to_string(), ApplyEditErrorKind::FileNotFound => f.write_str("file not found"),
ApplyEditErrorKind::UnknownURISchema => "URI schema not supported".to_string(), ApplyEditErrorKind::UnknownURISchema => f.write_str("URI schema not supported"),
ApplyEditErrorKind::IoError(err) => err.to_string(), ApplyEditErrorKind::IoError(err) => f.write_str(&format!("{err}")),
} }
} }
} }
@ -1360,7 +1360,7 @@ fn compute_inlay_hints_for_view(
// Most language servers will already send them sorted but ensure this is the case to // Most language servers will already send them sorted but ensure this is the case to
// avoid errors on our end. // avoid errors on our end.
hints.sort_unstable_by_key(|inlay_hint| inlay_hint.position); hints.sort_by_key(|inlay_hint| inlay_hint.position);
let mut padding_before_inlay_hints = Vec::new(); let mut padding_before_inlay_hints = Vec::new();
let mut type_inlay_hints = Vec::new(); let mut type_inlay_hints = Vec::new();

@ -2308,7 +2308,7 @@ fn run_shell_command(
)); ));
compositor.replace_or_push("shell", popup); compositor.replace_or_push("shell", popup);
} }
editor.set_status("Command succeeded"); editor.set_status("Command run");
}, },
)); ));
Ok(call) Ok(call)
@ -2498,7 +2498,7 @@ fn read(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) ->
ensure!(!args.is_empty(), "file name is expected"); ensure!(!args.is_empty(), "file name is expected");
ensure!(args.len() == 1, "only the file name is expected"); ensure!(args.len() == 1, "only the file name is expected");
let filename = args.get(0).unwrap(); let filename = args.first().unwrap();
let path = PathBuf::from(filename.to_string()); let path = PathBuf::from(filename.to_string());
ensure!( ensure!(
path.exists() && path.is_file(), path.exists() && path.is_file(),

@ -117,10 +117,9 @@ FLAGS:
setup_logging(args.verbosity).context("failed to initialize logging")?; setup_logging(args.verbosity).context("failed to initialize logging")?;
// Before setting the working directory, resolve all the paths in args.files // Before setting the working directory, resolve all the paths in args.files
for (path, _) in args.files.iter_mut() { for (path, _) in &mut args.files {
*path = helix_stdx::path::canonicalize(&path); *path = helix_stdx::path::canonicalize(&*path);
} }
// NOTE: Set the working directory early so the correct configuration is loaded. Be aware that // NOTE: Set the working directory early so the correct configuration is loaded. Be aware that
// Application::new() depends on this logic so it must be updated if this changes. // Application::new() depends on this logic so it must be updated if this changes.
if let Some(path) = &args.working_directory { if let Some(path) = &args.working_directory {

@ -799,21 +799,25 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
if self.columns.len() > 1 { if self.columns.len() > 1 {
let active_column = self.query.active_column(self.prompt.position()); let active_column = self.query.active_column(self.prompt.position());
let header_style = cx.editor.theme.get("ui.picker.header"); let header_style = cx.editor.theme.get("ui.picker.header");
let header_column_style = cx.editor.theme.get("ui.picker.header.column");
table = table.header(Row::new(self.columns.iter().map(|column| { table = table.header(
if column.hidden { Row::new(self.columns.iter().map(|column| {
Cell::default() if column.hidden {
} else { Cell::default()
let style = if active_column.is_some_and(|name| Arc::ptr_eq(name, &column.name))
{
cx.editor.theme.get("ui.picker.header.active")
} else { } else {
header_style let style =
}; if active_column.is_some_and(|name| Arc::ptr_eq(name, &column.name)) {
cx.editor.theme.get("ui.picker.header.column.active")
} else {
header_column_style
};
Cell::from(Span::styled(Cow::from(&*column.name), style)) Cell::from(Span::styled(Cow::from(&*column.name), style))
} }
}))); }))
.style(header_style),
);
} }
use tui::widgets::TableState; use tui::widgets::TableState;

@ -649,6 +649,41 @@ async fn test_symlink_write_relative() -> anyhow::Result<()> {
Ok(()) Ok(())
} }
#[tokio::test(flavor = "multi_thread")]
#[cfg(not(target_os = "android"))]
async fn test_hardlink_write() -> anyhow::Result<()> {
let dir = tempfile::tempdir()?;
let mut file = tempfile::NamedTempFile::new_in(&dir)?;
let hardlink_path = dir.path().join("linked");
std::fs::hard_link(file.path(), &hardlink_path)?;
let mut app = helpers::AppBuilder::new()
.with_file(&hardlink_path, None)
.build()?;
test_key_sequence(
&mut app,
Some("ithe gostak distims the doshes<ret><esc>:w<ret>"),
None,
false,
)
.await?;
reload_file(&mut file).unwrap();
let mut file_content = String::new();
file.as_file_mut().read_to_string(&mut file_content)?;
assert_eq!(
LineFeedHandling::Native.apply("the gostak distims the doshes"),
file_content
);
assert!(helix_stdx::faccess::hardlink_count(&hardlink_path)? > 1);
assert!(same_file::is_same_file(file.path(), &hardlink_path)?);
Ok(())
}
async fn edit_file_with_content(file_content: &[u8]) -> anyhow::Result<()> { async fn edit_file_with_content(file_content: &[u8]) -> anyhow::Result<()> {
let mut file = tempfile::NamedTempFile::new()?; let mut file = tempfile::NamedTempFile::new()?;

@ -5,7 +5,7 @@
//! - A single line string where all graphemes have the same style is represented by a [`Span`]. //! - A single line string where all graphemes have the same style is represented by a [`Span`].
//! - A single line string where each grapheme may have its own style is represented by [`Spans`]. //! - A single line string where each grapheme may have its own style is represented by [`Spans`].
//! - A multiple line string where each grapheme may have its own style is represented by a //! - A multiple line string where each grapheme may have its own style is represented by a
//! [`Text`]. //! [`Text`].
//! //!
//! These types form a hierarchy: [`Spans`] is a collection of [`Span`] and each line of [`Text`] //! These types form a hierarchy: [`Spans`] is a collection of [`Span`] and each line of [`Text`]
//! is a [`Spans`]. //! is a [`Spans`].

@ -19,8 +19,8 @@ tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "p
parking_lot = "0.12" parking_lot = "0.12"
arc-swap = { version = "1.7.1" } arc-swap = { version = "1.7.1" }
gix = { version = "0.63.0", features = ["attributes", "status"], default-features = false, optional = true } gix = { version = "0.64.0", features = ["attributes", "status"], default-features = false, optional = true }
imara-diff = "0.1.6" imara-diff = "0.1.7"
anyhow = "1" anyhow = "1"
log = "0.4" log = "0.4"

@ -106,6 +106,7 @@ impl Serialize for Mode {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct DocumentSavedEvent { pub struct DocumentSavedEvent {
pub revision: usize, pub revision: usize,
pub save_time: SystemTime,
pub doc_id: DocumentId, pub doc_id: DocumentId,
pub path: PathBuf, pub path: PathBuf,
pub text: Rope, pub text: Rope,
@ -936,6 +937,9 @@ impl Document {
"Path is read only" "Path is read only"
)); ));
} }
// Assume it is a hardlink to prevent data loss if the metadata cant be read (e.g. on certain Windows configurations)
let is_hardlink = helix_stdx::faccess::hardlink_count(&write_path).unwrap_or(2) > 1;
let backup = if path.exists() { let backup = if path.exists() {
let path_ = write_path.clone(); let path_ = write_path.clone();
// hacks: we use tempfile to handle the complex task of creating // hacks: we use tempfile to handle the complex task of creating
@ -944,14 +948,22 @@ impl Document {
// since the path doesn't exist yet, we just want // since the path doesn't exist yet, we just want
// the path // the path
tokio::task::spawn_blocking(move || -> Option<PathBuf> { tokio::task::spawn_blocking(move || -> Option<PathBuf> {
tempfile::Builder::new() let mut builder = tempfile::Builder::new();
.prefix(path_.file_name()?) builder.prefix(path_.file_name()?).suffix(".bck");
.suffix(".bck")
.make_in(path_.parent()?, |backup| std::fs::rename(&path_, backup)) let backup_path = if is_hardlink {
.ok()? builder
.into_temp_path() .make_in(path_.parent()?, |backup| std::fs::copy(&path_, backup))
.keep() .ok()?
.ok() .into_temp_path()
} else {
builder
.make_in(path_.parent()?, |backup| std::fs::rename(&path_, backup))
.ok()?
.into_temp_path()
};
backup_path.keep().ok()
}) })
.await .await
.ok() .ok()
@ -968,8 +980,29 @@ impl Document {
} }
.await; .await;
let save_time = match fs::metadata(&write_path).await {
Ok(metadata) => metadata.modified().map_or(SystemTime::now(), |mtime| mtime),
Err(_) => SystemTime::now(),
};
if let Some(backup) = backup { if let Some(backup) = backup {
if write_result.is_err() { if is_hardlink {
let mut delete = true;
if write_result.is_err() {
// Restore backup
let _ = tokio::fs::copy(&backup, &write_path).await.map_err(|e| {
delete = false;
log::error!("Failed to restore backup on write failure: {e}")
});
}
if delete {
// Delete backup
let _ = tokio::fs::remove_file(backup)
.await
.map_err(|e| log::error!("Failed to remove backup file on write: {e}"));
}
} else if write_result.is_err() {
// restore backup // restore backup
let _ = tokio::fs::rename(&backup, &write_path) let _ = tokio::fs::rename(&backup, &write_path)
.await .await
@ -990,6 +1023,7 @@ impl Document {
let event = DocumentSavedEvent { let event = DocumentSavedEvent {
revision: current_rev, revision: current_rev,
save_time,
doc_id, doc_id,
path, path,
text: text.clone(), text: text.clone(),
@ -1048,6 +1082,25 @@ impl Document {
} }
} }
pub fn pickup_last_saved_time(&mut self) {
self.last_saved_time = match self.path() {
Some(path) => match path.metadata() {
Ok(metadata) => match metadata.modified() {
Ok(mtime) => mtime,
Err(err) => {
log::debug!("Could not fetch file system's mtime, falling back to current system time: {}", err);
SystemTime::now()
}
},
Err(err) => {
log::debug!("Could not fetch file system's mtime, falling back to current system time: {}", err);
SystemTime::now()
}
},
None => SystemTime::now(),
};
}
// Detect if the file is readonly and change the readonly field if necessary (unix only) // Detect if the file is readonly and change the readonly field if necessary (unix only)
pub fn detect_readonly(&mut self) { pub fn detect_readonly(&mut self) {
// Allows setting the flag for files the user cannot modify, like root files // Allows setting the flag for files the user cannot modify, like root files
@ -1085,9 +1138,7 @@ impl Document {
self.apply(&transaction, view.id); self.apply(&transaction, view.id);
self.append_changes_to_history(view); self.append_changes_to_history(view);
self.reset_modified(); self.reset_modified();
self.pickup_last_saved_time();
self.last_saved_time = SystemTime::now();
self.detect_indent_and_line_ending(); self.detect_indent_and_line_ending();
match provider_registry.get_diff_base(&path) { match provider_registry.get_diff_base(&path) {
@ -1126,6 +1177,7 @@ impl Document {
self.path = path; self.path = path;
self.detect_readonly(); self.detect_readonly();
self.pickup_last_saved_time();
} }
/// Set the programming language for the file and load associated data (e.g. highlighting) /// Set the programming language for the file and load associated data (e.g. highlighting)
@ -1196,7 +1248,7 @@ impl Document {
/// Initializes a new selection and view_data for the given view /// Initializes a new selection and view_data for the given view
/// if it does not already have them. /// if it does not already have them.
pub fn ensure_view_init(&mut self, view_id: ViewId) { pub fn ensure_view_init(&mut self, view_id: ViewId) {
if self.selections.get(&view_id).is_none() { if !self.selections.contains_key(&view_id) {
self.reset_selection(view_id); self.reset_selection(view_id);
} }
@ -1225,34 +1277,12 @@ impl Document {
use helix_core::Assoc; use helix_core::Assoc;
let old_doc = self.text().clone(); let old_doc = self.text().clone();
let changes = transaction.changes();
if !changes.apply(&mut self.text) {
return false;
}
let success = transaction.changes().apply(&mut self.text); if changes.is_empty() {
if success {
if emit_lsp_notification {
helix_event::dispatch(DocumentDidChange {
doc: self,
view: view_id,
old_text: &old_doc,
});
}
for selection in self.selections.values_mut() {
*selection = selection
.clone()
// Map through changes
.map(transaction.changes())
// Ensure all selections across all views still adhere to invariants.
.ensure_invariants(self.text.slice(..));
}
for view_data in self.view_data.values_mut() {
view_data.view_position.anchor = transaction
.changes()
.map_pos(view_data.view_position.anchor, Assoc::Before);
}
// if specified, the current selection should instead be replaced by transaction.selection
if let Some(selection) = transaction.selection() { if let Some(selection) = transaction.selection() {
self.selections.insert( self.selections.insert(
view_id, view_id,
@ -1263,129 +1293,160 @@ impl Document {
view: view_id, view: view_id,
}); });
} }
return true;
}
self.modified_since_accessed = true;
self.version += 1;
self.modified_since_accessed = true; for selection in self.selections.values_mut() {
*selection = selection
.clone()
// Map through changes
.map(transaction.changes())
// Ensure all selections across all views still adhere to invariants.
.ensure_invariants(self.text.slice(..));
} }
if !transaction.changes().is_empty() { for view_data in self.view_data.values_mut() {
self.version += 1; view_data.view_position.anchor = transaction
// start computing the diff in parallel .changes()
if let Some(diff_handle) = &self.diff_handle { .map_pos(view_data.view_position.anchor, Assoc::Before);
diff_handle.update_document(self.text.clone(), false); }
}
// generate revert to savepoint // generate revert to savepoint
if !self.savepoints.is_empty() { if !self.savepoints.is_empty() {
let revert = transaction.invert(&old_doc); let revert = transaction.invert(&old_doc);
self.savepoints self.savepoints
.retain_mut(|save_point| match save_point.upgrade() { .retain_mut(|save_point| match save_point.upgrade() {
Some(savepoint) => { Some(savepoint) => {
let mut revert_to_savepoint = savepoint.revert.lock(); let mut revert_to_savepoint = savepoint.revert.lock();
*revert_to_savepoint = *revert_to_savepoint =
revert.clone().compose(mem::take(&mut revert_to_savepoint)); revert.clone().compose(mem::take(&mut revert_to_savepoint));
true true
} }
None => false, None => false,
}) })
}
// update tree-sitter syntax tree
if let Some(syntax) = &mut self.syntax {
// TODO: no unwrap
let res = syntax.update(
old_doc.slice(..),
self.text.slice(..),
transaction.changes(),
);
if res.is_err() {
log::error!("TS parser failed, disabling TS for the current buffer: {res:?}");
self.syntax = None;
} }
}
// update tree-sitter syntax tree // TODO: all of that should likely just be hooks
if let Some(syntax) = &mut self.syntax { // start computing the diff in parallel
// TODO: no unwrap if let Some(diff_handle) = &self.diff_handle {
let res = syntax.update( diff_handle.update_document(self.text.clone(), false);
old_doc.slice(..), }
self.text.slice(..),
transaction.changes(), // map diagnostics over changes too
); changes.update_positions(self.diagnostics.iter_mut().map(|diagnostic| {
if res.is_err() { let assoc = if diagnostic.starts_at_word {
log::error!("TS parser failed, disabling TS for the current buffer: {res:?}"); Assoc::BeforeWord
self.syntax = None; } else {
} Assoc::After
};
(&mut diagnostic.range.start, assoc)
}));
changes.update_positions(self.diagnostics.iter_mut().filter_map(|diagnostic| {
if diagnostic.zero_width {
// for zero width diagnostics treat the diagnostic as a point
// rather than a range
return None;
} }
let assoc = if diagnostic.ends_at_word {
Assoc::AfterWord
} else {
Assoc::Before
};
Some((&mut diagnostic.range.end, assoc))
}));
self.diagnostics.retain_mut(|diagnostic| {
if diagnostic.zero_width {
diagnostic.range.end = diagnostic.range.start
} else if diagnostic.range.start >= diagnostic.range.end {
return false;
}
diagnostic.line = self.text.char_to_line(diagnostic.range.start);
true
});
let changes = transaction.changes(); self.diagnostics
.sort_by_key(|diagnostic| (diagnostic.range, diagnostic.severity, diagnostic.provider));
// map diagnostics over changes too // Update the inlay hint annotations' positions, helping ensure they are displayed in the proper place
changes.update_positions(self.diagnostics.iter_mut().map(|diagnostic| { let apply_inlay_hint_changes = |annotations: &mut Vec<InlineAnnotation>| {
let assoc = if diagnostic.starts_at_word { changes.update_positions(
Assoc::BeforeWord annotations
} else { .iter_mut()
Assoc::After .map(|annotation| (&mut annotation.char_idx, Assoc::After)),
}; );
(&mut diagnostic.range.start, assoc) };
}));
changes.update_positions(self.diagnostics.iter_mut().filter_map(|diagnostic| {
if diagnostic.zero_width {
// for zero width diagnostics treat the diagnostic as a point
// rather than a range
return None;
}
let assoc = if diagnostic.ends_at_word {
Assoc::AfterWord
} else {
Assoc::Before
};
Some((&mut diagnostic.range.end, assoc))
}));
self.diagnostics.retain_mut(|diagnostic| {
if diagnostic.zero_width {
diagnostic.range.end = diagnostic.range.start
} else if diagnostic.range.start >= diagnostic.range.end {
return false;
}
diagnostic.line = self.text.char_to_line(diagnostic.range.start);
true
});
self.diagnostics.sort_by_key(|diagnostic| { self.inlay_hints_oudated = true;
(diagnostic.range, diagnostic.severity, diagnostic.provider) for text_annotation in self.inlay_hints.values_mut() {
}); let DocumentInlayHints {
id: _,
type_inlay_hints,
parameter_inlay_hints,
other_inlay_hints,
padding_before_inlay_hints,
padding_after_inlay_hints,
} = text_annotation;
apply_inlay_hint_changes(padding_before_inlay_hints);
apply_inlay_hint_changes(type_inlay_hints);
apply_inlay_hint_changes(parameter_inlay_hints);
apply_inlay_hint_changes(other_inlay_hints);
apply_inlay_hint_changes(padding_after_inlay_hints);
}
// Update the inlay hint annotations' positions, helping ensure they are displayed in the proper place helix_event::dispatch(DocumentDidChange {
let apply_inlay_hint_changes = |annotations: &mut Vec<InlineAnnotation>| { doc: self,
changes.update_positions( view: view_id,
annotations old_text: &old_doc,
.iter_mut() });
.map(|annotation| (&mut annotation.char_idx, Assoc::After)),
);
};
self.inlay_hints_oudated = true; // if specified, the current selection should instead be replaced by transaction.selection
for text_annotation in self.inlay_hints.values_mut() { if let Some(selection) = transaction.selection() {
let DocumentInlayHints { self.selections.insert(
id: _, view_id,
type_inlay_hints, selection.clone().ensure_invariants(self.text.slice(..)),
parameter_inlay_hints, );
other_inlay_hints, helix_event::dispatch(SelectionDidChange {
padding_before_inlay_hints, doc: self,
padding_after_inlay_hints, view: view_id,
} = text_annotation; });
}
apply_inlay_hint_changes(padding_before_inlay_hints);
apply_inlay_hint_changes(type_inlay_hints);
apply_inlay_hint_changes(parameter_inlay_hints);
apply_inlay_hint_changes(other_inlay_hints);
apply_inlay_hint_changes(padding_after_inlay_hints);
}
if emit_lsp_notification { if emit_lsp_notification {
// TODO: move to hook // TODO: move to hook
// emit lsp notification // emit lsp notification
for language_server in self.language_servers() { for language_server in self.language_servers() {
let notify = language_server.text_document_did_change( let notify = language_server.text_document_did_change(
self.versioned_identifier(), self.versioned_identifier(),
&old_doc, &old_doc,
self.text(), self.text(),
changes, changes,
); );
if let Some(notify) = notify { if let Some(notify) = notify {
tokio::spawn(notify); tokio::spawn(notify);
}
} }
} }
} }
success
true
} }
fn apply_inner( fn apply_inner(
@ -1597,7 +1658,7 @@ impl Document {
} }
/// Set the document's latest saved revision to the given one. /// Set the document's latest saved revision to the given one.
pub fn set_last_saved_revision(&mut self, rev: usize) { pub fn set_last_saved_revision(&mut self, rev: usize, save_time: SystemTime) {
log::debug!( log::debug!(
"doc {} revision updated {} -> {}", "doc {} revision updated {} -> {}",
self.id, self.id,
@ -1605,7 +1666,7 @@ impl Document {
rev rev
); );
self.last_saved_revision = rev; self.last_saved_revision = rev;
self.last_saved_time = SystemTime::now(); self.last_saved_time = save_time;
} }
/// Get the document's latest saved revision. /// Get the document's latest saved revision.

@ -1376,6 +1376,11 @@ impl Editor {
} }
let is_dir = new_path.is_dir(); let is_dir = new_path.is_dir();
for ls in self.language_servers.iter_clients() { for ls in self.language_servers.iter_clients() {
// A new language server might have been started in `set_doc_path` and won't
// be initialized yet. Skip the `did_rename` notification for this server.
if !ls.is_initialized() {
continue;
}
if let Some(notification) = ls.did_rename(old_path, &new_path, is_dir) { if let Some(notification) = ls.did_rename(old_path, &new_path, is_dir) {
tokio::spawn(notification); tokio::spawn(notification);
}; };
@ -2083,7 +2088,7 @@ impl Editor {
}; };
let doc = doc_mut!(self, &save_event.doc_id); let doc = doc_mut!(self, &save_event.doc_id);
doc.set_last_saved_revision(save_event.revision); doc.set_last_saved_revision(save_event.revision, save_event.save_time);
} }
} }

@ -37,7 +37,7 @@ pub async fn select_thread_id(editor: &mut Editor, thread_id: ThreadId, force: b
debugger.thread_id = Some(thread_id); debugger.thread_id = Some(thread_id);
fetch_stack_trace(debugger, thread_id).await; fetch_stack_trace(debugger, thread_id).await;
let frame = debugger.stack_frames[&thread_id].get(0).cloned(); let frame = debugger.stack_frames[&thread_id].first().cloned();
if let Some(frame) = &frame { if let Some(frame) = &frame {
jump_to_stack_frame(editor, frame); jump_to_stack_frame(editor, frame);
} }

@ -1,3 +1,5 @@
use std::fmt::Display;
use crate::editor::Action; use crate::editor::Action;
use crate::Editor; use crate::Editor;
use crate::{DocumentId, ViewId}; use crate::{DocumentId, ViewId};
@ -73,13 +75,13 @@ impl From<helix_core::uri::UrlConversionError> for ApplyEditErrorKind {
} }
} }
impl ToString for ApplyEditErrorKind { impl Display for ApplyEditErrorKind {
fn to_string(&self) -> String { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
ApplyEditErrorKind::DocumentChanged => "document has changed".to_string(), ApplyEditErrorKind::DocumentChanged => f.write_str("document has changed"),
ApplyEditErrorKind::FileNotFound => "file not found".to_string(), ApplyEditErrorKind::FileNotFound => f.write_str("file not found"),
ApplyEditErrorKind::InvalidUrl(err) => err.to_string(), ApplyEditErrorKind::InvalidUrl(err) => f.write_str(&format!("{err}")),
ApplyEditErrorKind::IoError(err) => err.to_string(), ApplyEditErrorKind::IoError(err) => f.write_str(&format!("{err}")),
} }
} }
} }

@ -241,13 +241,23 @@ impl View {
let text_fmt = doc.text_format(viewport.width, None); let text_fmt = doc.text_format(viewport.width, None);
let annotations = self.text_annotations(doc, None); let annotations = self.text_annotations(doc, None);
// - 1 so we have at least one gap in the middle. let (scrolloff_top, scrolloff_bottom) = if CENTERING {
// a height of 6 with padding of 3 on each side will keep shifting the view back and forth (0, 0)
// as we type } else {
let scrolloff = if CENTERING { (
0 // - 1 from the top so we have at least one gap in the middle.
scrolloff.min(viewport.height.saturating_sub(1) as usize / 2),
scrolloff.min(viewport.height as usize / 2),
)
};
let (scrolloff_left, scrolloff_right) = if CENTERING {
(0, 0)
} else { } else {
scrolloff.min(viewport.height.saturating_sub(1) as usize / 2) (
// - 1 from the left so we have at least one gap in the middle.
scrolloff.min(viewport.width.saturating_sub(1) as usize / 2),
scrolloff.min(viewport.width as usize / 2),
)
}; };
let cursor = doc.selection(self.id).primary().cursor(doc_text); let cursor = doc.selection(self.id).primary().cursor(doc_text);
@ -262,14 +272,14 @@ impl View {
); );
let (new_anchor, at_top) = match off { let (new_anchor, at_top) = match off {
Ok((visual_pos, _)) if visual_pos.row < scrolloff + offset.vertical_offset => { Ok((visual_pos, _)) if visual_pos.row < scrolloff_top + offset.vertical_offset => {
if CENTERING { if CENTERING {
// cursor out of view // cursor out of view
return None; return None;
} }
(true, true) (true, true)
} }
Ok((visual_pos, _)) if visual_pos.row + scrolloff >= vertical_viewport_end => { Ok((visual_pos, _)) if visual_pos.row + scrolloff_bottom >= vertical_viewport_end => {
(true, false) (true, false)
} }
Ok((_, _)) => (false, false), Ok((_, _)) => (false, false),
@ -280,9 +290,9 @@ impl View {
if new_anchor { if new_anchor {
let v_off = if at_top { let v_off = if at_top {
scrolloff as isize scrolloff_top as isize
} else { } else {
viewport.height as isize - scrolloff as isize - 1 viewport.height as isize - scrolloff_bottom as isize - 1
}; };
(offset.anchor, offset.vertical_offset) = (offset.anchor, offset.vertical_offset) =
char_idx_at_visual_offset(doc_text, cursor, -v_off, 0, &text_fmt, &annotations); char_idx_at_visual_offset(doc_text, cursor, -v_off, 0, &text_fmt, &annotations);
@ -306,12 +316,12 @@ impl View {
.col; .col;
let last_col = offset.horizontal_offset + viewport.width.saturating_sub(1) as usize; let last_col = offset.horizontal_offset + viewport.width.saturating_sub(1) as usize;
if col > last_col.saturating_sub(scrolloff) { if col > last_col.saturating_sub(scrolloff_right) {
// scroll right // scroll right
offset.horizontal_offset += col - (last_col.saturating_sub(scrolloff)) offset.horizontal_offset += col - (last_col.saturating_sub(scrolloff_right))
} else if col < offset.horizontal_offset + scrolloff { } else if col < offset.horizontal_offset + scrolloff_left {
// scroll left // scroll left
offset.horizontal_offset = col.saturating_sub(scrolloff) offset.horizontal_offset = col.saturating_sub(scrolloff_left)
}; };
} }

@ -1533,7 +1533,7 @@ source = { git = "https://github.com/Flakebi/tree-sitter-tablegen", rev = "568dd
name = "markdown" name = "markdown"
scope = "source.md" scope = "source.md"
injection-regex = "md|markdown" injection-regex = "md|markdown"
file-types = ["md", "markdown", "mkd", "mkdn", "mdwn", "mdown", "markdn", "mdtxt", "mdtext", "workbook", { glob = "PULLREQ_EDITMSG" }] file-types = ["md", "markdown", "mdx", "mkd", "mkdn", "mdwn", "mdown", "markdn", "mdtxt", "mdtext", "workbook", { glob = "PULLREQ_EDITMSG" }]
roots = [".marksman.toml"] roots = [".marksman.toml"]
language-servers = [ "marksman", "markdown-oxide" ] language-servers = [ "marksman", "markdown-oxide" ]
indent = { tab-width = 2, unit = " " } indent = { tab-width = 2, unit = " " }
@ -1995,12 +1995,12 @@ shebangs = []
roots = ["project.godot"] roots = ["project.godot"]
auto-format = true auto-format = true
formatter = { command = "gdformat", args = ["-"] } formatter = { command = "gdformat", args = ["-"] }
comment-token = "#" comment-tokens = ["#", "##"]
indent = { tab-width = 4, unit = "\t" } indent = { tab-width = 4, unit = "\t" }
[[grammar]] [[grammar]]
name = "gdscript" name = "gdscript"
source = { git = "https://github.com/PrestonKnopp/tree-sitter-gdscript", rev = "a4b57cc3bcbfc24550e858159647e9238e7ad1ac" } source = { git = "https://github.com/PrestonKnopp/tree-sitter-gdscript", rev = "1f1e782fe2600f50ae57b53876505b8282388d77" }
[[language]] [[language]]
name = "godot-resource" name = "godot-resource"
@ -2015,7 +2015,7 @@ indent = { tab-width = 4, unit = "\t" }
[[grammar]] [[grammar]]
name = "godot-resource" name = "godot-resource"
source = { git = "https://github.com/PrestonKnopp/tree-sitter-godot-resource", rev = "b6ef0768711086a86b3297056f9ffb5cc1d77b4a" } source = { git = "https://github.com/PrestonKnopp/tree-sitter-godot-resource", rev = "2ffb90de47417018651fc3b970e5f6b67214dc9d" }
[[language]] [[language]]
name = "nu" name = "nu"
@ -3073,7 +3073,7 @@ source = { git = "https://github.com/lefp/tree-sitter-opencl", rev = "8e1d24a570
[[language]] [[language]]
name = "just" name = "just"
scope = "source.just" scope = "source.just"
file-types = [{ glob = "justfile" }, { glob = "Justfile" }, { glob = ".justfile" }, { glob = ".Justfile" }] file-types = ["just", { glob = "justfile" }, { glob = "Justfile" }, { glob = ".justfile" }, { glob = ".Justfile" }]
injection-regex = "just" injection-regex = "just"
comment-token = "#" comment-token = "#"
indent = { tab-width = 4, unit = " " } indent = { tab-width = 4, unit = " " }
@ -3082,7 +3082,7 @@ indent = { tab-width = 4, unit = " " }
[[grammar]] [[grammar]]
name = "just" name = "just"
source = { git = "https://github.com/IndianBoy42/tree-sitter-just", rev = "8af0aab79854aaf25b620a52c39485849922f766" } source = { git = "https://github.com/IndianBoy42/tree-sitter-just", rev = "379fbe36d1e441bc9414ea050ad0c85c9d6935ea" }
[[language]] [[language]]
name = "gn" name = "gn"
@ -3203,6 +3203,19 @@ grammar = "jinja2"
name = "jinja2" name = "jinja2"
source = { git = "https://github.com/varpeti/tree-sitter-jinja2", rev = "a533cd3c33aea6acb0f9bf9a56f35dcfe6a8eb53" } source = { git = "https://github.com/varpeti/tree-sitter-jinja2", rev = "a533cd3c33aea6acb0f9bf9a56f35dcfe6a8eb53" }
[[language]]
name = "jjdescription"
scope = "jj.description"
file-types = [{ glob = "*.jjdescription" }]
comment-token = "JJ:"
indent = { tab-width = 2, unit = " " }
rulers = [51, 73]
text-width = 72
[[grammar]]
name = "jjdescription"
source = { git = "https://github.com/kareigu/tree-sitter-jjdescription", rev = "2ddec6cad07b366aee276a608e1daa2c29d3caf2" }
[[grammar]] [[grammar]]
name = "wren" name = "wren"
source = { git = "https://git.sr.ht/~jummit/tree-sitter-wren", rev = "6748694be32f11e7ec6b5faeb1b48ca6156d4e06" } source = { git = "https://git.sr.ht/~jummit/tree-sitter-wren", rev = "6748694be32f11e7ec6b5faeb1b48ca6156d4e06" }
@ -3477,7 +3490,7 @@ name = "tcl"
scope = "source.tcl" scope = "source.tcl"
injection-regex = "tcl" injection-regex = "tcl"
file-types = [ "tcl" ] file-types = [ "tcl" ]
shebangs = [ "tclish", "jimsh", "wish" ] shebangs = [ "tclsh", "tclish", "jimsh", "wish" ]
comment-token = '#' comment-token = '#'
[[grammar]] [[grammar]]

@ -14,6 +14,7 @@
(attribute_call (identifier) @function) (attribute_call (identifier) @function)
(base_call (identifier) @function) (base_call (identifier) @function)
(call (identifier) @function) (call (identifier) @function)
(lambda (name) @function)
; Function definitions ; Function definitions
@ -88,6 +89,7 @@
"<<" "<<"
">>" ">>"
":=" ":="
":"
] @operator ] @operator
(annotation (identifier) @keyword.storage.modifier) (annotation (identifier) @keyword.storage.modifier)
@ -97,6 +99,7 @@
"else" "else"
"elif" "elif"
"match" "match"
"when"
] @keyword.control.conditional ] @keyword.control.conditional
[ [

@ -6,6 +6,7 @@
(pattern_section) (pattern_section)
(function_definition) (function_definition)
(lambda)
(constructor_definition) (constructor_definition)
(class_definition) (class_definition)
(enum_definition) (enum_definition)

@ -5,6 +5,8 @@
(function_definition (function_definition
(body) @function.inside) @function.around (body) @function.inside) @function.around
(lambda (body) @function.inside) @function.around
(parameters (parameters
[ [
(identifier) (identifier)
@ -15,5 +17,12 @@
(arguments (_expression) @parameter.inside @parameter.around) (arguments (_expression) @parameter.inside @parameter.around)
[
(const_statement)
(variable_statement)
(pair)
(enumerator)
] @entry.around
(comment) @comment.inside (comment) @comment.inside
(comment)+ @comment.around (comment)+ @comment.around

@ -11,8 +11,41 @@
(property (property
(path) @_is_code (path) @_is_code
(string) @injection.content)) (string) @injection.content))
(#match? @_type "type") (#eq? @_type "type")
(#match? @_is_shader "Shader") (#match? @_is_shader "Shader")
(#eq? @_is_code "code") (#eq? @_is_code "code")
(#set! injection.language "glsl") (#set! injection.language "glsl")
) )
((section
(identifier) @_is_resource
(property
(path) @_is_code
(string) @injection.content))
(#eq? @_is_resource "resource")
(#eq? @_is_code "code")
(#set! injection.language "glsl")
)
((section
(identifier) @_id
(property
(path) @_is_expression
(string) @injection.content))
(#eq? @_id "sub_resource")
(#eq? @_is_expression "expression")
(#set! injection.language "glsl")
)
((section
(attribute
(identifier) @_type
(string) @_is_shader)
(property
(path) @_is_code
(string) @injection.content))
(#eq? @_type "type")
(#match? @_is_shader "GDScript")
(#eq? @_is_code "script/source")
(#set! injection.language "gdscript")
)

@ -0,0 +1,23 @@
(section
(identifier)
(_)
(property) @class.inside
) @class.around
(attribute
(identifier)
(_) @parameter.inside) @parameter.around
(property
(path)
(_) @entry.inside) @entry.around
(pair
(_) @entry.inside) @entry.around
(array
(_) @entry.around)
(comment) @comment.inside
(comment)+ @comment.around

@ -0,0 +1,8 @@
(text) @string
(filepath) @string.special.path
(change type: "A" @diff.plus)
(change type: "D" @diff.minus)
(change type: "M" @diff.delta)
(comment) @comment

@ -1,4 +1,10 @@
(body) @fold ; From <https://github.com/IndianBoy42/tree-sitter-just/blob/6c2f018ab1d90946c0ce029bb2f7d57f56895dff/queries-flavored/helix/folds.scm>
(recipe) @fold
(interpolation) @fold ; Define collapse points
(item (_) @fold)
([
(recipe)
(string)
(external_command)
] @fold
(#trim! @fold))

@ -1,33 +1,149 @@
(assignment (NAME) @variable) ; From <https://github.com/IndianBoy42/tree-sitter-just/blob/6c2f018ab1d90946c0ce029bb2f7d57f56895dff/queries-flavored/helix/highlights.scm>
(alias (NAME) @variable)
(value (NAME) @variable)
(parameter (NAME) @variable)
(setting (NAME) @keyword)
(setting "shell" @keyword)
(call (NAME) @function) ; This file specifies how matched syntax patterns should be highlighted
(dependency (NAME) @function)
(depcall (NAME) @function)
(recipeheader (NAME) @function)
(depcall (expression) @variable.parameter) [
(parameter) @variable.parameter "export"
(variadic_parameters) @variable.parameter "import"
] @keyword.control.import
["if" "else"] @keyword.control.conditional "mod" @keyword.directive
(string) @string [
"alias"
"set"
"shell"
] @keyword
(boolean ["true" "false"]) @constant.builtin.boolean [
"if"
"else"
] @keyword.control.conditional
(comment) @comment ; Variables
; (interpolation) @string (value
(identifier) @variable)
(shebang interpreter:(TEXT) @keyword ) @comment (alias
left: (identifier) @variable)
["export" "alias" "set"] @keyword (assignment
left: (identifier) @variable)
["@" "==" "!=" "+" ":="] @operator ; Functions
[ "(" ")" "[" "]" "{{" "}}" "{" "}"] @punctuation.bracket (recipe_header
name: (identifier) @function)
(dependency
name: (identifier) @function)
(dependency_expression
name: (identifier) @function)
(function_call
name: (identifier) @function)
; Parameters
(parameter
name: (identifier) @variable.parameter)
; Namespaces
(module
name: (identifier) @namespace)
; Operators
[
":="
"?"
"=="
"!="
"=~"
"@"
"="
"$"
"*"
"+"
"&&"
"@-"
"-@"
"-"
"/"
":"
] @operator
; Punctuation
"," @punctuation.delimiter
[
"{"
"}"
"["
"]"
"("
")"
"{{"
"}}"
] @punctuation.bracket
[ "`" "```" ] @punctuation.special
; Literals
(boolean) @constant.builtin.boolean
[
(string)
(external_command)
] @string
(escape_sequence) @constant.character.escape
; Comments
(comment) @comment.line
(shebang) @keyword.directive
; highlight known settings (filtering does not always work)
(setting
left: (identifier) @keyword
(#any-of? @keyword
"allow-duplicate-recipes"
"dotenv-filename"
"dotenv-load"
"dotenv-path"
"export"
"fallback"
"ignore-comments"
"positional-arguments"
"shell"
"tempdi"
"windows-powershell"
"windows-shell"))
; highlight known attributes (filtering does not always work)
(attribute
(identifier) @attribute
(#any-of? @attribute
"private"
"allow-duplicate-recipes"
"dotenv-filename"
"dotenv-load"
"dotenv-path"
"export"
"fallback"
"ignore-comments"
"positional-arguments"
"shell"
"tempdi"
"windows-powershell"
"windows-shell"))
; Numbers are part of the syntax tree, even if disallowed
(numeric_error) @error

@ -1,3 +1,11 @@
; From <https://github.com/IndianBoy42/tree-sitter-just/blob/6c2f018ab1d90946c0ce029bb2f7d57f56895dff/queries-flavored/helix/indents.scm>
;
; This query specifies how to auto-indent logical blocks.
;
; Better documentation with diagrams is in https://docs.helix-editor.com/guides/indent.html
[ [
(recipe_body) (recipe)
] @indent (string)
(external_command)
] @indent @extend

@ -1,16 +1,84 @@
; From <https://github.com/IndianBoy42/tree-sitter-just/blob/6c2f018ab1d90946c0ce029bb2f7d57f56895dff/queries-flavored/helix/injections.scm>
;
; Specify nested languages that live within a `justfile`
; ================ Always applicable ================
((comment) @injection.content ((comment) @injection.content
(#set! injection.language "comment")) (#set! injection.language "comment"))
; Highlight the RHS of `=~` as regex
((regex_literal
(_) @injection.content)
(#set! injection.language "regex"))
; ================ Global defaults ================
; Default everything to be bash
(recipe_body
!shebang
(#set! injection.language "bash")
(#set! injection.include-children)) @injection.content
(external_command
(command_body) @injection.content
(#set! injection.language "bash"))
; ================ Global language specified ================
; Global language is set with something like one of the following:
;
; set shell := ["bash", "-c", ...]
; set shell := ["pwsh.exe"]
;
; We can extract the first item of the array, but we can't extract the language
; name from the string with something like regex. So instead we special case
; two things: powershell, which is likely to come with a `.exe` attachment that
; we need to strip, and everything else which hopefully has no extension. We
; separate this with a `#match?`.
;
; Unfortunately, there also isn't a way to allow arbitrary nesting or
; alternatively set "global" capture variables. So we can set this for item-
; level external commands, but not for e.g. external commands within an
; expression without getting _really_ annoying. Should at least look fine since
; they default to bash. Limitations...
; See https://github.com/tree-sitter/tree-sitter/issues/880 for more on that.
(source_file
(setting "shell" ":=" "[" (string) @_langstr
(#match? @_langstr ".*(powershell|pwsh|cmd).*")
(#set! injection.language "powershell"))
[
(recipe
(recipe_body
!shebang
(#set! injection.include-children)) @injection.content)
(assignment
(expression
(value
(external_command
(command_body) @injection.content))))
])
(source_file
(setting "shell" ":=" "[" (string) @injection.language
(#not-match? @injection.language ".*(powershell|pwsh|cmd).*"))
[
(recipe
(recipe_body
!shebang
(#set! injection.include-children)) @injection.content)
(shebang_recipe (assignment
(shebang (expression
interpreter:(TEXT) @injection.language) (value
(shebang_body) @injection.content (external_command
) (command_body) @injection.content))))
])
(source_file ; ================ Recipe language specified - Helix only ================
(item (setting lang:(NAME) @injection.language))
(item (recipe (body (recipe_body) @injection.content)))
)
; ((interpolation (expression) @injection.content) ; Set highlighting for recipes that specify a language using builtin shebang matching
; (#set! injection.language "just")) (recipe_body
(shebang) @injection.shebang
(#set! injection.include-children)) @injection.content

@ -1,10 +1,42 @@
(assignment (NAME) @local.definition) ; From <https://github.com/IndianBoy42/tree-sitter-just/blob/6c2f018ab1d90946c0ce029bb2f7d57f56895dff/queries-flavored/helix/locals.scm>
(alias left:(NAME) @local.definition) ;
(alias right:(NAME) @local.reference) ; This file tells us about the scope of variables so e.g. local
(value (NAME) @local.reference) ; variables override global functions with the same name
(parameter (NAME) @local.definition)
; Scope
(call (NAME) @local.reference)
(dependency (NAME) @local.reference) (recipe) @local.scope
(depcall (NAME) @local.reference)
(recipeheader (NAME) @local.definition) ; Definitions
(alias
left: (identifier) @local.definition)
(assignment
left: (identifier) @local.definition)
(module
name: (identifier) @local.definition)
(parameter
name: (identifier) @local.definition)
(recipe_header
name: (identifier) @local.definition)
; References
(alias
right: (identifier) @local.reference)
(function_call
name: (identifier) @local.reference)
(dependency
name: (identifier) @local.reference)
(dependency_expression
name: (identifier) @local.reference)
(value
(identifier) @local.reference)

@ -1,48 +1,18 @@
(body) @function.inside ; From <https://github.com/IndianBoy42/tree-sitter-just/blob/6c2f018ab1d90946c0ce029bb2f7d57f56895dff/queries-flavored/helix/textobjects.scm>
(recipe) @function.around ;
(expression ; Specify how to navigate around logical blocks in code
if:(expression) @function.inside
)
(expression
else:(expression) @function.inside
)
(interpolation (expression) @function.inside) @function.around
(settinglist (stringlist) @function.inside) @function.around
(call (NAME) @class.inside) @class.around (recipe
(dependency (NAME) @class.inside) @class.around (recipe_body) @function.inside) @function.around
(depcall (NAME) @class.inside)
(dependency) @parameter.around (parameters
(depcall) @parameter.inside ((_) @parameter.inside . ","? @parameter.around)) @parameter.around
(depcall (expression) @parameter.inside)
(stringlist (dependency_expression
(string) @parameter.inside (_) @parameter.inside) @parameter.around
. ","? @_end
; Commented out since we don't support `#make-range!` at the moment
; (#make-range! "parameter.around" @parameter.inside @_end)
)
(parameters
[(parameter)
(variadic_parameters)] @parameter.inside
. " "? @_end
; Commented out since we don't support `#make-range!` at the moment
; (#make-range! "parameter.around" @parameter.inside @_end)
)
(expression (function_call
(condition) @function.inside arguments: (sequence
) @function.around (expression) @parameter.inside) @parameter.around) @function.around
(expression
if:(expression) @function.inside
)
(expression
else:(expression) @function.inside
)
(item [(alias) (assignment) (export) (setting)]) @class.around
(recipeheader) @class.around
(line) @class.around
(comment) @comment.around (comment) @comment.around

@ -0,0 +1,165 @@
# Theme: Ao
# Author: YardQuit
# SYNTAX HIGHLIGHTING
"attribute" = { fg = "yellow" }
"type" = { fg = "white" }
"type.builtin" = { fg = "white" }
"type.parameter" = { fg = "white" }
"type.enum" = { fg = "white" }
"type.enum.variant" = { fg = "white" }
"constructor" = { fg = "orange" }
"constant" = { fg = "blue" }
"constant.character.escape" = { fg = "yellow" }
"string" = { fg = "blue" }
"string.regexp" = { fg = "yellow" }
"comment" = { fg = "gray" }
"variable" = { fg = "orange" }
"variable.parameter" = { fg = "yellow" }
"variable.other" = { fg = "green" }
"variable.other.member" = { fg = "green" }
"label" = { fg = "blue" }
"punctuation" = { fg = "white" }
"punctuation.bracket" = { fg = "orange" }
"punctuation.special" = { fg = "yellow" }
"keyword" = { fg = "red" }
"keyword.operator" = { fg = "blue" }
"keyword.directive" = { fg = "white" }
"keyword.function" = { fg = "red" }
"keyword.storage" = { fg = "red" }
"keyword.storage.modifier" = { fg = "green" }
"operator" = { fg = "white" }
"function" = { fg = "purple" }
"function.method" = { fg = "green" }
"function.macro" = { fg = "green" }
"function.special" = { fg = "yellow" }
"tag" = { fg = "green" }
"namespace" = { fg = "white" }
"diff" = { fg = "white" }
"diff.minus" = { fg = "red" }
"diff.delta" = { fg = "brown" }
# MARKUP, SYNAX HIGHLIGHTING AND INTERFACE HYBRID
"markup.heading" = { fg = "blaze_orange" }
"markup.heading.1" = { fg = "crystal_blue", modifiers = ["bold"] }
"markup.heading.2" = { fg = "sky_blue", modifiers = ["bold"] }
"markup.heading.3" = { fg = "dreamy_blue", modifiers = ["bold"] }
"markup.heading.4" = { fg = "crystal_blue" }
"markup.heading.5" = { fg = "sky_blue" }
"markup.heading.6" = { fg = "dreamy_blue" }
"markup.list" = { fg = "blaze_orange" }
"markup.bold" = { fg = "crystal_blue", modifiers = ["bold"] }
"markup.italic" = { fg = "crystal_blue", modifiers = ["italic"] }
"markup.strikethrough" = { fg = "crystal_blue", modifiers = ["crossed_out"] }
"markup.link" = { fg = "crystal_blue", underline = { color = "light_purple", style = "line" } }
"markup.link.url" = { fg = "slate_purple", underline = { color = "slate_purple", style = "line" } }
"markup.link.label" = { fg = "crystal_blue" }
"markup.link.text" = { fg = "crystal_blue", modifiers = ["bold"] }
"markup.quote" = { fg = "winter_sky", modifiers = ["italic"] }
"markup.raw" = { fg = "winter_sky" }
"markup.raw.block" = { fg = "white" }
# USER INTERFACE
"ui.background" = { bg = "deep_abyss"} # workspace background
"ui.background.separator" = { fg = "winter_sky" } # picker separator below input line (space + j)
"ui.gutter" = { bg = "deep_abyss" } # gutter
"ui.gutter.selected" = { bg = "pitch_black" } # gutter for the line the cursor is on
"ui.linenr" = { fg = "slate_gray" } # line numbers
"ui.linenr.selected" = { fg = "blaze_orange", modifiers = ["bold"] } # line number for the line the cursor is on
"ui.statusline" = { fg = "winter_sky", bg = "twilight_blue" } # statusline, fucused
"ui.statusline.inactive" = { fg = "slate_gray", bg = "pitch_black" } # statusline, unfocused
"ui.statusline.normal" = { fg = "deep_abyss", bg = "leafy_green", modifiers = ["bold"] } # statusline normal mode (if editor.color-modes is enabled)
"ui.statusline.insert" = { fg = "deep_abyss", bg = "blaze_orange", modifiers = ["bold"] } # statusline insert mode (if editor.color-modes is enabled)
"ui.statusline.select" = { fg = "deep_abyss", bg = "sky_blue", modifiers = ["bold"] } # statusline select mode (if editor.color-modes is enabled)
"ui.statusline.separator" = { fg = "winter_sky" } # separator character is statusline
"ui.bufferline" = { fg = "slate_gray", modifiers = ["bold"] } # bufferline inactive tab
"ui.bufferline.active" = { fg = "winter_sky", bg = "twilight_blue" } # bufferline active tab
"ui.bufferline.background" = { bg = "pitch_black" } # bufferline background
"ui.virtual.ruler" = { bg = "stormy_night" } # ruler columns
"ui.virtual.whitespace" = { fg = "stormy_night" } # whitespace characters
"ui.virtual.indent-guide" = { fg = "stormy_night" } # vertical indent width guides
"ui.virtual.inlay-hint" = { fg = "slate_gray" } # inlay hints of all kinds
"ui.virtual.inlay-hint.parameter" = { fg = "slate_gray" } # inlay hints of kind parameter (lsps are not required to set a kind)
"ui.virtual.inlay-hint.type" = { fg = "slate_gray" } # inlay hints of kind type (lsps are not required to set a kind)
"ui.virtual.wrap" = { fg = "slate_gray" } # soft-wrap indicator
"ui.virtual.jump-label" = { modifiers = ["reversed"] } # virtual jump labels (g + w)
"ui.selection" = { bg = "deep_purple" } # slave selections in the editing area
"ui.selection.primary" = { bg = "light_purple" } # primary selection in the editing area
"ui.cursor" = { modifiers = ["reversed"] } # only if "ui.cursor.primary.normal" isn't set
"ui.cursor.normal" = { fg = "winter_sky", bg = "twilight_blue" } # slave cursor block in normal mode
"ui.cursor.insert" = { bg = "rustic_red" } # slave cursor block in insert mode
"ui.cursor.select" = { bg = "deep_purple" } # slave cursor block in select mode
"ui.cursor.match" = { fg = "deep_abyss", bg = "blaze_orange", modifiers = ["bold"] } # matching bracket etc
"ui.cursor.primary" = { modifiers = ["reversed"] } # cursor with primary selection (has no effect due to "ui.cursor.primary.normal" is set)
"ui.cursor.primary.normal" = { fg = "deep_abyss", bg = "sky_blue" } # cursor block in normal mode
"ui.cursor.primary.insert" = { fg = "deep_abyss", bg = "ruby_glow" } # cursor block in insert mode
"ui.cursor.primary.select" = { fg = "winter_sky", bg = "deep_purple" } # cursor block in select mode (not the selected color)
"ui.cursorline.primary" = { bg = "nightfall_blue" } # line of the primary cursor
"ui.cursorline.secondary" = { bg = "midnight_thunder"} # lines of secondary cursors
"ui.cursorcolumn.primary" = { bg = "nightfall_blue" } # column of the primary cursor
"ui.cursorcolumn.secondary" = { bg = "midnight_thunder" } # columns of secondary cursors
# USER INTERFACE - MENUS AND POPUP
"ui.popup" = { fg = "winter_sky", bg = "midnight_thunder" } # documentation popups (space + k)
"ui.popup.info" = { fg = "winter_sky", bg = "nightfall_blue" } # prompt for multiple key options, menu border (space, g, z, m, etc)
"ui.window" = { fg = "slate_gray" } # borderlines separating splits
"ui.help" = { fg = "winter_sky", bg = "nightfall_blue" } # description box for commands
"ui.text" = { fg = "white" } # default text style, command prompts, popup text, etc
"ui.text.focus" = { fg = "dreamy_blue" } # the currently selected line in the picker (space j, space f, space s, etc)
"ui.text.inactive" = { fg = "slate_gray" } # same as ui.text but when the text is inactive e.g. suggestions
"ui.text.info" = { fg = "winter_sky", bg = "nightfall_blue" } # the key: command in ui.popup.info boxes (space, g, z, m, etc)
"ui.menu" = { fg = "winter_sky", bg = "midnight_thunder" } # code and command completion menus ":"
"ui.menu.selected" = { fg = "winter_sky", bg = "twilight_blue" } # selected autocomplete item
"ui.menu.scroll" = { fg = "crystal_blue", bg = "moonlight_ocean" } # scrollbar
"ui.highlight" = { underline = { color = "sky_blue", style = "line" } } # highlighted lines in the picker preview
# USER INTERFACE - DIAGNOSTICS
"warning" = { fg = "lemon_zest" } # diagnostics warning (gutter)
"error" = { fg = "ruby_glow" } # diagnostics error (gutter)
"info" = { fg = "sky_blue" } # diagnostics info (gutter)
"hint" = { fg = "walnut_brown" } # diagnostics hint (gutter)
"diagnostic" = { modifiers = ["reversed"] } # diagnostics fallback style (editing area)
"diagnostic.hint" = { fg = "deep_abyss", bg = "walnut_brown" } # diagnostics hint (editing area)
"diagnostic.info" = { fg = "winter_sky", bg = "twilight_blue" } # diagnostics info (editing area)
"diagnostic.warning" = { fg = "winter_sky", bg = "rustic_amber" } # diagnostics warning (editing area)
"diagnostic.error" = { fg = "winter_sky", bg = "rustic_red" } # diagnostics error (editing area)
# COLOR NAMES
[palette]
# PALETTE USER INTERFACE
deep_abyss = "#080d15"
stormy_night = "#254862"
nightfall_blue = "#1f2937"
midnight_thunder = "#0d1526"
twilight_blue = "#2c5484"
pitch_black = "#000000"
winter_sky = "#f3f4f6"
slate_gray = "#838a97"
blaze_orange = "#ff9000"
lemon_zest = "#ffba00"
leafy_green = "#81be83"
dreamy_blue = "#6eb0ff"
crystal_blue = "#99c7ff"
sky_blue = "#45b1e8"
moonlight_ocean = "#0c1420"
rustic_red = "#540b0c"
ruby_glow = "#fa7970"
walnut_brown = "#987654"
rustic_amber = "#9d5800"
slate_purple = "#d2a8ff"
light_purple = "#7533bd"
deep_purple = "#4c1785"
# SYNTAX HIGHLIGHTING
black = "#0d1117"
red = "#fa7970"
green = "#81be83"
yellow = "#ffba00"
orange = "#ff9000"
blue = "#45b1e8"
purple = "#d2a8ff"
brown = "#987654"
gray = "#838a97"
white = "#dadada"

@ -988,6 +988,28 @@ lines.
=================================================================
= 9.4 JUMP WITH TWO-CHARACTER LABELS =
=================================================================
Type gw to enable the 2-character labels. The start of each word
will be replaced by 2 highlighted characters. Type any sequence
of 2 highlighted characters to jump to the corresponding label,
or use ESC to drop the labels.
The 2-character labels allow you to quickly jump to any location
in the viewable selection.
1. Move the cursor to the start of the line marked '-->' below.
2. Press gw to enable the 2-character labels, and then the two
characters that replace the two letters he at the start of
here to jump to the corresponding word.
--> This is just a simple line of text.
There may be many such lines
But you really want to jump here!
This is fast with the 2-character labels.
================================================================= =================================================================
= CHAPTER 9 RECAP = = CHAPTER 9 RECAP =
================================================================= =================================================================
@ -1001,8 +1023,8 @@ lines.
* Press Ctrl-i and Ctrl-o to go forward and backward in the * Press Ctrl-i and Ctrl-o to go forward and backward in the
jumplist. jumplist.
* Type gw to enable 2-character labels, and any 2 characters to
jump to the corresponding label, or ESC to drop the labels.

Loading…
Cancel
Save