Skip to content

Commit

Permalink
Add kotlin language (#1689)
Browse files Browse the repository at this point in the history
* Add kotlin language

Queries taken from https://github.com/nvim-treesitter/nvim-treesitter/blob/master/queries/kotlin seem to work well enough for my needs though I don't use kotlin heavily.

* Update lang-support doc

* Updates the kotlin highlight query to use helixs scopes

* Updates the queries from PR feedback

* Adds 'shallow = true' to gitmodules

* Removes kotlin locals.scm

* Remove blank line

Co-authored-by: Ivan Tham <pickfire@riseup.net>

Co-authored-by: Ivan Tham <pickfire@riseup.net>
  • Loading branch information
mdaffin and pickfire committed Feb 23, 2022
1 parent 40eb126 commit f83843c
Show file tree
Hide file tree
Showing 7 changed files with 365 additions and 1 deletion.
7 changes: 6 additions & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
1 change: 1 addition & 0 deletions book/src/generated/lang-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
| javascript || || `typescript-language-server` |
| json || || |
| julia || | | `julia` |
| kotlin || | | `kotlin-language-server` |
| latex || | | |
| lean || | | `lean` |
| ledger || | | |
Expand Down
1 change: 1 addition & 0 deletions helix-syntax/languages/tree-sitter-kotlin
Submodule tree-sitter-kotlin added at a4f71e
9 changes: 9 additions & 0 deletions languages.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
17 changes: 17 additions & 0 deletions runtime/queries/kotlin/folds.scm
Original file line number Diff line number Diff line change
@@ -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
295 changes: 295 additions & 0 deletions runtime/queries/kotlin/highlights.scm
Original file line number Diff line number Diff line change
@@ -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
36 changes: 36 additions & 0 deletions runtime/queries/kotlin/injections.scm
Original file line number Diff line number Diff line change
@@ -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"))

0 comments on commit f83843c

Please sign in to comment.