fix(config): SimpleToml multi-line string arrays
The line-based parser treated 'conventions = [' as the complete value,
binding conventions to listOf("[") and dropping every entry on the
following lines. Accumulate value lines until the top-level bracket
closes (quote-aware), skipping comments inside the array.
This commit is contained in:
@@ -17,8 +17,9 @@ internal object SimpleToml {
|
||||
val rootKeys = mutableMapOf<String, Any>()
|
||||
val sections = mutableMapOf<String, MutableMap<String, Any>>()
|
||||
|
||||
for (line in lines) {
|
||||
val trimmed = line.trim()
|
||||
var i = 0
|
||||
while (i < lines.size) {
|
||||
val trimmed = lines[i].trim()
|
||||
when {
|
||||
trimmed.isEmpty() || trimmed.startsWith("#") -> Unit
|
||||
trimmed.startsWith("[") && !trimmed.startsWith("[[") && trimmed.endsWith("]") -> {
|
||||
@@ -29,7 +30,14 @@ internal object SimpleToml {
|
||||
val eqIndex = trimmed.indexOf("=")
|
||||
if (eqIndex > 0) {
|
||||
val key = trimmed.substring(0, eqIndex).trim()
|
||||
val valueStr = trimmed.substring(eqIndex + 1).trim()
|
||||
var valueStr = trimmed.substring(eqIndex + 1).trim()
|
||||
// Multi-line array: accumulate lines until the top-level bracket closes.
|
||||
while (valueStr.startsWith("[") && !arrayClosed(valueStr) && i + 1 < lines.size) {
|
||||
i++
|
||||
val next = lines[i].trim()
|
||||
if (next.isEmpty() || next.startsWith("#")) continue
|
||||
valueStr += " $next"
|
||||
}
|
||||
val parsed = parseSimpleValue(valueStr)
|
||||
if (currentSection.isEmpty()) {
|
||||
rootKeys[key] = parsed
|
||||
@@ -39,10 +47,27 @@ internal object SimpleToml {
|
||||
}
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
return Pair(rootKeys, sections)
|
||||
}
|
||||
|
||||
/** True when every `[` opened outside quotes has been balanced by a `]`. */
|
||||
private fun arrayClosed(value: String): Boolean {
|
||||
var depth = 0
|
||||
var inQuotes = false
|
||||
var quoteChar = ' '
|
||||
for (ch in value) {
|
||||
when {
|
||||
(ch == '"' || ch == '\'') && !inQuotes -> { inQuotes = true; quoteChar = ch }
|
||||
ch == quoteChar && inQuotes -> inQuotes = false
|
||||
ch == '[' && !inQuotes -> depth++
|
||||
ch == ']' && !inQuotes -> depth--
|
||||
}
|
||||
}
|
||||
return depth <= 0
|
||||
}
|
||||
|
||||
fun asString(value: Any?, default: String = ""): String =
|
||||
if (value is String) value else default
|
||||
|
||||
|
||||
@@ -38,6 +38,30 @@ class ProjectProfileLoaderTest {
|
||||
assertEquals(mapOf("build" to "./gradlew build", "test" to "./gradlew check"), p.commands)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `multi-line conventions array parses all entries`() {
|
||||
// Idiomatic TOML splits long string arrays across lines; the line-based parser used
|
||||
// to swallow everything after the opening bracket (conventions became listOf("[")).
|
||||
val root = tempRoot()
|
||||
Files.writeString(
|
||||
Paths.get(root, ".correx", "project.toml"),
|
||||
"""
|
||||
about = "kernel"
|
||||
conventions = [
|
||||
"no bare try-catch",
|
||||
# comment inside the array
|
||||
"events are the only source of truth",
|
||||
]
|
||||
|
||||
[commands]
|
||||
test = "./gradlew check"
|
||||
""".trimIndent(),
|
||||
)
|
||||
val p = ProjectProfileLoader.load(root)
|
||||
assertEquals(listOf("no bare try-catch", "events are the only source of truth"), p.conventions)
|
||||
assertEquals(mapOf("test" to "./gradlew check"), p.commands)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `malformed file returns default without throwing`() {
|
||||
val root = tempRoot()
|
||||
|
||||
Reference in New Issue
Block a user