aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules7
-rw-r--r--book/src/generated/lang-support.md1
m---------helix-syntax/languages/tree-sitter-kotlin0
-rw-r--r--languages.toml9
-rw-r--r--runtime/queries/kotlin/folds.scm17
-rw-r--r--runtime/queries/kotlin/highlights.scm295
-rw-r--r--runtime/queries/kotlin/injections.scm36
7 files changed, 364 insertions, 1 deletions
diff --git a/.gitmodules b/.gitmodules
index 5b6609a8..fa1e0900 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -209,7 +209,7 @@
[submodule "helix-syntax/languages/tree-sitter-graphql"]
path = helix-syntax/languages/tree-sitter-graphql
url = https://github.com/bkegley/tree-sitter-graphql
- shallow = true
+ shallow = true
[submodule "helix-syntax/languages/tree-sitter-elm"]
path = helix-syntax/languages/tree-sitter-elm
url = https://github.com/elm-tooling/tree-sitter-elm
@@ -229,3 +229,8 @@
[submodule "helix-syntax/languages/tree-sitter-erlang"]
path = helix-syntax/languages/tree-sitter-erlang
url = https://github.com/the-mikedavis/tree-sitter-erlang
+ shallow = true
+[submodule "helix-syntax/languages/tree-sitter-kotlin"]
+ path = helix-syntax/languages/tree-sitter-kotlin
+ url = https://github.com/fwcd/tree-sitter-kotlin.git
+ shallow = true
diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md
index 89405073..49538215 100644
--- a/book/src/generated/lang-support.md
+++ b/book/src/generated/lang-support.md
@@ -27,6 +27,7 @@
| javascript | ✓ | | ✓ | `typescript-language-server` |
| json | ✓ | | ✓ | |
| julia | ✓ | | | `julia` |
+| kotlin | ✓ | | | `kotlin-language-server` |
| latex | ✓ | | | |
| lean | ✓ | | | `lean` |
| ledger | ✓ | | | |
diff --git a/helix-syntax/languages/tree-sitter-kotlin b/helix-syntax/languages/tree-sitter-kotlin
new file mode 160000
+Subproject a4f71eb9b8c9b19ded3e0e9470be4b1b77c2b56
diff --git a/languages.toml b/languages.toml
index 35758566..9876bcf1 100644
--- a/languages.toml
+++ b/languages.toml
@@ -741,3 +741,12 @@ file-types = ["erl", "hrl", "app", "rebar.config"]
roots = ["rebar.config"]
comment-token = "%%"
indent = { tab-width = 4, unit = " " }
+
+[[language]]
+name = "kotlin"
+scope = "source.kotlin"
+file-types = ["kt", "kts"]
+roots = ["settings.gradle", "settings.gradle.kts"]
+comment-token = "//"
+indent = { tab-width = 4, unit = " " }
+language-server = { command = "kotlin-language-server" }
diff --git a/runtime/queries/kotlin/folds.scm b/runtime/queries/kotlin/folds.scm
new file mode 100644
index 00000000..768972b8
--- /dev/null
+++ b/runtime/queries/kotlin/folds.scm
@@ -0,0 +1,17 @@
+[
+ (import_list)
+
+ (when_expression)
+ (control_structure_body)
+
+ (lambda_literal)
+ (function_body)
+ (primary_constructor)
+ (secondary_constructor)
+ (anonymous_initializer)
+
+ (class_body)
+ (enum_class_body)
+
+ (interpolated_expression)
+] @fold
diff --git a/runtime/queries/kotlin/highlights.scm b/runtime/queries/kotlin/highlights.scm
new file mode 100644
index 00000000..7b90fcf9
--- /dev/null
+++ b/runtime/queries/kotlin/highlights.scm
@@ -0,0 +1,295 @@
+;;; Operators & Punctuation
+
+(multi_line_string_literal
+ "$" @punctuation
+ (interpolated_identifier) @none)
+(multi_line_string_literal
+ "${" @punctuation
+ (interpolated_expression) @none
+ "}" @punctuation.)
+
+; NOTE: `interpolated_identifier`s can be highlighted in any way
+(line_string_literal
+ "$" @punctuation
+ (interpolated_identifier) @none)
+(line_string_literal
+ "${" @punctuation
+ (interpolated_expression) @none
+ "}" @punctuation)
+
+[
+ "."
+ ","
+ ";"
+ ":"
+ "::"
+] @punctuation.delimiter
+
+[
+ "(" ")"
+ "[" "]"
+ "{" "}"
+] @punctuation.bracket
+
+[
+ "!"
+ "!="
+ "!=="
+ "="
+ "=="
+ "==="
+ ">"
+ ">="
+ "<"
+ "<="
+ "||"
+ "&&"
+ "+"
+ "++"
+ "+="
+ "-"
+ "--"
+ "-="
+ "*"
+ "*="
+ "/"
+ "/="
+ "%"
+ "%="
+ "?."
+ "?:"
+ "!!"
+ "is"
+ "!is"
+ "in"
+ "!in"
+ "as"
+ "as?"
+ ".."
+ "->"
+] @operator
+
+;;; Keywords
+
+(type_alias "typealias" @keyword)
+[
+ (class_modifier)
+ (member_modifier)
+ (function_modifier)
+ (property_modifier)
+ (platform_modifier)
+ (variance_modifier)
+ (parameter_modifier)
+ (visibility_modifier)
+ (reification_modifier)
+ (inheritance_modifier)
+]@keyword
+
+[
+ "val"
+ "var"
+ "enum"
+ "class"
+ "object"
+ "interface"
+; "typeof" ; NOTE: It is reserved for future use
+] @keyword
+
+("fun") @keyword.function
+
+(jump_expression) @keyword.control.return
+
+[
+ "if"
+ "else"
+ "when"
+] @keyword.control.conditional
+
+[
+ "for"
+ "do"
+ "while"
+] @keyword.control.repeat
+
+[
+ "try"
+ "catch"
+ "throw"
+ "finally"
+] @keyword.control.exception
+
+(annotation
+ "@" @attribute (use_site_target)? @attribute)
+(annotation
+ (user_type
+ (type_identifier) @attribute))
+(annotation
+ (constructor_invocation
+ (user_type
+ (type_identifier) @attribute)))
+
+(file_annotation
+ "@" @attribute "file" @attribute ":" @attribute)
+(file_annotation
+ (user_type
+ (type_identifier) @attribute))
+(file_annotation
+ (constructor_invocation
+ (user_type
+ (type_identifier) @attribute)))
+
+;;; Literals
+; NOTE: Escapes not allowed in multi-line strings
+(line_string_literal (character_escape_seq) @constant.character.escape)
+
+[
+ (line_string_literal)
+ (multi_line_string_literal)
+] @string
+
+(character_literal) @constant.character
+
+[
+ "null" ; should be highlighted the same as booleans
+ (boolean_literal)
+] @constant.builtin.boolean
+
+(real_literal) @constant.numeric.float
+[
+ (integer_literal)
+ (long_literal)
+ (hex_literal)
+ (bin_literal)
+ (unsigned_literal)
+] @constant.numeric.integer
+
+[
+ (comment)
+ (shebang_line)
+] @comment
+
+;;; Function calls
+
+(call_expression
+ . (simple_identifier) @function.builtin
+ (#match? @function.builtin "^(arrayOf|arrayOfNulls|byteArrayOf|shortArrayOf|intArrayOf|longArrayOf|ubyteArrayOf|ushortArrayOf|uintArrayOf|ulongArrayOf|floatArrayOf|doubleArrayOf|booleanArrayOf|charArrayOf|emptyArray|mapOf|setOf|listOf|emptyMap|emptySet|emptyList|mutableMapOf|mutableSetOf|mutableListOf|print|println|error|TODO|run|runCatching|repeat|lazy|lazyOf|enumValues|enumValueOf|assert|check|checkNotNull|require|requireNotNull|with|suspend|synchronized)$"))
+
+; object.function() or object.property.function()
+(call_expression
+ (navigation_expression
+ (navigation_suffix
+ (simple_identifier) @function) . ))
+
+; function()
+(call_expression
+ . (simple_identifier) @function)
+
+;;; Function definitions
+
+; lambda parameters
+(lambda_literal
+ (lambda_parameters
+ (variable_declaration
+ (simple_identifier) @variable.parameter)))
+
+(parameter_with_optional_type
+ (simple_identifier) @variable.parameter)
+
+(parameter
+ (simple_identifier) @variable.parameter)
+
+(anonymous_initializer
+ ("init") @constructor)
+
+(constructor_invocation
+ (user_type
+ (type_identifier) @constructor))
+
+(secondary_constructor
+ ("constructor") @constructor)
+(primary_constructor) @constructor
+
+(getter
+ ("get") @function.builtin)
+(setter
+ ("set") @function.builtin)
+
+(function_declaration
+ . (simple_identifier) @function)
+
+; TODO: Seperate labeled returns/breaks/continue/super/this
+; Must be implemented in the parser first
+(label) @label
+
+(import_header
+ (identifier
+ (simple_identifier) @function @_import .)
+ (import_alias
+ (type_identifier) @function)?
+ (#match? @_import "^[a-z]"))
+
+; The last `simple_identifier` in a `import_header` will always either be a function
+; or a type. Classes can appear anywhere in the import path, unlike functions
+(import_header
+ (identifier
+ (simple_identifier) @type @_import)
+ (import_alias
+ (type_identifier) @type)?
+ (#match? @_import "^[A-Z]"))
+
+(import_header
+ "import" @keyword.control.import)
+
+(package_header
+ . (identifier)) @namespace
+
+((type_identifier) @type.builtin
+ (#match? @function.builtin "^(Byte|Short|Int|Long|UByte|UShort|UInt|ULong|Float|Double|Boolean|Char|String|Array|ByteArray|ShortArray|IntArray|LongArray|UByteArray|UShortArray|UIntArray|ULongArray|FloatArray|DoubleArray|BooleanArray|CharArray|Map|Set|List|EmptyMap|EmptySet|EmptyList|MutableMap|MutableSet|MutableList)$"))
+
+(type_identifier) @type
+
+(enum_entry
+ (simple_identifier) @constant)
+
+(_
+ (navigation_suffix
+ (simple_identifier) @constant
+ (#match? @constant "^[A-Z][A-Z0-9_]*$")))
+
+; SCREAMING CASE identifiers are assumed to be constants
+((simple_identifier) @constant
+(#match? @constant "^[A-Z][A-Z0-9_]*$"))
+
+; id_1.id_2.id_3: `id_2` and `id_3` are assumed as object properties
+(_
+ (navigation_suffix
+ (simple_identifier) @variable.other.member))
+
+(class_body
+ (property_declaration
+ (variable_declaration
+ (simple_identifier) @variable.other.member)))
+
+(class_parameter
+ (simple_identifier) @variable.other.member)
+
+; `super` keyword inside classes
+(super_expression) @variable.builtin
+
+; `this` this keyword inside classes
+(this_expression) @variable.builtin
+
+;;; Identifiers
+; `field` keyword inside property getter/setter
+; FIXME: This will highlight the keyword outside of getters and setters
+; since tree-sitter does not allow us to check for arbitrary nestation
+((simple_identifier) @variable.builtin
+(#eq? @variable.builtin "field"))
+
+; `it` keyword inside lambdas
+; FIXME: This will highlight the keyword outside of lambdas since tree-sitter
+; does not allow us to check for arbitrary nestation
+((simple_identifier) @variable.builtin
+(#eq? @variable.builtin "it"))
+
+(simple_identifier) @variable
diff --git a/runtime/queries/kotlin/injections.scm b/runtime/queries/kotlin/injections.scm
new file mode 100644
index 00000000..abdc2050
--- /dev/null
+++ b/runtime/queries/kotlin/injections.scm
@@ -0,0 +1,36 @@
+((comment) @injection.content
+ (#set! injection.language "comment"))
+
+; There are 3 ways to define a regex
+; - "[abc]?".toRegex()
+((call_expression
+ (navigation_expression
+ ([(line_string_literal) (multi_line_string_literal)] @injection.content)
+ (navigation_suffix
+ ((simple_identifier) @_function
+ (#eq? @_function "toRegex")))))
+ (#set! injection.language "regex"))
+
+; - Regex("[abc]?")
+((call_expression
+ ((simple_identifier) @_function
+ (#eq? @_function "Regex"))
+ (call_suffix
+ (value_arguments
+ (value_argument
+ [ (line_string_literal) (multi_line_string_literal) ] @injection.content))))
+ (#set! injection.language "regex"))
+
+; - Regex.fromLiteral("[abc]?")
+((call_expression
+ (navigation_expression
+ ((simple_identifier) @_class
+ (#eq? @_class "Regex"))
+ (navigation_suffix
+ ((simple_identifier) @_function
+ (#eq? @_function "fromLiteral"))))
+ (call_suffix
+ (value_arguments
+ (value_argument
+ [ (line_string_literal) (multi_line_string_literal) ] @injection.content))))
+ (#set! injection.language "regex"))