plugin and formatter improvements

This commit is contained in:
sergeych committed 2026-01-17 07:33:30 +03:00
1 parent f848d79d39
commit 0759346e4b
5 files changed
+152 -35

No files matched your search

@@ -122,6 +122,24 @@ class Compiler(
return doc
}
private fun nextNonWhitespace(): Token {
while (true) {
val t = cc.next()
when (t.type) {
Token.Type.SINGLE_LINE_COMMENT, Token.Type.MULTILINE_COMMENT -> {
pushPendingDocToken(t)
}
Token.Type.NEWLINE -> {
if (!prevWasComment) clearPendingDoc() else prevWasComment = false
}
Token.Type.EOF -> return t
else -> return t
}
}
}
// Set just before entering a declaration parse, taken from keyword token position
private var pendingDeclStart: Pos? = null
private var pendingDeclDoc: MiniDoc? = null
@@ -320,9 +338,15 @@ class Compiler(
}
Token.Type.LABEL -> continue
Token.Type.SINGLE_LINE_COMMENT, Token.Type.MULTILINE_COMMENT -> continue
Token.Type.SINGLE_LINE_COMMENT, Token.Type.MULTILINE_COMMENT -> {
pushPendingDocToken(t)
continue
}
Token.Type.NEWLINE -> continue
Token.Type.NEWLINE -> {
if (!prevWasComment) clearPendingDoc() else prevWasComment = false
continue
}
Token.Type.SEMICOLON -> continue
@@ -1345,7 +1369,7 @@ class Compiler(
modifiers.add(currentToken.value)
val next = cc.peekNextNonWhitespace()
if (next.type == Token.Type.ID || next.type == Token.Type.OBJECT) {
currentToken = cc.next()
currentToken = nextNonWhitespace()
} else {
break
}
@@ -1373,7 +1397,9 @@ class Compiler(
throw ScriptError(currentToken.pos, "abstract members cannot be private")
pendingDeclStart = firstId.pos
pendingDeclDoc = consumePendingDoc()
// pendingDeclDoc might be already set by an annotation
if (pendingDeclDoc == null)
pendingDeclDoc = consumePendingDoc()
val isMember = (codeContexts.lastOrNull() is CodeContext.ClassBody)
@@ -1900,27 +1926,31 @@ class Compiler(
// skip '{'
cc.skipTokenOfType(Token.Type.LBRACE)
do {
val t = cc.nextNonWhitespace()
when (t.type) {
Token.Type.ID -> {
names += t.value
positions += t.pos
val t1 = cc.nextNonWhitespace()
when (t1.type) {
Token.Type.COMMA ->
continue
if (cc.peekNextNonWhitespace().type != Token.Type.RBRACE) {
do {
val t = cc.nextNonWhitespace()
when (t.type) {
Token.Type.ID -> {
names += t.value
positions += t.pos
val t1 = cc.nextNonWhitespace()
when (t1.type) {
Token.Type.COMMA ->
continue
Token.Type.RBRACE -> break
else -> {
t1.raiseSyntax("unexpected token")
Token.Type.RBRACE -> break
else -> {
t1.raiseSyntax("unexpected token")
}
}
}
}
else -> t.raiseSyntax("expected enum entry name")
}
} while (true)
else -> t.raiseSyntax("expected enum entry name")
}
} while (true)
} else {
cc.nextNonWhitespace()
}
miniSink?.onEnumDecl(
MiniEnumDecl(
@@ -108,8 +108,9 @@ object LyngFormatter {
if (t == "finally") return true
// Short definition form: fun x() = or val x =
if (Regex("^(override\\s+)?(fun|fn)\\b.*=\\s*$").matches(t)) return true
if (Regex("^(private\\s+|protected\\s+|public\\s+|override\\s+)?(val|var)\\b.*=\\s*$").matches(t)) return true
// We allow optional 'static' as well
if (Regex("^(static\\s+)?(override\\s+)?(fun|fn)\\b.*=\\s*$").matches(t)) return true
if (Regex("^(static\\s+)?(private\\s+|protected\\s+|public\\s+|override\\s+)?(val|var)\\b.*=\\s*$").matches(t)) return true
// property accessors ending with ) or =
if (isAccessorRelated(t)) {
@@ -122,7 +123,8 @@ object LyngFormatter {
val t = s.trim()
if (!t.endsWith("=")) return false
// Is it a function or property definition?
if (Regex("\\b(fun|fn|val|var)\\b").containsMatchIn(t)) return true
// (Note: we exclude 'static' here to avoid double indent if it's already handled)
if (Regex("^(override\\s+)?(fun|fn|val|var)\\b").containsMatchIn(t)) return true
// Is it an accessor?
if (isPropertyAccessor(t)) return true
return false
@@ -44,13 +44,22 @@ data class MiniDoc(
fun parse(range: MiniRange, lines: Iterable<String>, extraTags: Map<String, List<String>> = emptyMap()): MiniDoc {
val parsedTags = mutableMapOf<String, MutableList<String>>()
var currentTag: String? = null
val currentContent = StringBuilder()
val currentContent = mutableListOf<String>()
fun flush() {
currentTag?.let { tag ->
parsedTags.getOrPut(tag) { mutableListOf() }.add(currentContent.toString().trim())
val trimmedContent = if (currentContent.size > 1) {
// First line is common content, we want to trim indent from subsequent lines
// based on THEIR common indent.
val firstLine = currentContent[0]
val remaining = currentContent.drop(1).joinToString("\n").trimIndent()
if (remaining.isEmpty()) firstLine else firstLine + "\n" + remaining
} else {
currentContent.joinToString("\n")
}
parsedTags.getOrPut(tag) { mutableListOf() }.add(trimmedContent.trim())
}
currentContent.setLength(0)
currentContent.clear()
}
val descriptionLines = mutableListOf<String>()
@@ -58,33 +67,45 @@ data class MiniDoc(
for (rawLine in lines) {
for (line in rawLine.lines()) {
val trimmed = line.trim()
// Strip leading '*' and space if present (standard doc comment style)
val cleanedLine = line.trim().let {
if (it.startsWith("*")) it.substring(1).removePrefix(" ") else line
}
val trimmed = cleanedLine.trim()
if (trimmed.startsWith("@")) {
inTags = true
flush()
val parts = trimmed.substring(1).split(Regex("\\s+"), 2)
currentTag = parts[0]
currentContent.append(parts.getOrNull(1) ?: "")
val tagFirstLine = parts.getOrNull(1) ?: ""
if (tagFirstLine.isNotEmpty()) {
// Find where the content starts in the cleaned line to preserve relative indentation
val contentIndex = cleanedLine.indexOf(tagFirstLine)
if (contentIndex >= 0) {
currentContent.add(cleanedLine.substring(contentIndex))
} else {
currentContent.add(tagFirstLine)
}
}
} else {
if (inTags) {
if (currentContent.isNotEmpty()) currentContent.append("\n")
currentContent.append(line)
currentContent.add(cleanedLine)
} else {
descriptionLines.add(line)
descriptionLines.add(cleanedLine)
}
}
}
}
flush()
val raw = descriptionLines.joinToString("\n").trimEnd()
val raw = descriptionLines.joinToString("\n").trimIndent().trim()
val summary = raw.lines().firstOrNull { it.isNotBlank() }?.trim()
val finalTags = parsedTags.toMutableMap()
extraTags.forEach { (k, v) ->
finalTags.getOrPut(k) { mutableListOf() }.addAll(v)
}
return MiniDoc(range, raw, summary, finalTags)
}
}