Skip to content
This repository has been archived by the owner on Sep 30, 2024. It is now read-only.

Commit

Permalink
SCIP ctags: add kinds for Kotlin (#57998)
Browse files Browse the repository at this point in the history
Improved ctags kind output for Kotlin:
* Split type into class, interface, object, and enum
* Split variable into enumMember, constant, and property
* Add type alias
  • Loading branch information
jtibshirani authored Nov 1, 2023
1 parent 714aca7 commit 1df495a
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 78 deletions.
Original file line number Diff line number Diff line change
@@ -1,20 +1,44 @@
(source_file
(package_header
(identifier)
@descriptor.namespace)) @scope
@descriptor.namespace @kind.package)) @scope

(function_declaration
(simple_identifier) @descriptor.method
(function_body) @local)
(simple_identifier) @descriptor.method @kind.method
(function_body)? @local)

(anonymous_function (_ (type_identifier) @descriptor.type . (type_identifier) @descriptor.method)) @local
(class_declaration (type_identifier) @descriptor.type) @scope
(object_declaration (type_identifier) @descriptor.type) @scope
(class_parameter (simple_identifier) @descriptor.term)
(enum_entry (simple_identifier) @descriptor.term)
(property_declaration (variable_declaration (simple_identifier) @descriptor.term))
(anonymous_function) @local

(multi_variable_declaration (variable_declaration (simple_identifier) @descriptor.term))
(class_declaration "interface" (type_identifier) @descriptor.type @kind.interface) @scope
(class_declaration "enum" "class" (type_identifier) @descriptor.type @kind.enum) @scope

