aerial.nvim
aerial.nvim copied to clipboard
feature request: Kotlin support
Language: Kotlin
package org.javacs.kt
import java.io.PrintWriter
import java.io.StringWriter
import java.util.*
import java.util.logging.Formatter
import java.util.logging.LogRecord
import java.util.logging.Handler
import java.util.logging.Level
import java.time.Instant
internal val LOG = object : Logger() {
override fun toString(): String = "logger"
}
protected class Demo<T> private constructor() : Collection<T> {
init {
LOG.toString()
}
private inline fun <reified S> Iterable<T>.something(callback: S.() -> Unit) {
TODO()
throw Exception("unreachable")
}
}
enum class LogLevel(val value: Int) {
NONE(100),
ERROR(2),
WARN(1),
INFO(0),
DEBUG(-1),
TRACE(-2),
DEEP_TRACE(-3),
ALL(-100)
}
class Logger {
class LogMessage(
val level: LogLevel,
val message: String
) {
val formatted: String
get() = "[$level] $message"
}
private var outBackend: ((LogMessage) -> Unit)? = null
private var errBackend: ((LogMessage) -> Unit)? = null
private val outQueue: Queue<LogMessage> = ArrayDeque()
private val errQueue: Queue<LogMessage> = ArrayDeque()
private val newline = System.lineSeparator()
val logTime = false
var level = LogLevel.INFO
private fun outputError(msg: LogMessage) {
if (errBackend == null) {
errQueue.offer(msg)
} else {
errBackend?.invoke(msg)
}
}
private fun output(msg: LogMessage) {
if (outBackend == null) {
outQueue.offer(msg)
} else {
outBackend?.invoke(msg)
}
}
private fun log(msgLevel: LogLevel, msg: String, placeholders: Array<out Any?>) {
if (level.value <= msgLevel.value) {
output(LogMessage(msgLevel, format(insertPlaceholders(msg, placeholders))))
}
}
suspend fun error(msg: String, vararg placeholders: Any?) = log(LogLevel.ERROR, msg, placeholders)
suspend fun warn(msg: String, vararg placeholders: Any?) = log(LogLevel.WARN, msg, placeholders)
suspend fun info(msg: String, vararg placeholders: Any?) = log(LogLevel.INFO, msg, placeholders)
suspend fun debug(msg: String, vararg placeholders: Any?) = log(LogLevel.DEBUG, msg, placeholders)
suspend fun trace(msg: String, vararg placeholders: Any?) = log(LogLevel.TRACE, msg, placeholders)
suspend fun deepTrace(msg: String, vararg placeholders: Any?) = log(LogLevel.DEEP_TRACE, msg, placeholders)
fun connectJULFrontend() {
class JULRedirector(private val downstream: Logger): Handler() {
override fun publish(record: LogRecord) {
when (record.level) {
Level.SEVERE -> downstream.error(record.message)
Level.WARNING -> downstream.warn(record.message)
Level.INFO -> downstream.info(record.message)
Level.CONFIG -> downstream.debug(record.message)
Level.FINE -> downstream.trace(record.message)
else -> downstream.deepTrace(record.message)
}
}
override fun flush() {}
override fun close() {}
}
val rootLogger = java.util.logging.Logger.getLogger("").also {
it.addHandler(JULRedirector(this))
}
}
fun connectOutputBackend(outBackend: (LogMessage) -> Unit) {
this.outBackend = outBackend
flushOutQueue()
}
fun connectErrorBackend(errBackend: (LogMessage) -> Unit) {
this.errBackend = errBackend
flushErrQueue()
}
fun connectStdioBackend() {
connectOutputBackend { println(it.formatted) }
connectOutputBackend { System.err.println(it.formatted) }
}
private fun insertPlaceholders(msg: String, placeholders: Array<out Any?>): String {
val msgLength = msg.length
val lastIndex = msgLength - 1
var charIndex = 0
var placeholderIndex = 0
var result = StringBuilder()
while (charIndex < msgLength) {
val currentChar = msg.get(charIndex)
val nextChar = if (charIndex != lastIndex) msg.get(charIndex + 1) else '?'
if ((currentChar == '{') && (nextChar == '}')) {
if (placeholderIndex >= placeholders.size) {
return "ERROR: Tried to log more '{}' placeholders than there are values"
}
result.append(placeholders[placeholderIndex] ?: "null")
placeholderIndex += 1
charIndex += 2
} else {
result.append(currentChar)
charIndex += 1
}
}
return result.toString()
}
private fun flushOutQueue() {
while (outQueue.isNotEmpty()) {
outBackend?.invoke(outQueue.poll())
}
}
private fun flushErrQueue() {
while (errQueue.isNotEmpty()) {
errBackend?.invoke(errQueue.poll())
}
}
private fun format(msg: String): String {
val time = if (logTime) "${Instant.now()} " else ""
var thread = Thread.currentThread().name
return time + shortenOrPad(thread, 10) + msg
}
private fun shortenOrPad(str: String, length: Int): String =
if (str.length <= length) {
str.padEnd(length, ' ')
} else {
".." + str.substring(str.length - length + 2)
}
}
My attempt:
(source_file
(#set! "kind" "File")) @symbol
; Package
(package_header
(identifier) @name
(#set! "kind" "Package")) @symbol
; Enums
(class_declaration
(type_identifier) @name
(enum_class_body)
(#set! "kind" "Enum")) @symbol
(enum_entry
(simple_identifier) @name
(#set! "kind" "EnumMember")) @symbol
; Classes
(class_declaration
(type_identifier) @name
(#set! "kind" "Class")) @symbol
(object_declaration
(type_identifier) @name
(#set! "kind" "Object")) @symbol
; Functions
(function_declaration
(simple_identifier) @name
(#set! "kind" "Function")) @symbol
(anonymous_initializer
(#offset! @name 0 0 0 4)
(#set! "kind" "Constructor")) @symbol
(secondary_constructor
(#offset! @name 0 1 0 -1)
(#set! "kind" "Constructor")) @symbol
; Properties
(property_declaration
(variable_declaration
(simple_identifier) @name
)
(#set! "kind" "Property")) @symbol
(getter
(#offset! @name 0 1 0 -1)
(#set! "kind" "Method")) @symbol
(setter
(#offset! @name 0 1 0 -1)
(#set! "kind" "Method")) @symbol
; Blocks
(call_expression
(call_expression
(simple_identifier) @name
)
(call_suffix
(annotated_lambda)
)
(#set! "kind" "Namespace")) @symbol
(call_expression
(simple_identifier) @name
(call_suffix
(annotated_lambda)
)
(#set! "kind" "Namespace")) @symbol
(call_expression
(navigation_expression
(navigation_suffix
(simple_identifier) @name
)
)
(call_suffix
(annotated_lambda)
)
(#set! "kind" "Namespace")) @symbol
(boolean_literal
(#set! "kind" "Boolean")) @symbol
(string_literal
(#set! "kind" "String")) @symbol
(integer_literal
(#set! "kind" "Number")) @symbol
If you want to submit a PR, all you'll need to do is add the query file, add a test file, and run tests with make update_snapshots and confirm that the generated json file contains the expected symbol data. Take a look at #405 for reference