├── .gitattributes ├── .gitignore ├── .zed └── settings.json ├── Cargo.lock ├── Cargo.toml ├── README.md ├── extension.toml ├── languages └── swift │ ├── brackets.scm │ ├── config.toml │ ├── highlights.scm │ ├── indents.scm │ ├── injections.scm │ ├── locals.scm │ ├── outline.scm │ ├── runnables.scm │ ├── tags.scm │ ├── tasks.json │ └── textobjects.scm └── src ├── language_server.rs └── swift.rs /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | *.wasm 3 | .idea 4 | grammars 5 | target 6 | -------------------------------------------------------------------------------- /.zed/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "file_types": { 3 | "JSONC": ["**/tasks.json"] 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 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.81" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" 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.198" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" 108 | dependencies = [ 109 | "serde_derive", 110 | ] 111 | 112 | [[package]] 113 | name = "serde_derive" 114 | version = "1.0.198" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" 117 | dependencies = [ 118 | "proc-macro2", 119 | "quote", 120 | "syn", 121 | ] 122 | 123 | [[package]] 124 | name = "serde_json" 125 | version = "1.0.116" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" 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.60" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" 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.2.0" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "9fd16b8b30a9dc920fc1678ff852f696b5bdf5b5843bc745a128be0aac29859e" 311 | dependencies = [ 312 | "serde", 313 | "serde_json", 314 | "wit-bindgen", 315 | ] 316 | 317 | [[package]] 318 | name = "zed_swift" 319 | version = "0.4.1" 320 | dependencies = [ 321 | "zed_extension_api", 322 | ] 323 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zed_swift" 3 | version = "0.4.1" 4 | edition = "2021" 5 | publish = false 6 | 7 | [lib] 8 | path = "src/swift.rs" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | zed_extension_api = "0.2.0" 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zed-swift-extension 2 | 3 | Swift syntax highlighting & language server for [Zed](https://github.com/zed-industries/zed) 4 | -------------------------------------------------------------------------------- /extension.toml: -------------------------------------------------------------------------------- 1 | id = "swift" 2 | name = "Swift" 3 | description = "Swift support." 4 | version = "0.4.1" 5 | schema_version = 1 6 | authors = [ 7 | "ejjonny ", 8 | "Samuser107 L.Longheval ", 9 | ] 10 | repository = "https://github.com/zed-extensions/swift" 11 | 12 | [language_servers.sourcekit-lsp] 13 | name = "sourcekit-lsp" 14 | language = "Swift" 15 | 16 | [grammars.swift] 17 | repository = "https://github.com/alex-pinkus/tree-sitter-swift" 18 | commit = "33e56e4e2a6455f3fd4e04cfe5c7a314d498612d" 19 | -------------------------------------------------------------------------------- /languages/swift/brackets.scm: -------------------------------------------------------------------------------- 1 | ("(" @open ")" @close) 2 | ("[" @open "]" @close) 3 | ("{" @open "}" @close) 4 | ("\"" @open "\"" @close) 5 | -------------------------------------------------------------------------------- /languages/swift/config.toml: -------------------------------------------------------------------------------- 1 | name = "Swift" 2 | grammar = "swift" 3 | path_suffixes = ["swift", "swiftinterface"] 4 | line_comments = ["// "] 5 | block_comment = ["/*", "*/"] 6 | autoclose_before = ")}]" 7 | brackets = [ 8 | { start = "{", end = "}", close = true, newline = true }, 9 | { start = "[", end = "]", close = true, newline = true }, 10 | { start = "(", end = ")", close = true, newline = true }, 11 | { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] }, 12 | { start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] }, 13 | { start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] }, 14 | ] 15 | documentation = { start = "/*", end = "*/", prefix = "* ", tab_size = 1 } 16 | -------------------------------------------------------------------------------- /languages/swift/highlights.scm: -------------------------------------------------------------------------------- 1 | [ "." ";" ":" "," ] @punctuation.delimiter 2 | [ "\\(" "(" ")" "[" "]" "{" "}" ] @punctuation.bracket ; TODO: "\\(" ")" in interpolations should be @punctuation.special 3 | 4 | ; Identifiers 5 | (attribute) @variable 6 | (type_identifier) @type 7 | (self_expression) @variable.builtin 8 | (user_type (type_identifier) @variable.builtin (#eq? @variable.builtin "Self")) 9 | 10 | ; Declarations 11 | "func" @keyword.function 12 | [ 13 | (visibility_modifier) 14 | (member_modifier) 15 | (function_modifier) 16 | (property_modifier) 17 | (parameter_modifier) 18 | (inheritance_modifier) 19 | (mutation_modifier) 20 | ] @keyword 21 | 22 | (function_declaration (simple_identifier) @function.method) 23 | (init_declaration ["init" @constructor]) 24 | (deinit_declaration ["deinit" @constructor]) 25 | (throws) @keyword 26 | "async" @keyword 27 | "await" @keyword 28 | (where_keyword) @keyword 29 | (parameter external_name: (simple_identifier) @property) 30 | (parameter name: (simple_identifier) @property) 31 | (type_parameter (type_identifier) @property) 32 | (inheritance_constraint (identifier (simple_identifier) @property)) 33 | (equality_constraint (identifier (simple_identifier) @property)) 34 | (pattern bound_identifier: (simple_identifier)) @variable 35 | 36 | [ 37 | "typealias" 38 | "struct" 39 | "class" 40 | "actor" 41 | "enum" 42 | "protocol" 43 | "extension" 44 | "indirect" 45 | "nonisolated" 46 | "override" 47 | "convenience" 48 | "required" 49 | "mutating" 50 | "nonmutating" 51 | "associatedtype" 52 | ] @keyword 53 | 54 | (opaque_type ["some" @keyword]) 55 | (existential_type ["any" @keyword]) 56 | 57 | (precedence_group_declaration 58 | ["precedencegroup" @keyword] 59 | (simple_identifier) @type) 60 | (precedence_group_attribute 61 | (simple_identifier) @keyword 62 | [(simple_identifier) @type 63 | (boolean_literal) @boolean]) 64 | 65 | [ 66 | (getter_specifier) 67 | (setter_specifier) 68 | (modify_specifier) 69 | ] @keyword 70 | 71 | (class_body (property_declaration (pattern (simple_identifier) @variable.parameter))) 72 | (protocol_property_declaration (pattern (simple_identifier) @variable.parameter)) 73 | 74 | (value_argument 75 | name: (value_argument_label) @property) 76 | 77 | (import_declaration 78 | "import" @keyword.import) 79 | 80 | (enum_entry 81 | "case" @keyword) 82 | 83 | ; Function calls 84 | (call_expression (simple_identifier) @function.call) ; foo() 85 | (call_expression ; foo.bar.baz(): highlight the baz() 86 | (navigation_expression 87 | (navigation_suffix (simple_identifier) @function.call))) 88 | ((navigation_expression 89 | (simple_identifier) @type) ; SomeType.method(): highlight SomeType as a type 90 | (#match? @property "^[A-Z]")) 91 | (call_expression (simple_identifier) @keyword (#eq? @keyword "defer")) ; defer { ... } 92 | 93 | (try_operator) @operator 94 | (try_operator ["try" @keyword]) 95 | 96 | (directive) @function.macro 97 | (diagnostic) @function.macro 98 | 99 | ; Statements 100 | (for_statement ["for" @keyword.repeat]) 101 | (for_statement ["in" @keyword.repeat]) 102 | (for_statement (pattern) @variable) 103 | (else) @keyword 104 | (as_operator) @keyword 105 | 106 | ["while" "repeat" "continue" "break"] @keyword.repeat 107 | 108 | ["let" "var"] @keyword 109 | 110 | (guard_statement 111 | "guard" @keyword.conditional) 112 | 113 | (if_statement 114 | "if" @keyword.conditional) 115 | 116 | (switch_statement 117 | "switch" @keyword.conditional) 118 | 119 | (switch_entry 120 | "case" @keyword) 121 | 122 | (switch_entry 123 | "fallthrough" @keyword) 124 | 125 | (switch_entry 126 | (default_keyword) @keyword) 127 | 128 | "return" @keyword.return 129 | 130 | (ternary_expression 131 | ["?" ":"] @keyword.conditional) 132 | 133 | ["do" (throw_keyword) (catch_keyword)] @keyword 134 | 135 | (statement_label) @label 136 | 137 | ; Comments 138 | [ 139 | (comment) 140 | (multiline_comment) 141 | ] @comment @spell 142 | 143 | ((comment) @comment.documentation 144 | (#lua-match? @comment.documentation "^///[^/]")) 145 | 146 | ((comment) @comment.documentation 147 | (#lua-match? @comment.documentation "^///$")) 148 | 149 | ((multiline_comment) @comment.documentation 150 | (#lua-match? @comment.documentation "^/[*][*][^*].*[*]/$")) 151 | 152 | ; String literals 153 | (line_str_text) @string 154 | (str_escaped_char) @string 155 | (multi_line_str_text) @string 156 | (raw_str_part) @string 157 | (raw_str_end_part) @string 158 | (raw_str_interpolation_start) @punctuation.special 159 | ["\"" "\"\"\""] @string 160 | 161 | ; Lambda literals 162 | (lambda_literal ["in" @keyword.operator]) 163 | 164 | ; Basic literals 165 | [ 166 | (integer_literal) 167 | (hex_literal) 168 | (oct_literal) 169 | (bin_literal) 170 | ] @number 171 | (real_literal) @number.float 172 | (boolean_literal) @boolean 173 | "nil" @keyword 174 | 175 | ; Regex literals 176 | (regex_literal) @string.regexp 177 | 178 | ; Operators 179 | (custom_operator) @operator 180 | 181 | [ 182 | "!" 183 | "?" 184 | "+" 185 | "-" 186 | "*" 187 | "/" 188 | "%" 189 | "=" 190 | "+=" 191 | "-=" 192 | "*=" 193 | "/=" 194 | "<" 195 | ">" 196 | "<=" 197 | ">=" 198 | "++" 199 | "--" 200 | "&" 201 | "~" 202 | "%=" 203 | "!=" 204 | "!==" 205 | "==" 206 | "===" 207 | "??" 208 | "->" 209 | "..<" 210 | "..." 211 | (bang) 212 | ] @operator 213 | 214 | (value_parameter_pack ["each" @keyword]) 215 | (value_pack_expansion ["repeat" @keyword]) 216 | (type_parameter_pack ["each" @keyword]) 217 | (type_pack_expansion ["repeat" @keyword]) 218 | -------------------------------------------------------------------------------- /languages/swift/indents.scm: -------------------------------------------------------------------------------- 1 | [ 2 | ; ... refers to the section that will get affected by this indent.begin capture 3 | (protocol_body) ; protocol Foo { ... } 4 | (class_body) ; class Foo { ... } 5 | (enum_class_body) ; enum Foo { ... } 6 | (function_declaration) ; func Foo (...) {...} 7 | (init_declaration) ; init(...) {...} 8 | (deinit_declaration) ; deinit {...} 9 | (computed_property) ; { ... } 10 | (subscript_declaration) ; subscript Foo(...) { ... } 11 | 12 | (computed_getter) ; get { ... } 13 | (computed_setter) ; set { ... } 14 | 15 | (assignment) ; a = b 16 | 17 | (control_transfer_statement) ; return ... 18 | (for_statement) 19 | (while_statement) 20 | (repeat_while_statement) 21 | (do_statement) 22 | (if_statement) 23 | (switch_statement) 24 | (guard_statement) 25 | 26 | (type_parameters) ; x 27 | (tuple_type) ; (...) 28 | (array_type) ; [String] 29 | (dictionary_type) ; [Foo: Bar] 30 | 31 | (call_expression) ; callFunc(...) 32 | (tuple_expression) ; ( foo + bar ) 33 | (array_literal) ; [ foo, bar ] 34 | (dictionary_literal) ; [ foo: bar, x: y ] 35 | (lambda_literal) 36 | (willset_didset_block) 37 | (willset_clause) 38 | (didset_clause) 39 | ] @indent.begin 40 | 41 | ; @something(...) 42 | ((modifiers 43 | (attribute) @indent.begin)) 44 | 45 | (function_declaration 46 | (modifiers 47 | . 48 | (attribute) 49 | (_)* @indent.branch) 50 | . 51 | _ @indent.branch 52 | (#not-has-type? @indent.branch type_parameters parameter)) 53 | 54 | 55 | (_ "{" "}" @end) @indent 56 | (_ "(" ")" @end) @indent 57 | (_ "<" ">" @end) @indent 58 | (_ "[" "]" @end) @indent 59 | 60 | ; if-elseif 61 | (if_statement 62 | (if_statement) @indent.dedent) 63 | 64 | ; case Foo: 65 | ; default Foo: 66 | ; @attribute default Foo: 67 | (switch_entry . _ @indent.branch) 68 | 69 | (function_declaration ")" @indent.branch) 70 | 71 | (type_parameters ">" @indent.branch @indent.end .) 72 | (tuple_expression ")" @indent.branch @indent.end) 73 | (value_arguments ")" @indent.branch @indent.end) 74 | (tuple_type ")" @indent.branch @indent.end) 75 | (modifiers 76 | (attribute ")" @indent.branch @indent.end)) 77 | 78 | [ 79 | "}" 80 | "]" 81 | ] @indent.branch @indent.end 82 | 83 | 84 | [ 85 | ; (ERROR) 86 | (comment) 87 | (multiline_comment) 88 | (raw_str_part) 89 | (multi_line_string_literal) 90 | ] @indent.auto 91 | 92 | (directive) @indent.ignore 93 | -------------------------------------------------------------------------------- /languages/swift/injections.scm: -------------------------------------------------------------------------------- 1 | ; Parse regex syntax within regex literals 2 | 3 | ((regex_literal) @injection.content 4 | (#set! injection.language "regex")) 5 | -------------------------------------------------------------------------------- /languages/swift/locals.scm: -------------------------------------------------------------------------------- 1 | (import_declaration (identifier) @definition.import) 2 | (function_declaration name: (simple_identifier) @definition.function) 3 | 4 | ; Scopes 5 | [ 6 | (statements) 7 | (for_statement) 8 | (while_statement) 9 | (repeat_while_statement) 10 | (do_statement) 11 | (if_statement) 12 | (guard_statement) 13 | (switch_statement) 14 | (property_declaration) 15 | (function_declaration) 16 | (class_declaration) 17 | (protocol_declaration) 18 | ] @local.scope 19 | -------------------------------------------------------------------------------- /languages/swift/outline.scm: -------------------------------------------------------------------------------- 1 | (protocol_declaration 2 | declaration_kind: "protocol" @name 3 | . 4 | _ * @name 5 | . 6 | body: (protocol_body) 7 | ) @item 8 | 9 | (class_declaration 10 | declaration_kind: ( 11 | [ 12 | "actor" 13 | "class" 14 | "extension" 15 | "enum" 16 | "struct" 17 | ] 18 | ) @name 19 | . 20 | _ * @name 21 | . 22 | body: (_) 23 | ) @item 24 | 25 | (init_declaration 26 | name: "init" @name 27 | . 28 | _ * @name 29 | . 30 | body: (function_body) 31 | ) @item 32 | 33 | (deinit_declaration 34 | "deinit" @name) @item 35 | 36 | (function_declaration 37 | "func" @name 38 | . 39 | _ * @name 40 | . 41 | body: (function_body) 42 | ) @item 43 | 44 | (class_body 45 | (property_declaration 46 | (value_binding_pattern) @name 47 | name: (pattern) @name 48 | (type_annotation)? @name 49 | ) @item 50 | ) 51 | 52 | (enum_class_body 53 | (property_declaration 54 | (value_binding_pattern) @name 55 | name: (pattern) @name 56 | (type_annotation)? @name 57 | ) @item 58 | ) 59 | 60 | ( 61 | (protocol_function_declaration) @name 62 | ) @item 63 | 64 | ( 65 | (protocol_property_declaration) @name 66 | ) @item 67 | -------------------------------------------------------------------------------- /languages/swift/runnables.scm: -------------------------------------------------------------------------------- 1 | ;; @Suite struct TestSuite 2 | ( 3 | (class_declaration 4 | (modifiers 5 | (attribute 6 | (user_type 7 | (type_identifier) @run (#eq? @run "Suite") 8 | ) 9 | ) 10 | ) 11 | name: (type_identifier) @_name 12 | ) @_swift-test-suite 13 | (#set! tag swift-test-suite) 14 | ) 15 | 16 | ;; @Test test func 17 | ( 18 | (function_declaration 19 | (modifiers 20 | (attribute 21 | (user_type 22 | (type_identifier) @run (#eq? @run "Test") 23 | ) 24 | ) 25 | ) 26 | name: (simple_identifier) @_name 27 | ) @_swift-test-test 28 | (#set! tag swift-test-test) 29 | ) 30 | 31 | ;; QuickSpec subclass 32 | ( 33 | (class_declaration 34 | name: (type_identifier) @_name 35 | (inheritance_specifier 36 | inherits_from: (user_type 37 | (type_identifier) @run (#eq? @run "QuickSpec") 38 | ) 39 | ) 40 | ) @_swift-test-quick-spec 41 | (#set! tag swift-test-quick-spec) 42 | ) 43 | 44 | ;; AsyncSpec subclass 45 | ( 46 | (class_declaration 47 | name: (type_identifier) @_name 48 | (inheritance_specifier 49 | inherits_from: (user_type 50 | (type_identifier) @run (#eq? @run "AsyncSpec") 51 | ) 52 | ) 53 | ) @_swift-test-async-spec 54 | (#set! tag swift-test-async-spec) 55 | ) 56 | 57 | ;; XCTestCase subclass 58 | ( 59 | (class_declaration 60 | name: (type_identifier) @_name 61 | (inheritance_specifier 62 | inherits_from: (user_type 63 | (type_identifier) @run (#eq? @run "XCTestCase") 64 | ) 65 | ) 66 | ) @_swift-test-test-case 67 | (#set! tag swift-test-test-case) 68 | ) 69 | 70 | ;; Test function within XCTestCase 71 | ( 72 | (class_declaration 73 | (inheritance_specifier 74 | inherits_from: (user_type 75 | (type_identifier) @test_class_name (#eq? @test_class_name "XCTestCase") 76 | ) 77 | ) 78 | body: (class_body 79 | (function_declaration 80 | name: (simple_identifier) @_name @run (#match? @run "^test") 81 | ) 82 | ) 83 | ) @_swift-test-func 84 | (#set! tag swift-test-func) 85 | ) 86 | -------------------------------------------------------------------------------- /languages/swift/tags.scm: -------------------------------------------------------------------------------- 1 | (class_declaration 2 | name: (type_identifier) @name) @definition.class 3 | 4 | (protocol_declaration 5 | name: (type_identifier) @name) @definition.interface 6 | 7 | (class_declaration 8 | (class_body 9 | [ 10 | (function_declaration 11 | name: (simple_identifier) @name 12 | ) 13 | (subscript_declaration 14 | (parameter (simple_identifier) @name) 15 | ) 16 | (init_declaration "init" @name) 17 | (deinit_declaration "deinit" @name) 18 | ] 19 | ) 20 | ) @definition.method 21 | 22 | (protocol_declaration 23 | (protocol_body 24 | [ 25 | (protocol_function_declaration 26 | name: (simple_identifier) @name 27 | ) 28 | (subscript_declaration 29 | (parameter (simple_identifier) @name) 30 | ) 31 | (init_declaration "init" @name) 32 | ] 33 | ) 34 | ) @definition.method 35 | 36 | (class_declaration 37 | (class_body 38 | [ 39 | (property_declaration 40 | (pattern (simple_identifier) @name) 41 | ) 42 | ] 43 | ) 44 | ) @definition.property 45 | 46 | (property_declaration 47 | (pattern (simple_identifier) @name) 48 | ) @definition.property 49 | 50 | (function_declaration 51 | name: (simple_identifier) @name) @definition.function -------------------------------------------------------------------------------- /languages/swift/tasks.json: -------------------------------------------------------------------------------- 1 | [ 2 | // These don't yet work: `swift test --filter` only supports Symbols (not filenames) 3 | // { 4 | // "label": "swift test", 5 | // "command": "swift", 6 | // "args": ["test", "--filter", "${ZED_STEM}"], 7 | // "tags": [ 8 | // "swift-test-async-spec", 9 | // "swift-test-quick-spec", 10 | // "swift-test-func", 11 | // "swift-test-suite", 12 | // "swift-test-test-case", 13 | // "swift-test-test" 14 | // ] 15 | // } 16 | ] 17 | -------------------------------------------------------------------------------- /languages/swift/textobjects.scm: -------------------------------------------------------------------------------- 1 | 2 | 3 | ; MARK: Structure 4 | 5 | (function_declaration 6 | body: (_) @function.inside) @function.around 7 | 8 | ; TODO: Classes/structs/enums 9 | 10 | 11 | ; MARK: Tests 12 | 13 | ; Only matches prefix test. Other conventions 14 | ; might be nice to add! 15 | (function_declaration 16 | name: (simple_identifier) @_name 17 | (#match? @_name "^test") 18 | ) 19 | 20 | -------------------------------------------------------------------------------- /src/language_server.rs: -------------------------------------------------------------------------------- 1 | use zed_extension_api::lsp::{Completion, CompletionKind, Symbol, SymbolKind}; 2 | use zed_extension_api::settings::LspSettings; 3 | use zed_extension_api::{self as zed, CodeLabel, CodeLabelSpan, LanguageServerId}; 4 | 5 | #[derive(Clone, Debug)] 6 | pub struct LanguageServerBinary { 7 | pub path: String, 8 | pub args: Option>, 9 | } 10 | 11 | #[derive(Default)] 12 | pub struct SourceKitLsp; 13 | 14 | impl SourceKitLsp { 15 | pub const SERVER_ID: &'static str = "sourcekit-lsp"; 16 | 17 | pub fn new() -> Self { 18 | Self {} 19 | } 20 | 21 | fn get_executable_args() -> Vec { 22 | Vec::new() 23 | } 24 | 25 | fn language_server_binary( 26 | &self, 27 | language_server_id: &LanguageServerId, 28 | worktree: &zed::Worktree, 29 | ) -> zed_extension_api::Result { 30 | let lsp_settings = LspSettings::for_worktree(language_server_id.as_ref(), worktree)?; 31 | 32 | if let Some(binary_settings) = lsp_settings.binary { 33 | if let Some(path) = binary_settings.path { 34 | return Ok(LanguageServerBinary { 35 | path, 36 | args: binary_settings.arguments, 37 | }); 38 | } 39 | } 40 | 41 | if let Some(path) = worktree.which(Self::SERVER_ID) { 42 | return Ok(LanguageServerBinary { 43 | path, 44 | args: Some(Self::get_executable_args()), 45 | }); 46 | } 47 | 48 | Ok(LanguageServerBinary { 49 | path: "/usr/bin/xcrun".into(), 50 | args: Some(vec![Self::SERVER_ID.into()]), 51 | }) 52 | } 53 | 54 | pub fn language_server_command( 55 | &mut self, 56 | language_server_id: &LanguageServerId, 57 | worktree: &zed::Worktree, 58 | ) -> zed_extension_api::Result { 59 | let binary = self.language_server_binary(language_server_id, worktree)?; 60 | 61 | Ok(zed::Command { 62 | command: binary.path, 63 | args: binary.args.unwrap_or(Self::get_executable_args()), 64 | env: worktree.shell_env(), 65 | }) 66 | } 67 | 68 | pub fn label_for_completion(&self, completion: Completion) -> Option { 69 | use CompletionKind::*; 70 | 71 | let kind = completion.kind?; 72 | 73 | match kind { 74 | Class | Enum | Interface | Keyword | Module | Struct => { 75 | let highlight_name = match completion.kind? { 76 | Class | Interface | Enum | Struct => Some("type".to_string()), 77 | Keyword => Some("keyword".to_string()), 78 | _ => None, 79 | }; 80 | 81 | Some(CodeLabel { 82 | code: Default::default(), 83 | filter_range: (0..completion.label.len()).into(), 84 | spans: vec![CodeLabelSpan::literal(completion.label, highlight_name)], 85 | }) 86 | } 87 | EnumMember => { 88 | let start = "enum Enum { case "; 89 | let code = format!("{start}{} }}", completion.label); 90 | 91 | Some(CodeLabel { 92 | code, 93 | spans: vec![CodeLabelSpan::code_range( 94 | start.len()..start.len() + completion.label.len(), 95 | )], 96 | filter_range: (0..completion.label.find('(').unwrap_or(completion.label.len())) 97 | .into(), 98 | }) 99 | } 100 | Function => { 101 | let func = "func "; 102 | let mut return_type = String::new(); 103 | 104 | if let Some(detail) = completion.detail { 105 | if !detail.is_empty() { 106 | return_type = format!(" -> {detail}"); 107 | } 108 | } 109 | 110 | let before_braces = format!("{func}{}{return_type}", completion.label); 111 | let code = format!("{before_braces} {{}}"); 112 | 113 | Some(CodeLabel { 114 | code, 115 | spans: vec![CodeLabelSpan::code_range(func.len()..before_braces.len())], 116 | filter_range: (0..completion.label.find('(')?).into(), 117 | }) 118 | } 119 | TypeParameter => { 120 | let typealias = "typealias "; 121 | let code = format!("{typealias}{} = {}", completion.label, completion.detail?); 122 | 123 | Some(CodeLabel { 124 | spans: vec![CodeLabelSpan::code_range(typealias.len()..code.len())], 125 | code, 126 | filter_range: (0..completion.label.len()).into(), 127 | }) 128 | } 129 | Value => { 130 | let mut r#type = String::new(); 131 | 132 | if let Some(detail) = completion.detail { 133 | if !detail.is_empty() { 134 | r#type = format!(": {detail}"); 135 | } 136 | } 137 | 138 | let var = format!("var variable{type} = "); 139 | let code = format!("{var}{}", completion.label); 140 | 141 | Some(CodeLabel { 142 | spans: vec![CodeLabelSpan::code_range(var.len()..code.len())], 143 | code, 144 | filter_range: (0..completion.label.len()).into(), 145 | }) 146 | } 147 | Variable => { 148 | let var = "var "; 149 | let code = format!("{var}{}: {}", completion.label, completion.detail?); 150 | 151 | Some(CodeLabel { 152 | spans: vec![CodeLabelSpan::code_range(var.len()..code.len())], 153 | code, 154 | filter_range: (0..completion.label.len()).into(), 155 | }) 156 | } 157 | _ => None, 158 | } 159 | } 160 | 161 | pub fn label_for_symbol(&self, symbol: Symbol) -> Option { 162 | match symbol.kind { 163 | SymbolKind::Method | SymbolKind::Function => { 164 | // Simple label: "func " 165 | let code = format!("func {}", symbol.name); 166 | Some(CodeLabel { 167 | code: code.clone(), 168 | spans: vec![CodeLabelSpan::code_range(0..code.len())], 169 | filter_range: (0..symbol.name.len()).into(), 170 | }) 171 | } 172 | SymbolKind::Variable | SymbolKind::Constant => { 173 | // Simple label: "var/let " 174 | let code = format!("var/let {}", symbol.name); 175 | Some(CodeLabel { 176 | code: code.clone(), 177 | spans: vec![CodeLabelSpan::code_range(0..code.len())], 178 | filter_range: (0..symbol.name.len()).into(), 179 | }) 180 | } 181 | SymbolKind::Class => { 182 | // Simple label: "class " 183 | let code = format!("class {}", symbol.name); 184 | Some(CodeLabel { 185 | code: code.clone(), 186 | spans: vec![CodeLabelSpan::code_range(0..code.len())], 187 | filter_range: (0..symbol.name.len()).into(), 188 | }) 189 | } 190 | SymbolKind::Struct => { 191 | // Simple label: "struct " 192 | let code = format!("struct {}", symbol.name); 193 | Some(CodeLabel { 194 | code: code.clone(), 195 | spans: vec![CodeLabelSpan::code_range(0..code.len())], 196 | filter_range: (0..symbol.name.len()).into(), 197 | }) 198 | } 199 | SymbolKind::Enum => { 200 | // Simple label: "enum " 201 | let code = format!("enum {}", symbol.name); 202 | Some(CodeLabel { 203 | code: code.clone(), 204 | spans: vec![CodeLabelSpan::code_range(0..code.len())], 205 | filter_range: (0..symbol.name.len()).into(), 206 | }) 207 | } 208 | _ => None, 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/swift.rs: -------------------------------------------------------------------------------- 1 | mod language_server; 2 | use language_server::SourceKitLsp; 3 | 4 | use zed::settings::LspSettings; 5 | use zed_extension_api::{ 6 | self as zed, 7 | lsp::{Completion, Symbol}, 8 | serde_json, CodeLabel, LanguageServerId, Result, 9 | }; 10 | 11 | #[derive(Default)] 12 | struct SwiftExtension { 13 | sourcekit_lsp: Option, 14 | } 15 | 16 | impl zed::Extension for SwiftExtension { 17 | fn new() -> Self { 18 | Self::default() 19 | } 20 | 21 | fn language_server_command( 22 | &mut self, 23 | language_server_id: &LanguageServerId, 24 | worktree: &zed::Worktree, 25 | ) -> Result { 26 | match language_server_id.as_ref() { 27 | SourceKitLsp::SERVER_ID => { 28 | let lsp = self.sourcekit_lsp.get_or_insert_with(SourceKitLsp::new); 29 | lsp.language_server_command(language_server_id, worktree) 30 | } 31 | _ => Err(format!("Unknown language server: {}", language_server_id)), 32 | } 33 | } 34 | 35 | fn language_server_initialization_options( 36 | &mut self, 37 | language_server_id: &LanguageServerId, 38 | worktree: &zed::Worktree, 39 | ) -> Result> { 40 | let initialization_options = 41 | LspSettings::for_worktree(language_server_id.as_ref(), worktree) 42 | .ok() 43 | .and_then(|lsp_settings| lsp_settings.initialization_options.clone()) 44 | .unwrap_or_default(); 45 | 46 | Ok(Some(serde_json::json!(initialization_options))) 47 | } 48 | 49 | fn label_for_completion( 50 | &self, 51 | language_server_id: &LanguageServerId, 52 | completion: Completion, 53 | ) -> Option { 54 | match language_server_id.as_ref() { 55 | SourceKitLsp::SERVER_ID => self 56 | .sourcekit_lsp 57 | .as_ref()? 58 | .label_for_completion(completion), 59 | _ => None, 60 | } 61 | } 62 | 63 | fn label_for_symbol( 64 | &self, 65 | language_server_id: &LanguageServerId, 66 | symbol: Symbol, 67 | ) -> Option { 68 | match language_server_id.as_ref() { 69 | SourceKitLsp::SERVER_ID => self.sourcekit_lsp.as_ref()?.label_for_symbol(symbol), 70 | _ => None, 71 | } 72 | } 73 | } 74 | 75 | zed::register_extension!(SwiftExtension); 76 | --------------------------------------------------------------------------------