;; Exclude enums from the 'class' kind
((class_declaration
("enum")? @_enum "class"
(type_identifier) @descriptor.type @kind.class)
(#filter! @_enum "enum")) @scope

(object_declaration (type_identifier) @descriptor.type @kind.object) @scope
(companion_object (type_identifier) @descriptor.type @kind.object) @scope

(type_alias (type_identifier) @descriptor.type @kind.typealias)

(class_parameter (simple_identifier) @descriptor.term @kind.property)
(enum_entry (simple_identifier) @descriptor.term @kind.enummember)

;; In the grammar, property_modifier always represents 'const'
(property_declaration
(modifiers (property_modifier))
(variable_declaration (simple_identifier) @descriptor.term @kind.constant))

;; Exclude constants from the 'property' kind
((property_declaration
(modifiers (property_modifier) @_const)?
(variable_declaration (simple_identifier) @descriptor.term @kind.property))
(#filter! @_const "property_modifier"))

(property_declaration
(multi_variable_declaration (variable_declaration (simple_identifier) @descriptor.term @kind.property)))

;; Future TODOs:
;; - Should probably unescape `Escaped` simple identifiers
Original file line number Diff line number Diff line change
Expand Up @@ -2,109 +2,127 @@
source: crates/scip-syntax/src/lib.rs
expression: dumped
---
package org.example
// ^^^^^^^^^^^ definition(Package) scip-ctags `org.example`/

// Top level constant property
const val PI = 3.14
// ^^ definition scip-ctags PI.
// ^^ definition(Constant) scip-ctags `org.example`/PI.

// Top level property with getter
val version: String
// ^^^^^^^ definition scip-ctags version.
// ^^^^^^^ definition(Property) scip-ctags `org.example`/version.
get() = "1.0.0"

// Top level function
fun printHello() {
// ^^^^^^^^^^ definition scip-ctags printHello().
// ^^^^^^^^^^ definition(Method) scip-ctags `org.example`/printHello().
println("Hello, Kotlin!")
}

// Class with properties and methods
class MyKotlinClass {
// ^^^^^^^^^^^^^ definition scip-ctags MyKotlinClass#
// ^^^^^^^^^^^^^ definition(Class) scip-ctags `org.example`/MyKotlinClass#
var prop: String = "property"
// ^^^^ definition scip-ctags MyKotlinClass#prop.
// ^^^^ definition(Property) scip-ctags `org.example`/MyKotlinClass#prop.

fun method() {
// ^^^^^^ definition scip-ctags MyKotlinClass#method().
// ^^^^^^ definition(Method) scip-ctags `org.example`/MyKotlinClass#method().
println("This is a method")
}
}

// Data class
data class User(val name: String, val age: Int)
// ^^^^ definition scip-ctags User#
// ^^^^ definition scip-ctags User#name.
// ^^^ definition scip-ctags User#age.
// ^^^^ definition(Class) scip-ctags `org.example`/User#
// ^^^^ definition(Property) scip-ctags `org.example`/User#name.
// ^^^ definition(Property) scip-ctags `org.example`/User#age.

// Enum class
enum class Days {
// ^^^^ definition scip-ctags Days#
// ^^^^ definition(Enum) scip-ctags `org.example`/Days#
MONDAY,
// ^^^^^^ definition scip-ctags Days#MONDAY.
// ^^^^^^ definition(EnumMember) scip-ctags `org.example`/Days#MONDAY.
TUESDAY,
// ^^^^^^^ definition scip-ctags Days#TUESDAY.
// ^^^^^^^ definition(EnumMember) scip-ctags `org.example`/Days#TUESDAY.
WEDNESDAY,
// ^^^^^^^^^ definition scip-ctags Days#WEDNESDAY.
// ^^^^^^^^^ definition(EnumMember) scip-ctags `org.example`/Days#WEDNESDAY.
THURSDAY,
// ^^^^^^^^ definition scip-ctags Days#THURSDAY.
// ^^^^^^^^ definition(EnumMember) scip-ctags `org.example`/Days#THURSDAY.
FRIDAY,
// ^^^^^^ definition scip-ctags Days#FRIDAY.
// ^^^^^^ definition(EnumMember) scip-ctags `org.example`/Days#FRIDAY.
SATURDAY,
// ^^^^^^^^ definition scip-ctags Days#SATURDAY.
// ^^^^^^^^ definition(EnumMember) scip-ctags `org.example`/Days#SATURDAY.
SUNDAY
// ^^^^^^ definition scip-ctags Days#SUNDAY.
// ^^^^^^ definition(EnumMember) scip-ctags `org.example`/Days#SUNDAY.
}

// Object (singleton)
object MyObject {
// ^^^^^^^^ definition scip-ctags MyObject#
// ^^^^^^^^ definition(Object) scip-ctags `org.example`/MyObject#
val property = "Object property"
// ^^^^^^^^ definition scip-ctags MyObject#property.
// ^^^^^^^^ definition(Property) scip-ctags `org.example`/MyObject#property.
}

// Interface
interface MyInterface {
// ^^^^^^^^^^^ definition scip-ctags MyInterface#
// ^^^^^^^^^^^ definition(Interface) scip-ctags `org.example`/MyInterface#
fun interfaceMethod(): String
// ^^^^^^^^^^^^^^^ definition(Method) scip-ctags `org.example`/MyInterface#interfaceMethod().
}

object SimpleSingleton {
// ^^^^^^^^^^^^^^^ definition(Object) scip-ctags `org.example`/SimpleSingleton#
val answer = 42;
// ^^^^^^ definition(Property) scip-ctags `org.example`/SimpleSingleton#answer.
fun greet(name: String) = "Hello, $name!"
// ^^^^^ definition(Method) scip-ctags `org.example`/SimpleSingleton#greet().
}

// Type alias
typealias UserList = List<User>
// ^^^^^^^^ definition(TypeAlias) scip-ctags `org.example`/UserList#

// Extension function
fun String.print() {
// ^^^^^ definition scip-ctags print().
// ^^^^^ definition(Method) scip-ctags `org.example`/print().
println(this)
}

// Sealed class
sealed class Result {
// ^^^^^^ definition scip-ctags Result#
// ^^^^^^ definition(Class) scip-ctags `org.example`/Result#
data class Success(val message: String) : Result()
// ^^^^^^^ definition scip-ctags Result#Success#
// ^^^^^^^ definition scip-ctags Result#Success#message.
// ^^^^^^^ definition(Class) scip-ctags `org.example`/Result#Success#
// ^^^^^^^ definition(Property) scip-ctags `org.example`/Result#Success#message.
data class Error(val error: Exception) : Result()
// ^^^^^ definition scip-ctags Result#Error#
// ^^^^^ definition scip-ctags Result#Error#error.
// ^^^^^ definition(Class) scip-ctags `org.example`/Result#Error#
// ^^^^^ definition(Property) scip-ctags `org.example`/Result#Error#error.
}

// Inline class
inline class Password(val value: String)
// ^^^^^^^^ definition scip-ctags Password#
// ^^^^^ definition scip-ctags Password#value.
// ^^^^^^^^ definition(Class) scip-ctags `org.example`/Password#
// ^^^^^ definition(Property) scip-ctags `org.example`/Password#value.

// Companion object
class MyClassWithCompanion {
// ^^^^^^^^^^^^^^^^^^^^ definition scip-ctags MyClassWithCompanion#
companion object {
// ^^^^^^^^^^^^^^^^^^^^ definition(Class) scip-ctags `org.example`/MyClassWithCompanion#
companion object ConstantCompanion {
// ^^^^^^^^^^^^^^^^^ definition(Object) scip-ctags `org.example`/MyClassWithCompanion#ConstantCompanion#
const val CONSTANT = "Companion constant"
// ^^^^^^^^ definition scip-ctags MyClassWithCompanion#CONSTANT.
// ^^^^^^^^ definition(Constant) scip-ctags `org.example`/MyClassWithCompanion#ConstantCompanion#CONSTANT.
}
}

fun `Escaped`() {}
// ^^^^^^^^^ definition scip-ctags . . . ``Escaped``().
// ^^^^^^^^^ definition(Method) scip-ctags . . . `org.example`/``Escaped``().

// Multi-variable declaration
val (left, right) = directions()
// ^^^^ definition scip-ctags left.
// ^^^^^ definition scip-ctags right.
// ^^^^ definition(Property) scip-ctags `org.example`/left.
// ^^^^^ definition(Property) scip-ctags `org.example`/right.

// Anonymous function
fun(x: Int, y: Int): Int = x + y

Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,44 @@
source: crates/scip-syntax/src/lib.rs
expression: "String::from_utf8_lossy(buf_writer.buffer())"
---
{"_type":"tag","name":"MyKotlinClass","path":"globals.kt","language":"kotlin","line":14,"kind":"type","scope":null}
{"_type":"tag","name":"method","path":"globals.kt","language":"kotlin","line":17,"kind":"method","scope":"MyKotlinClass"}
{"_type":"tag","name":"prop","path":"globals.kt","language":"kotlin","line":15,"kind":"variable","scope":"MyKotlinClass"}
{"_type":"tag","name":"User","path":"globals.kt","language":"kotlin","line":23,"kind":"type","scope":null}
{"_type":"tag","name":"age","path":"globals.kt","language":"kotlin","line":23,"kind":"variable","scope":"User"}
{"_type":"tag","name":"name","path":"globals.kt","language":"kotlin","line":23,"kind":"variable","scope":"User"}
{"_type":"tag","name":"Days","path":"globals.kt","language":"kotlin","line":26,"kind":"type","scope":null}
{"_type":"tag","name":"SUNDAY","path":"globals.kt","language":"kotlin","line":33,"kind":"variable","scope":"Days"}
{"_type":"tag","name":"SATURDAY","path":"globals.kt","language":"kotlin","line":32,"kind":"variable","scope":"Days"}
{"_type":"tag","name":"FRIDAY","path":"globals.kt","language":"kotlin","line":31,"kind":"variable","scope":"Days"}
{"_type":"tag","name":"THURSDAY","path":"globals.kt","language":"kotlin","line":30,"kind":"variable","scope":"Days"}
{"_type":"tag","name":"WEDNESDAY","path":"globals.kt","language":"kotlin","line":29,"kind":"variable","scope":"Days"}
{"_type":"tag","name":"TUESDAY","path":"globals.kt","language":"kotlin","line":28,"kind":"variable","scope":"Days"}
{"_type":"tag","name":"MONDAY","path":"globals.kt","language":"kotlin","line":27,"kind":"variable","scope":"Days"}
{"_type":"tag","name":"MyObject","path":"globals.kt","language":"kotlin","line":37,"kind":"type","scope":null}
{"_type":"tag","name":"property","path":"globals.kt","language":"kotlin","line":38,"kind":"variable","scope":"MyObject"}
{"_type":"tag","name":"MyInterface","path":"globals.kt","language":"kotlin","line":42,"kind":"type","scope":null}
{"_type":"tag","name":"Result","path":"globals.kt","language":"kotlin","line":55,"kind":"type","scope":null}
{"_type":"tag","name":"Success","path":"globals.kt","language":"kotlin","line":56,"kind":"type","scope":"Result"}
{"_type":"tag","name":"message","path":"globals.kt","language":"kotlin","line":56,"kind":"variable","scope":"Result.Success"}
{"_type":"tag","name":"Error","path":"globals.kt","language":"kotlin","line":57,"kind":"type","scope":"Result"}
{"_type":"tag","name":"error","path":"globals.kt","language":"kotlin","line":57,"kind":"variable","scope":"Result.Error"}
{"_type":"tag","name":"Password","path":"globals.kt","language":"kotlin","line":61,"kind":"type","scope":null}
{"_type":"tag","name":"value","path":"globals.kt","language":"kotlin","line":61,"kind":"variable","scope":"Password"}
{"_type":"tag","name":"MyClassWithCompanion","path":"globals.kt","language":"kotlin","line":64,"kind":"type","scope":null}
{"_type":"tag","name":"CONSTANT","path":"globals.kt","language":"kotlin","line":66,"kind":"variable","scope":"MyClassWithCompanion"}
{"_type":"tag","name":"right","path":"globals.kt","language":"kotlin","line":72,"kind":"variable","scope":null}
{"_type":"tag","name":"left","path":"globals.kt","language":"kotlin","line":72,"kind":"variable","scope":null}
{"_type":"tag","name":"`Escaped`","path":"globals.kt","language":"kotlin","line":70,"kind":"method","scope":null}
{"_type":"tag","name":"print","path":"globals.kt","language":"kotlin","line":50,"kind":"method","scope":null}
{"_type":"tag","name":"printHello","path":"globals.kt","language":"kotlin","line":9,"kind":"method","scope":null}
{"_type":"tag","name":"version","path":"globals.kt","language":"kotlin","line":5,"kind":"variable","scope":null}
{"_type":"tag","name":"PI","path":"globals.kt","language":"kotlin","line":2,"kind":"variable","scope":null}
{"_type":"tag","name":"org.example","path":"globals.kt","language":"kotlin","line":1,"kind":"package","scope":null}
{"_type":"tag","name":"MyKotlinClass","path":"globals.kt","language":"kotlin","line":16,"kind":"class","scope":"org.example"}
{"_type":"tag","name":"method","path":"globals.kt","language":"kotlin","line":19,"kind":"method","scope":"org.example.MyKotlinClass"}
{"_type":"tag","name":"prop","path":"globals.kt","language":"kotlin","line":17,"kind":"property","scope":"org.example.MyKotlinClass"}
{"_type":"tag","name":"User","path":"globals.kt","language":"kotlin","line":25,"kind":"class","scope":"org.example"}
{"_type":"tag","name":"age","path":"globals.kt","language":"kotlin","line":25,"kind":"property","scope":"org.example.User"}
{"_type":"tag","name":"name","path":"globals.kt","language":"kotlin","line":25,"kind":"property","scope":"org.example.User"}
{"_type":"tag","name":"Days","path":"globals.kt","language":"kotlin","line":28,"kind":"enum","scope":"org.example"}
{"_type":"tag","name":"SUNDAY","path":"globals.kt","language":"kotlin","line":35,"kind":"enumMember","scope":"org.example.Days"}
{"_type":"tag","name":"SATURDAY","path":"globals.kt","language":"kotlin","line":34,"kind":"enumMember","scope":"org.example.Days"}
{"_type":"tag","name":"FRIDAY","path":"globals.kt","language":"kotlin","line":33,"kind":"enumMember","scope":"org.example.Days"}
{"_type":"tag","name":"THURSDAY","path":"globals.kt","language":"kotlin","line":32,"kind":"enumMember","scope":"org.example.Days"}
{"_type":"tag","name":"WEDNESDAY","path":"globals.kt","language":"kotlin","line":31,"kind":"enumMember","scope":"org.example.Days"}
{"_type":"tag","name":"TUESDAY","path":"globals.kt","language":"kotlin","line":30,"kind":"enumMember","scope":"org.example.Days"}
{"_type":"tag","name":"MONDAY","path":"globals.kt","language":"kotlin","line":29,"kind":"enumMember","scope":"org.example.Days"}
{"_type":"tag","name":"MyObject","path":"globals.kt","language":"kotlin","line":39,"kind":"object","scope":"org.example"}
{"_type":"tag","name":"property","path":"globals.kt","language":"kotlin","line":40,"kind":"property","scope":"org.example.MyObject"}
{"_type":"tag","name":"MyInterface","path":"globals.kt","language":"kotlin","line":44,"kind":"interface","scope":"org.example"}
{"_type":"tag","name":"interfaceMethod","path":"globals.kt","language":"kotlin","line":45,"kind":"method","scope":"org.example.MyInterface"}
{"_type":"tag","name":"SimpleSingleton","path":"globals.kt","language":"kotlin","line":48,"kind":"object","scope":"org.example"}
{"_type":"tag","name":"greet","path":"globals.kt","language":"kotlin","line":50,"kind":"method","scope":"org.example.SimpleSingleton"}
{"_type":"tag","name":"answer","path":"globals.kt","language":"kotlin","line":49,"kind":"property","scope":"org.example.SimpleSingleton"}
{"_type":"tag","name":"Result","path":"globals.kt","language":"kotlin","line":62,"kind":"class","scope":"org.example"}
{"_type":"tag","name":"Success","path":"globals.kt","language":"kotlin","line":63,"kind":"class","scope":"org.example.Result"}
{"_type":"tag","name":"message","path":"globals.kt","language":"kotlin","line":63,"kind":"property","scope":"org.example.Result.Success"}
{"_type":"tag","name":"Error","path":"globals.kt","language":"kotlin","line":64,"kind":"class","scope":"org.example.Result"}
{"_type":"tag","name":"error","path":"globals.kt","language":"kotlin","line":64,"kind":"property","scope":"org.example.Result.Error"}
{"_type":"tag","name":"Password","path":"globals.kt","language":"kotlin","line":68,"kind":"class","scope":"org.example"}
{"_type":"tag","name":"value","path":"globals.kt","language":"kotlin","line":68,"kind":"property","scope":"org.example.Password"}
{"_type":"tag","name":"MyClassWithCompanion","path":"globals.kt","language":"kotlin","line":71,"kind":"class","scope":"org.example"}
{"_type":"tag","name":"ConstantCompanion","path":"globals.kt","language":"kotlin","line":72,"kind":"object","scope":"org.example.MyClassWithCompanion"}
{"_type":"tag","name":"CONSTANT","path":"globals.kt","language":"kotlin","line":73,"kind":"constant","scope":"org.example.MyClassWithCompanion.ConstantCompanion"}
{"_type":"tag","name":"right","path":"globals.kt","language":"kotlin","line":80,"kind":"property","scope":"org.example"}
{"_type":"tag","name":"left","path":"globals.kt","language":"kotlin","line":80,"kind":"property","scope":"org.example"}
{"_type":"tag","name":"`Escaped`","path":"globals.kt","language":"kotlin","line":77,"kind":"method","scope":"org.example"}
{"_type":"tag","name":"print","path":"globals.kt","language":"kotlin","line":57,"kind":"method","scope":"org.example"}
{"_type":"tag","name":"UserList","path":"globals.kt","language":"kotlin","line":54,"kind":"typeAlias","scope":"org.example"}
{"_type":"tag","name":"printHello","path":"globals.kt","language":"kotlin","line":11,"kind":"method","scope":"org.example"}
{"_type":"tag","name":"version","path":"globals.kt","language":"kotlin","line":7,"kind":"property","scope":"org.example"}
{"_type":"tag","name":"PI","path":"globals.kt","language":"kotlin","line":4,"kind":"constant","scope":"org.example"}

Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub fn captures_to_kind(kind: &Option<&String>) -> symbol_information::Kind {
"kind.methodspec" => MethodSpecification,
"kind.module" => Module,
"kind.namespace" => Namespace,
"kind.object" => Object,
"kind.package" => Package,
"kind.property" => Property,
"kind.setter" => Setter,
Expand Down Expand Up @@ -71,6 +72,7 @@ pub fn symbol_kind_to_ctags_kind(kind: &symbol_information::Kind) -> Option<&'st
MethodSpecification => Some("methodSpec"),
Module => Some("module"),
Namespace => Some("namespace"),
Object => Some("object"),
Package => Some("package"),
Property => Some("property"),
Setter => Some("setter"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
package org.example

// Top level constant property
const val PI = 3.14

Expand Down Expand Up @@ -43,6 +45,11 @@ interface MyInterface {
fun interfaceMethod(): String
}

object SimpleSingleton {
val answer = 42;
fun greet(name: String) = "Hello, $name!"
}

// Type alias
typealias UserList = List<User>

Expand All @@ -62,11 +69,15 @@ inline class Password(val value: String)

// Companion object
class MyClassWithCompanion {
companion object {
companion object ConstantCompanion {
const val CONSTANT = "Companion constant"
}
}

fun `Escaped`() {}

// Multi-variable declaration
val (left, right) = directions()

// Anonymous function
fun(x: Int, y: Int): Int = x + y

0 comments on commit 1df495a

Please sign in to comment.