├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── extension.toml ├── languages └── rescript │ ├── brackets.scm │ ├── config.toml │ ├── highlights.scm │ ├── injections.scm │ ├── locals.scm │ └── textobjects.scm └── src └── rescript.rs /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | target 3 | *.wasm 4 | grammars/rescript 5 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anyhow" 7 | version = "1.0.82" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" 10 | 11 | [[package]] 12 | name = "bitflags" 13 | version = "2.5.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" 16 | 17 | [[package]] 18 | name = "equivalent" 19 | version = "1.0.1" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 22 | 23 | [[package]] 24 | name = "hashbrown" 25 | version = "0.14.3" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" 28 | 29 | [[package]] 30 | name = "heck" 31 | version = "0.4.1" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 34 | dependencies = [ 35 | "unicode-segmentation", 36 | ] 37 | 38 | [[package]] 39 | name = "id-arena" 40 | version = "2.2.1" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" 43 | 44 | [[package]] 45 | name = "indexmap" 46 | version = "2.2.6" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" 49 | dependencies = [ 50 | "equivalent", 51 | "hashbrown", 52 | "serde", 53 | ] 54 | 55 | [[package]] 56 | name = "itoa" 57 | version = "1.0.11" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 60 | 61 | [[package]] 62 | name = "leb128" 63 | version = "0.2.5" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" 66 | 67 | [[package]] 68 | name = "log" 69 | version = "0.4.21" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" 72 | 73 | [[package]] 74 | name = "proc-macro2" 75 | version = "1.0.80" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "a56dea16b0a29e94408b9aa5e2940a4eedbd128a1ba20e8f7ae60fd3d465af0e" 78 | dependencies = [ 79 | "unicode-ident", 80 | ] 81 | 82 | [[package]] 83 | name = "quote" 84 | version = "1.0.36" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 87 | dependencies = [ 88 | "proc-macro2", 89 | ] 90 | 91 | [[package]] 92 | name = "ryu" 93 | version = "1.0.17" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" 96 | 97 | [[package]] 98 | name = "semver" 99 | version = "1.0.22" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" 102 | 103 | [[package]] 104 | name = "serde" 105 | version = "1.0.197" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" 108 | dependencies = [ 109 | "serde_derive", 110 | ] 111 | 112 | [[package]] 113 | name = "serde_derive" 114 | version = "1.0.197" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" 117 | dependencies = [ 118 | "proc-macro2", 119 | "quote", 120 | "syn", 121 | ] 122 | 123 | [[package]] 124 | name = "serde_json" 125 | version = "1.0.115" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" 128 | dependencies = [ 129 | "itoa", 130 | "ryu", 131 | "serde", 132 | ] 133 | 134 | [[package]] 135 | name = "smallvec" 136 | version = "1.13.2" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 139 | 140 | [[package]] 141 | name = "spdx" 142 | version = "0.10.4" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "29ef1a0fa1e39ac22972c8db23ff89aea700ab96aa87114e1fb55937a631a0c9" 145 | dependencies = [ 146 | "smallvec", 147 | ] 148 | 149 | [[package]] 150 | name = "syn" 151 | version = "2.0.59" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a" 154 | dependencies = [ 155 | "proc-macro2", 156 | "quote", 157 | "unicode-ident", 158 | ] 159 | 160 | [[package]] 161 | name = "unicode-ident" 162 | version = "1.0.12" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 165 | 166 | [[package]] 167 | name = "unicode-segmentation" 168 | version = "1.11.0" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" 171 | 172 | [[package]] 173 | name = "unicode-xid" 174 | version = "0.2.4" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" 177 | 178 | [[package]] 179 | name = "wasm-encoder" 180 | version = "0.201.0" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "b9c7d2731df60006819b013f64ccc2019691deccf6e11a1804bc850cd6748f1a" 183 | dependencies = [ 184 | "leb128", 185 | ] 186 | 187 | [[package]] 188 | name = "wasm-metadata" 189 | version = "0.201.0" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "0fd83062c17b9f4985d438603cde0a5e8c5c8198201a6937f778b607924c7da2" 192 | dependencies = [ 193 | "anyhow", 194 | "indexmap", 195 | "serde", 196 | "serde_derive", 197 | "serde_json", 198 | "spdx", 199 | "wasm-encoder", 200 | "wasmparser", 201 | ] 202 | 203 | [[package]] 204 | name = "wasmparser" 205 | version = "0.201.0" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708" 208 | dependencies = [ 209 | "bitflags", 210 | "indexmap", 211 | "semver", 212 | ] 213 | 214 | [[package]] 215 | name = "wit-bindgen" 216 | version = "0.22.0" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "288f992ea30e6b5c531b52cdd5f3be81c148554b09ea416f058d16556ba92c27" 219 | dependencies = [ 220 | "bitflags", 221 | "wit-bindgen-rt", 222 | "wit-bindgen-rust-macro", 223 | ] 224 | 225 | [[package]] 226 | name = "wit-bindgen-core" 227 | version = "0.22.0" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "e85e72719ffbccf279359ad071497e47eb0675fe22106dea4ed2d8a7fcb60ba4" 230 | dependencies = [ 231 | "anyhow", 232 | "wit-parser", 233 | ] 234 | 235 | [[package]] 236 | name = "wit-bindgen-rt" 237 | version = "0.22.0" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "fcb8738270f32a2d6739973cbbb7c1b6dd8959ce515578a6e19165853272ee64" 240 | 241 | [[package]] 242 | name = "wit-bindgen-rust" 243 | version = "0.22.0" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "d8a39a15d1ae2077688213611209849cad40e9e5cccf6e61951a425850677ff3" 246 | dependencies = [ 247 | "anyhow", 248 | "heck", 249 | "indexmap", 250 | "wasm-metadata", 251 | "wit-bindgen-core", 252 | "wit-component", 253 | ] 254 | 255 | [[package]] 256 | name = "wit-bindgen-rust-macro" 257 | version = "0.22.0" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "d376d3ae5850526dfd00d937faea0d81a06fa18f7ac1e26f386d760f241a8f4b" 260 | dependencies = [ 261 | "anyhow", 262 | "proc-macro2", 263 | "quote", 264 | "syn", 265 | "wit-bindgen-core", 266 | "wit-bindgen-rust", 267 | ] 268 | 269 | [[package]] 270 | name = "wit-component" 271 | version = "0.201.0" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "421c0c848a0660a8c22e2fd217929a0191f14476b68962afd2af89fd22e39825" 274 | dependencies = [ 275 | "anyhow", 276 | "bitflags", 277 | "indexmap", 278 | "log", 279 | "serde", 280 | "serde_derive", 281 | "serde_json", 282 | "wasm-encoder", 283 | "wasm-metadata", 284 | "wasmparser", 285 | "wit-parser", 286 | ] 287 | 288 | [[package]] 289 | name = "wit-parser" 290 | version = "0.201.0" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "196d3ecfc4b759a8573bf86a9b3f8996b304b3732e4c7de81655f875f6efdca6" 293 | dependencies = [ 294 | "anyhow", 295 | "id-arena", 296 | "indexmap", 297 | "log", 298 | "semver", 299 | "serde", 300 | "serde_derive", 301 | "serde_json", 302 | "unicode-xid", 303 | "wasmparser", 304 | ] 305 | 306 | [[package]] 307 | name = "zed_extension_api" 308 | version = "0.1.0" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "594fd10dd0f2f853eb243e2425e7c95938cef49adb81d9602921d002c5e6d9d9" 311 | dependencies = [ 312 | "serde", 313 | "serde_json", 314 | "wit-bindgen", 315 | ] 316 | 317 | [[package]] 318 | name = "zed_rescript" 319 | version = "0.2.0" 320 | dependencies = [ 321 | "zed_extension_api", 322 | ] 323 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zed_rescript" 3 | version = "0.2.0" 4 | edition = "2021" 5 | publish = false 6 | license = "MIT" 7 | 8 | # [lints] 9 | # workspace = true 10 | 11 | [lib] 12 | path = "src/rescript.rs" 13 | crate-type = ["cdylib"] 14 | 15 | [dependencies] 16 | zed_extension_api = "0.1.0" 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Karolis Narkevicius 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | mkdir -p target/rescript 3 | zed-extension --source-dir . --output-dir target/ --scratch-dir target/ 4 | tar -xzf target/archive.tar.gz -C target/rescript 5 | cp -Rf target/rescript ~/Library/Application\ Support/Zed/extensions/installed/ 6 | tree ~/Library/Application\ Support/Zed/extensions/installed/rescript 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rescript-zed 2 | 3 | ReScript support for [Zed](zed.dev) editor. 4 | 5 | This extension plugs in the following projects: 6 | 7 | - [tree-sitter-rescript](https://github.com/rescript-lang/tree-sitter-rescript) parser 8 | - [@rescript/language-server](https://github.com/rescript-lang/rescript-vscode) LSP 9 | 10 | ## Developing 11 | 12 | Zed and it's support for extensions is being actively developed. The current workflow that can be used to build this extension locally and install it into Zed is: 13 | 14 | Clone zed and build the `zed-extension` cli: 15 | 16 | git clone git@github.com:zed-industries/zed.git 17 | cd zed 18 | cargo build --release --package extension_cli 19 | ln -sf "$(pwd -P)/target/release/zed-extension" /usr/local/bin/zed-extension 20 | 21 | Build and install the extension locally 22 | 23 | make build 24 | 25 | Tail zed logs 26 | 27 | tail -f ~/Library/Logs/Zed/Zed.log 28 | 29 | After opening a ReScript file, open Cmd + Shift + P nav and find 30 | 31 | language selector: toggle 32 | 33 | And to see the language server logs, open Cmd + Shift + P nav and find 34 | 35 | debug: open language server logs 36 | -------------------------------------------------------------------------------- /extension.toml: -------------------------------------------------------------------------------- 1 | id = "rescript" 2 | name = "ReScript" 3 | description = "ReScript support." 4 | version = "0.2.0" 5 | schema_version = 1 6 | authors = ["Karolis Narkevicius "] 7 | repository = "https://github.com/humaans/rescript-zed" 8 | 9 | [language_servers.rescript-language-server] 10 | name = "rescript-language-server" 11 | language = "ReScript" 12 | 13 | [grammars.rescript] 14 | repository = "https://github.com/rescript-lang/tree-sitter-rescript" 15 | commit = "4606cd81c4c31d1d02390fee530858323410a74c" 16 | -------------------------------------------------------------------------------- /languages/rescript/brackets.scm: -------------------------------------------------------------------------------- 1 | ("(" @open ")" @close) 2 | ("[" @open "]" @close) 3 | ("{" @open "}" @close) 4 | -------------------------------------------------------------------------------- /languages/rescript/config.toml: -------------------------------------------------------------------------------- 1 | name = "ReScript" 2 | grammar = "rescript" 3 | path_suffixes = ["res", "resi"] 4 | line_comments = ["// "] 5 | block_comment = ["/* ", " */"] 6 | brackets = [ 7 | { start = "{", end = "}", close = true, newline = true }, 8 | { start = "[", end = "]", close = true, newline = true }, 9 | { start = "(", end = ")", close = true, newline = true }, 10 | { start = "\"", end = "\"", close = true, newline = false, not_in = [ 11 | "string", 12 | ] }, 13 | { start = "'", end = "'", close = true, newline = false, not_in = [ 14 | "string", 15 | "comment", 16 | ] }, 17 | ] 18 | tab_size = 2 19 | -------------------------------------------------------------------------------- /languages/rescript/highlights.scm: -------------------------------------------------------------------------------- 1 | (comment) @comment 2 | 3 | ; Identifiers 4 | ;------------ 5 | 6 | ; Escaped identifiers like \"+." 7 | ((value_identifier) @constant.macro 8 | (#match? @constant.macro "^\\.*$")) 9 | 10 | 11 | ((value_identifier) @variable) 12 | 13 | [ 14 | (type_identifier) 15 | (unit_type) 16 | (list) 17 | (list_pattern) 18 | ] @type 19 | 20 | ((type_identifier) @type.builtin 21 | (#any-of? @type.builtin 22 | "int" "char" "string" "float" "bool" "unit")) 23 | 24 | [ 25 | (variant_identifier) 26 | (polyvar_identifier) 27 | ] @constructor 28 | 29 | (record_type_field (property_identifier) @property) 30 | (record_field (property_identifier) @property) 31 | (object (field (property_identifier) @property)) 32 | (object_type (field (property_identifier) @property)) 33 | (module_identifier) @module 34 | 35 | (member_expression (property_identifier) @variable.member) 36 | 37 | (value_identifier_path 38 | (module_identifier) 39 | (value_identifier) @variable) 40 | 41 | 42 | (record_pattern 43 | (value_identifier_path 44 | (value_identifier) @variable.member)) 45 | 46 | (record_pattern 47 | (value_identifier) @variable) 48 | 49 | (labeled_argument 50 | label: (value_identifier) @variable.parameter) 51 | 52 | 53 | ; Parameters 54 | ;---------------- 55 | 56 | (list_pattern (value_identifier) @variable.parameter) 57 | (spread_pattern (value_identifier) @variable.parameter) 58 | 59 | ; String literals 60 | ;---------------- 61 | 62 | [ 63 | (string) 64 | (template_string) 65 | ] @string 66 | 67 | 68 | (character) @character 69 | (escape_sequence) @string.escape 70 | 71 | ; Other literals 72 | ;--------------- 73 | 74 | [ 75 | (true) 76 | (false) 77 | ] @boolean 78 | 79 | (number) @number 80 | (polyvar) @constructor 81 | (polyvar_string) @constructor 82 | 83 | ; Functions 84 | ;---------- 85 | 86 | ; parameter(s) in parens 87 | 88 | (parameter (value_identifier) @variable.parameter) 89 | (labeled_parameter (value_identifier) @variable.parameter) 90 | 91 | ; single parameter with no parens 92 | (function parameter: (value_identifier) @variable.parameter) 93 | 94 | ; first-level descructuring (required for nvim-tree-sitter as it only matches direct 95 | ; children and the above patterns do not match destructuring patterns in NeoVim) 96 | (parameter (tuple_pattern (tuple_item_pattern (value_identifier) @variable.parameter))) 97 | (parameter (array_pattern (value_identifier) @variable.parameter)) 98 | (parameter (record_pattern (value_identifier) @variable.parameter)) 99 | 100 | ; function identifier in let binding 101 | (let_binding 102 | pattern: (value_identifier) @function 103 | body: (function)) 104 | 105 | ; function calls 106 | 107 | (call_expression 108 | function: (value_identifier_path 109 | _ 110 | (value_identifier) @function.call)) 111 | 112 | (call_expression 113 | function: (value_identifier) @function.call) 114 | 115 | ; highlight the right-hand side of a pipe operator as a function call 116 | (pipe_expression 117 | _ 118 | [(value_identifier_path 119 | _ 120 | (value_identifier) @function.call) 121 | (value_identifier) @function.call]) 122 | 123 | 124 | ; Meta 125 | ;----- 126 | 127 | (decorator_identifier) @attribute 128 | 129 | (extension_identifier) @keyword 130 | ("%") @keyword 131 | 132 | ; Misc 133 | ;----- 134 | 135 | (polyvar_type_pattern "#" @constructor) 136 | 137 | [ 138 | "include" 139 | "open" 140 | ] @keyword.import 141 | 142 | 143 | [ 144 | "private" 145 | "mutable" 146 | "rec" 147 | ] @keyword.modifier 148 | 149 | [ 150 | "type" 151 | ] @keyword.type 152 | 153 | [ 154 | "and" 155 | "with" 156 | "as" 157 | ] @keyword.operator 158 | 159 | [ 160 | "export" 161 | "external" 162 | "let" 163 | "module" 164 | "assert" 165 | "await" 166 | "lazy" 167 | "constraint" 168 | ] @keyword 169 | 170 | (("await") @keyword.coroutine) 171 | 172 | ((function "async" @keyword.coroutine)) 173 | 174 | (module_unpack "unpack" @keyword) 175 | 176 | [ 177 | "if" 178 | "else" 179 | "switch" 180 | "when" 181 | ] @keyword.conditional 182 | 183 | [ 184 | "exception" 185 | "try" 186 | "catch" 187 | ] @keyword.exception 188 | 189 | (call_expression 190 | function: (value_identifier) @keyword.exception 191 | (#eq? @keyword.exception "raise")) 192 | 193 | [ 194 | "for" 195 | "in" 196 | "to" 197 | "downto" 198 | "while" 199 | ] @keyword.repeat 200 | 201 | [ 202 | "." 203 | "," 204 | "|" 205 | ":" 206 | ] @punctuation.delimiter 207 | 208 | [ 209 | "++" 210 | "+" 211 | "+." 212 | "-" 213 | "-." 214 | "*" 215 | "**" 216 | "*." 217 | "/." 218 | "<=" 219 | "==" 220 | "===" 221 | "!" 222 | "!=" 223 | "!==" 224 | ">=" 225 | "&&" 226 | "||" 227 | "=" 228 | ":=" 229 | "->" 230 | "|>" 231 | ":>" 232 | "+=" 233 | "=>" 234 | (uncurry) 235 | ] @operator 236 | 237 | ; Explicitly enclose these operators with binary_expression 238 | ; to avoid confusion with JSX tag delimiters 239 | (binary_expression ["<" ">" "/"] @operator) 240 | 241 | [ 242 | "(" 243 | ")" 244 | "{" 245 | "}" 246 | "[" 247 | "]" 248 | "<" 249 | ">" 250 | ] @punctuation.bracket 251 | 252 | (unit ["(" ")"] @constant.builtin) 253 | 254 | (template_substitution 255 | "${" @punctuation.special 256 | "}" @punctuation.special) @none 257 | 258 | (polyvar_type 259 | [ 260 | "[" 261 | "[>" 262 | "[<" 263 | "]" 264 | ] @punctuation.bracket) 265 | 266 | [ 267 | "~" 268 | "?" 269 | ".." 270 | "..." 271 | ] @punctuation.special 272 | 273 | (ternary_expression ["?" ":"] @keyword.conditional.ternary) 274 | 275 | ; JSX 276 | ;---------- 277 | (jsx_identifier) @tag 278 | (jsx_element 279 | open_tag: (jsx_opening_element ["<" ">"] @tag.delimiter)) 280 | (jsx_element 281 | close_tag: (jsx_closing_element ["<" "/" ">"] @tag.delimiter)) 282 | (jsx_self_closing_element ["/" ">" "<"] @tag.delimiter) 283 | (jsx_fragment [">" "<" "/"] @tag.delimiter) 284 | (jsx_attribute (property_identifier) @tag.attribute) 285 | 286 | ; Error 287 | ;---------- 288 | 289 | (ERROR) @error 290 | -------------------------------------------------------------------------------- /languages/rescript/injections.scm: -------------------------------------------------------------------------------- 1 | ((comment) @injection.content (#set! injection.language "comment")) 2 | 3 | ; %re 4 | (extension_expression 5 | (extension_identifier) @_name 6 | (#eq? @_name "re") 7 | (expression_statement (_) @injection.content (#set! injection.language "regex"))) 8 | 9 | ; %raw 10 | (extension_expression 11 | (extension_identifier) @_name 12 | (#eq? @_name "raw") 13 | (expression_statement 14 | (_ (_) @injection.content (#set! injection.language "javascript")))) 15 | 16 | ; %graphql 17 | (extension_expression 18 | (extension_identifier) @_name 19 | (#eq? @_name "graphql") 20 | (expression_statement 21 | (_ (_) @injection.content (#set! injection.language "graphql")))) 22 | 23 | ; %relay 24 | (extension_expression 25 | (extension_identifier) @_name 26 | (#eq? @_name "relay") 27 | (expression_statement 28 | (_ (_) @injection.content (#set! injection.language "graphql") ))) 29 | -------------------------------------------------------------------------------- /languages/rescript/locals.scm: -------------------------------------------------------------------------------- 1 | (switch_expression) @scope 2 | 3 | ; Definitions 4 | ;------------ 5 | (type_declaration) @definition.type 6 | (let_binding) @definition.var 7 | (module_declaration) @definition.namespace 8 | -------------------------------------------------------------------------------- /languages/rescript/textobjects.scm: -------------------------------------------------------------------------------- 1 | ; Queries for nvim-treesitter/nvim-treesitter-textobjects 2 | ;-------------------------------------------------------- 3 | 4 | ; Classes (modules) 5 | ;------------------ 6 | 7 | (module_binding definition: ((_) @class.inner)) @class.outer 8 | 9 | ; Blocks 10 | ;------- 11 | 12 | (block (_) @block.inner) @block.outer 13 | 14 | ; Functions 15 | ;---------- 16 | 17 | (function body: (_) @function.inner) @function.outer 18 | 19 | ; Calls 20 | ;------ 21 | 22 | (call_expression arguments: ((_) @call.inner)) @call.outer 23 | 24 | ; Comments 25 | ;--------- 26 | 27 | (comment) @comment.outer 28 | 29 | ; Parameters 30 | ;----------- 31 | 32 | (function parameter: (_) @parameter.inner @parameter.outer) 33 | 34 | (formal_parameters 35 | "," @_formal_parameters_start 36 | . (_) @parameter.inner 37 | (#make-range! "parameter.outer" @_formal_parameters_start @parameter.inner)) 38 | (formal_parameters 39 | . (_) @parameter.inner 40 | . ","? @_formal_parameters_end 41 | (#make-range! "parameter.outer" @parameter.inner @_formal_parameters_end)) 42 | 43 | (arguments 44 | "," @_arguments_start 45 | . (_) @parameter.inner 46 | (#make-range! "parameter.outer" @_arguments_start @parameter.inner)) 47 | (arguments 48 | . (_) @parameter.inner 49 | . ","? @_arguments_end 50 | (#make-range! "parameter.outer" @parameter.inner @_arguments_end)) 51 | 52 | (function_type_parameters 53 | "," @_function_type_parameters_start 54 | . (_) @parameter.inner 55 | (#make-range! "parameter.outer" @_function_type_parameters_start @parameter.inner)) 56 | (function_type_parameters 57 | . (_) @parameter.inner 58 | . ","? @_function_type_parameters_end 59 | (#make-range! "parameter.outer" @parameter.inner @_function_type_parameters_end)) 60 | 61 | (functor_parameters 62 | "," @_functor_parameters_start 63 | . (_) @parameter.inner 64 | (#make-range! "parameter.outer" @_functor_parameters_start @parameter.inner)) 65 | (functor_parameters 66 | . (_) @parameter.inner 67 | . ","? @_functor_parameters_end 68 | (#make-range! "parameter.outer" @parameter.inner @_functor_parameters_end)) 69 | 70 | (type_parameters 71 | "," @_type_parameters_start 72 | . (_) @parameter.inner 73 | (#make-range! "parameter.outer" @_type_parameters_start @parameter.inner)) 74 | (type_parameters 75 | . (_) @parameter.inner 76 | . ","? @_type_parameters_end 77 | (#make-range! "parameter.outer" @parameter.inner @_type_parameters_end)) 78 | 79 | (type_arguments 80 | "," @_type_arguments_start 81 | . (_) @parameter.inner 82 | (#make-range! "parameter.outer" @_type_arguments_start @parameter.inner)) 83 | (type_arguments 84 | . (_) @parameter.inner 85 | . ","? @_type_arguments_end 86 | (#make-range! "parameter.outer" @parameter.inner @_type_arguments_end)) 87 | 88 | (decorator_arguments 89 | "," @_decorator_arguments_start 90 | . (_) @parameter.inner 91 | (#make-range! "parameter.outer" @_decorator_arguments_start @parameter.inner)) 92 | (decorator_arguments 93 | . (_) @parameter.inner 94 | . ","? @_arguments_end 95 | (#make-range! "parameter.outer" @parameter.inner @_arguments_end)) 96 | 97 | (variant_parameters 98 | "," @_variant_parameters_start 99 | . (_) @parameter.inner 100 | (#make-range! "parameter.outer" @_variant_parameters_start @parameter.inner)) 101 | (variant_parameters 102 | . (_) @parameter.inner 103 | . ","? @_variant_parameters_end 104 | (#make-range! "parameter.outer" @parameter.inner @_variant_parameters_end)) 105 | 106 | (polyvar_parameters 107 | "," @_polyvar_parameters_start 108 | . (_) @parameter.inner 109 | (#make-range! "parameter.outer" @_polyvar_parameters_start @parameter.inner)) 110 | (polyvar_parameters 111 | . (_) @parameter.inner 112 | . ","? @_polyvar_parameters_end 113 | (#make-range! "parameter.outer" @parameter.inner @_polyvar_parameters_end)) 114 | -------------------------------------------------------------------------------- /src/rescript.rs: -------------------------------------------------------------------------------- 1 | use std::{env, fs}; 2 | use zed_extension_api::{self as zed, Result}; 3 | 4 | const SERVER_PATH: &str = "node_modules/.bin/rescript-language-server"; 5 | const PACKAGE_NAME: &str = "@rescript/language-server"; 6 | 7 | struct ReScriptExtension { 8 | did_find_server: bool, 9 | } 10 | 11 | impl ReScriptExtension { 12 | fn server_exists(&self) -> bool { 13 | fs::metadata(SERVER_PATH).map_or(false, |stat| stat.is_file()) 14 | } 15 | 16 | fn server_script_path(&mut self, server_id: &zed::LanguageServerId) -> Result { 17 | let server_exists = self.server_exists(); 18 | self.did_find_server = true; 19 | if self.did_find_server && server_exists { 20 | return Ok(SERVER_PATH.to_string()); 21 | } 22 | 23 | zed::set_language_server_installation_status( 24 | &server_id, 25 | &zed::LanguageServerInstallationStatus::CheckingForUpdate, 26 | ); 27 | let version = zed::npm_package_latest_version(PACKAGE_NAME)?; 28 | 29 | if !server_exists 30 | || zed::npm_package_installed_version(PACKAGE_NAME)?.as_ref() != Some(&version) 31 | { 32 | zed::set_language_server_installation_status( 33 | &server_id, 34 | &zed::LanguageServerInstallationStatus::Downloading, 35 | ); 36 | let result = zed::npm_install_package(PACKAGE_NAME, &version); 37 | match result { 38 | Ok(()) => { 39 | if !self.server_exists() { 40 | Err(format!( 41 | "installed package '{PACKAGE_NAME}' did not contain expected path '{SERVER_PATH}'", 42 | ))?; 43 | } 44 | } 45 | Err(error) => { 46 | if !self.server_exists() { 47 | Err(error)?; 48 | } 49 | } 50 | } 51 | } 52 | 53 | self.did_find_server = true; 54 | Ok(SERVER_PATH.to_string()) 55 | } 56 | } 57 | 58 | impl zed::Extension for ReScriptExtension { 59 | fn new() -> Self { 60 | Self { 61 | did_find_server: false, 62 | } 63 | } 64 | 65 | fn language_server_command( 66 | &mut self, 67 | server_id: &zed::LanguageServerId, 68 | _worktree: &zed::Worktree, 69 | ) -> Result { 70 | let server_path = self.server_script_path(server_id)?; 71 | Ok(zed::Command { 72 | command: zed::node_binary_path()?, 73 | args: vec![ 74 | env::current_dir() 75 | .unwrap() 76 | .join(&server_path) 77 | .to_string_lossy() 78 | .to_string(), 79 | "--stdio".to_string(), 80 | ], 81 | env: Default::default(), 82 | }) 83 | } 84 | } 85 | 86 | zed::register_extension!(ReScriptExtension); 87 | --------------------------------------------------------------------------------