fixed formatter bugs and some tests; upgraded formatting in plugin
This commit is contained in:
1 parent
f592689631
commit
c63d643469
5 files changed
+131
-132
No files matched your search
@@ -738,8 +738,12 @@ class Compiler(
|
||||
val startAfterLbrace = cc.savePos()
|
||||
// Peek first non-ws token to decide whether it's likely a map literal
|
||||
val first = cc.peekNextNonWhitespace()
|
||||
// Empty {} should NOT be taken as a map literal to preserve block/lambda semantics
|
||||
if (first.type == Token.Type.RBRACE) return null
|
||||
// Empty {} should be parsed as an empty map literal in expression context
|
||||
if (first.type == Token.Type.RBRACE) {
|
||||
// consume '}' and return empty map literal
|
||||
cc.next() // consume the RBRACE
|
||||
return MapLiteralRef(emptyList())
|
||||
}
|
||||
if (first.type !in listOf(Token.Type.STRING, Token.Type.ID, Token.Type.ELLIPSIS)) return null
|
||||
|
||||
// Commit to map literal parsing
|
||||
@@ -1932,7 +1936,11 @@ class Compiler(
|
||||
val tOp = cc.next()
|
||||
if (tOp.value == "in") {
|
||||
// in loop
|
||||
val source = parseStatement() ?: throw ScriptError(start, "Bad for statement: expected expression")
|
||||
// We must parse an expression here. Using parseStatement() would treat a leading '{'
|
||||
// as a block, breaking inline map literals like: for (i in {foo: "bar"}) { ... }
|
||||
// So we parse an expression explicitly and wrap it into a StatementRef.
|
||||
val exprAfterIn = parseExpression() ?: throw ScriptError(start, "Bad for statement: expected expression")
|
||||
val source: Statement = exprAfterIn
|
||||
ensureRparen()
|
||||
|
||||
// Expose the loop variable name to the parser so identifiers inside the loop body
|
||||
|
||||
@@ -26,13 +26,15 @@ object LyngFormatter {
|
||||
|
||||
/** Returns the input with indentation recomputed from scratch, line by line. */
|
||||
fun reindent(text: String, config: LyngFormatConfig = LyngFormatConfig()): String {
|
||||
val lines = text.split('\n')
|
||||
// Normalize tabs to spaces globally before any transformation; results must contain no tabs
|
||||
val normalized = if (text.indexOf('\t') >= 0) text.replace("\t", " ".repeat(config.indentSize)) else text
|
||||
val lines = normalized.split('\n')
|
||||
val sb = StringBuilder(text.length + lines.size)
|
||||
var blockLevel = 0
|
||||
var parenBalance = 0
|
||||
var bracketBalance = 0
|
||||
var prevBracketContinuation = false
|
||||
val bracketBaseStack = ArrayDeque<String>()
|
||||
// We don't keep per-"[" base alignment; continuation rules define alignment.
|
||||
|
||||
fun codePart(s: String): String {
|
||||
val idx = s.indexOf("//")
|
||||
@@ -40,8 +42,8 @@ object LyngFormatter {
|
||||
}
|
||||
|
||||
fun indentOf(level: Int, continuation: Int): String =
|
||||
if (config.useTabs) "\t".repeat(level) + " ".repeat(continuation)
|
||||
else " ".repeat(level * config.indentSize + continuation)
|
||||
// Always produce spaces; tabs are not allowed in resulting code
|
||||
" ".repeat(level * config.indentSize + continuation)
|
||||
|
||||
var awaitingSingleIndent = false
|
||||
fun isControlHeaderNoBrace(s: String): Boolean {
|
||||
@@ -92,6 +94,11 @@ object LyngFormatter {
|
||||
else -> 0
|
||||
}
|
||||
|
||||
// Special rule: inside bracket lists, do not add base block indent for element lines.
|
||||
if (bracketBalance > 0 && firstChar != ']') {
|
||||
effectiveLevel = 0
|
||||
}
|
||||
|
||||
// Replace leading whitespace with the exact target indent; but keep fully blank lines truly empty
|
||||
val contentStart = line.indexOfFirst { it != ' ' && it != '\t' }.let { if (it < 0) line.length else it }
|
||||
var content = line.substring(contentStart)
|
||||
@@ -99,15 +106,15 @@ object LyngFormatter {
|
||||
if (content.startsWith("[")) {
|
||||
content = "[" + content.drop(1).trimStart()
|
||||
}
|
||||
// Determine base indent: for bracket blocks, preserve the exact leading whitespace
|
||||
val leadingWs = if (contentStart > 0) line.substring(0, contentStart) else ""
|
||||
val currentBracketBase = if (bracketBaseStack.isNotEmpty()) bracketBaseStack.last() else null
|
||||
val indentString = if (currentBracketBase != null) {
|
||||
val cont = if (continuation > 0) {
|
||||
if (config.useTabs) "\t" else " ".repeat(continuation)
|
||||
} else ""
|
||||
currentBracketBase + cont
|
||||
} else indentOf(effectiveLevel, continuation)
|
||||
// Normalize empty block on a single line: "{ }" -> "{}" (safe, idempotent)
|
||||
run {
|
||||
val t = content.trim()
|
||||
if (t.length >= 2 && t.first() == '{' && t.last() == '}' && t.substring(1, t.length - 1).isBlank()) {
|
||||
content = "{}"
|
||||
}
|
||||
}
|
||||
// Determine base indent using structural level and continuation only (spaces only)
|
||||
val indentString = indentOf(effectiveLevel, continuation)
|
||||
if (content.isEmpty()) {
|
||||
// preserve truly blank line as empty to avoid trailing spaces on empty lines
|
||||
// (also keeps continuation blocks visually clean)
|
||||
@@ -147,16 +154,14 @@ object LyngFormatter {
|
||||
// Reset one-shot flag after we used it on this line
|
||||
if (prevBracketContinuation) prevBracketContinuation = false
|
||||
// Set for the next iteration if current line ends with '['
|
||||
// Record whether THIS line ends with an opening '[' so the NEXT line gets a one-shot
|
||||
// continuation indent for the first element.
|
||||
if (endsWithBracket) {
|
||||
// One-shot continuation for the very next line
|
||||
prevBracketContinuation = true
|
||||
// Push base indent of the '[' line for subsequent lines in this bracket block
|
||||
bracketBaseStack.addLast(leadingWs)
|
||||
}
|
||||
|
||||
// If this line starts with ']' (closing bracket), pop the preserved base for this bracket level
|
||||
if (trimmedStart.startsWith("]") && bracketBaseStack.isNotEmpty()) {
|
||||
// ensure stack stays in sync with bracket levels
|
||||
bracketBaseStack.removeLast()
|
||||
} else {
|
||||
// Reset the one-shot flag if the previous line didn't end with '['
|
||||
prevBracketContinuation = false
|
||||
}
|
||||
}
|
||||
return sb.toString()
|
||||
@@ -263,7 +268,8 @@ object LyngFormatter {
|
||||
}
|
||||
i++
|
||||
}
|
||||
var baseIndent = if (onlyWs) base.toString() else ""
|
||||
// Normalize collected base indent: replace tabs with spaces
|
||||
var baseIndent = if (onlyWs) base.toString().replace("\t", " ".repeat(config.indentSize)) else ""
|
||||
var parentBaseIndent: String? = baseIndent
|
||||
if (baseIndent.isEmpty()) {
|
||||
// Fallback: use the indent of the nearest previous non-empty line as base.
|
||||
@@ -304,10 +310,11 @@ object LyngFormatter {
|
||||
if (foundIndent != null) {
|
||||
// If we are right after a line that opens a block, the base for the pasted
|
||||
// content should be one indent unit deeper than that line's base.
|
||||
parentBaseIndent = foundIndent
|
||||
val normFound = foundIndent.replace("\t", " ".repeat(config.indentSize))
|
||||
parentBaseIndent = normFound
|
||||
baseIndent = if (prevLineEndsWithOpenBrace) {
|
||||
if (config.useTabs) foundIndent + "\t" else foundIndent + " ".repeat(config.indentSize.coerceAtLeast(1))
|
||||
} else foundIndent
|
||||
normFound + " ".repeat(config.indentSize.coerceAtLeast(1))
|
||||
} else normFound
|
||||
}
|
||||
if (baseIndent.isEmpty()) {
|
||||
// Second fallback: compute structural block level up to this line and use it as base.
|
||||
@@ -329,8 +336,8 @@ object LyngFormatter {
|
||||
iScan = if (lineEnd < text.length) lineEnd + 1 else lineEnd
|
||||
}
|
||||
if (level > 0) {
|
||||
parentBaseIndent = if (config.useTabs) "\t".repeat(level - 1) else " ".repeat((level - 1).coerceAtLeast(0) * config.indentSize.coerceAtLeast(1))
|
||||
baseIndent = if (config.useTabs) "\t".repeat(level) else " ".repeat(level * config.indentSize.coerceAtLeast(1))
|
||||
parentBaseIndent = " ".repeat((level - 1).coerceAtLeast(0) * config.indentSize.coerceAtLeast(1))
|
||||
baseIndent = " ".repeat(level * config.indentSize.coerceAtLeast(1))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -343,9 +350,9 @@ object LyngFormatter {
|
||||
if (lineEnd < 0) lineEnd = formattedZero.length
|
||||
val line = formattedZero.substring(lineStart, lineEnd)
|
||||
if (line.isNotEmpty()) {
|
||||
val isCloser = line.dropWhile { it == ' ' || it == '\t' }.startsWith("}")
|
||||
val indentToUse = if (isCloser && parentBaseIndent != null) parentBaseIndent!! else baseIndent
|
||||
sb.append(indentToUse).append(line)
|
||||
// Apply the SAME base indent to all lines in the slice, including '}' lines.
|
||||
// Structural alignment of braces is already handled inside formattedZero.
|
||||
sb.append(baseIndent).append(line)
|
||||
} else sb.append(line)
|
||||
if (lineEnd < formattedZero.length) sb.append('\n')
|
||||
i = lineEnd + 1
|
||||
|
||||
Reference in new issue
Block a user