Format interpolated expressions as single line (#1247)

This forces iterpolated expressions to be single-line, so that newline
literals within the bounds of two string delimiters can be seen as
verbatime newlines in the resulting string.

Edge case: in the case of a line comment, it's not possible to keep
this as a single line expression.
These are kept as multi-line expressions.

Also:

* Remove `ForceWrap`, this node is not used.
* Rename `StringConstant` -> `StringChars`
This commit is contained in:
Daniel Chao
2025-10-24 03:23:41 -07:00
committed by GitHub
parent cce49a40fa
commit 3223083324
12 changed files with 355 additions and 126 deletions
@@ -37,6 +37,9 @@ internal class Builder(sourceText: String) {
private var id: Int = 0 private var id: Int = 0
private val source: CharArray = sourceText.toCharArray() private val source: CharArray = sourceText.toCharArray()
private var prevNode: Node? = null private var prevNode: Node? = null
private var noNewlines = false
private class CannotAvoidNewline : RuntimeException()
fun format(node: Node): FormatNode { fun format(node: Node): FormatNode {
prevNode = node prevNode = node
@@ -49,9 +52,8 @@ internal class Builder(sourceText: String) {
NodeType.TERMINAL, NodeType.TERMINAL,
NodeType.MODIFIER, NodeType.MODIFIER,
NodeType.IDENTIFIER, NodeType.IDENTIFIER,
NodeType.STRING_CONSTANT, NodeType.STRING_CHARS,
NodeType.STRING_ESCAPE, NodeType.STRING_ESCAPE,
NodeType.SINGLE_LINE_STRING_LITERAL_EXPR,
NodeType.INT_LITERAL_EXPR, NodeType.INT_LITERAL_EXPR,
NodeType.FLOAT_LITERAL_EXPR, NodeType.FLOAT_LITERAL_EXPR,
NodeType.BOOL_LITERAL_EXPR, NodeType.BOOL_LITERAL_EXPR,
@@ -64,9 +66,10 @@ internal class Builder(sourceText: String) {
NodeType.NOTHING_TYPE, NodeType.NOTHING_TYPE,
NodeType.SHEBANG, NodeType.SHEBANG,
NodeType.OPERATOR -> Text(node.text(source)) NodeType.OPERATOR -> Text(node.text(source))
NodeType.STRING_NEWLINE -> ForceLine NodeType.STRING_NEWLINE -> mustForceLine()
NodeType.MODULE_DECLARATION -> formatModuleDeclaration(node) NodeType.MODULE_DECLARATION -> formatModuleDeclaration(node)
NodeType.MODULE_DEFINITION -> formatModuleDefinition(node) NodeType.MODULE_DEFINITION -> formatModuleDefinition(node)
NodeType.SINGLE_LINE_STRING_LITERAL_EXPR -> formatSingleLineString(node)
NodeType.MULTI_LINE_STRING_LITERAL_EXPR -> formatMultilineString(node) NodeType.MULTI_LINE_STRING_LITERAL_EXPR -> formatMultilineString(node)
NodeType.ANNOTATION -> formatAnnotation(node) NodeType.ANNOTATION -> formatAnnotation(node)
NodeType.TYPEALIAS -> formatTypealias(node) NodeType.TYPEALIAS -> formatTypealias(node)
@@ -77,13 +80,13 @@ internal class Builder(sourceText: String) {
NodeType.PARAMETER_LIST_ELEMENTS -> formatParameterListElements(node) NodeType.PARAMETER_LIST_ELEMENTS -> formatParameterListElements(node)
NodeType.TYPE_PARAMETER_LIST -> formatTypeParameterList(node) NodeType.TYPE_PARAMETER_LIST -> formatTypeParameterList(node)
NodeType.TYPE_PARAMETER_LIST_ELEMENTS -> formatParameterListElements(node) NodeType.TYPE_PARAMETER_LIST_ELEMENTS -> formatParameterListElements(node)
NodeType.TYPE_PARAMETER -> Group(newId(), formatGeneric(node.children, SpaceOrLine)) NodeType.TYPE_PARAMETER -> Group(newId(), formatGeneric(node.children, spaceOrLine()))
NodeType.PARAMETER -> formatParameter(node) NodeType.PARAMETER -> formatParameter(node)
NodeType.EXTENDS_CLAUSE, NodeType.EXTENDS_CLAUSE,
NodeType.AMENDS_CLAUSE -> formatAmendsExtendsClause(node) NodeType.AMENDS_CLAUSE -> formatAmendsExtendsClause(node)
NodeType.IMPORT_LIST -> formatImportList(node) NodeType.IMPORT_LIST -> formatImportList(node)
NodeType.IMPORT -> formatImport(node) NodeType.IMPORT -> formatImport(node)
NodeType.IMPORT_ALIAS -> Group(newId(), formatGeneric(node.children, SpaceOrLine)) NodeType.IMPORT_ALIAS -> Group(newId(), formatGeneric(node.children, spaceOrLine()))
NodeType.CLASS -> formatClass(node) NodeType.CLASS -> formatClass(node)
NodeType.CLASS_HEADER -> formatClassHeader(node) NodeType.CLASS_HEADER -> formatClassHeader(node)
NodeType.CLASS_HEADER_EXTENDS -> formatClassHeaderExtends(node) NodeType.CLASS_HEADER_EXTENDS -> formatClassHeaderExtends(node)
@@ -167,7 +170,7 @@ internal class Builder(sourceText: String) {
private fun formatModule(node: Node): FormatNode { private fun formatModule(node: Node): FormatNode {
val nodes = val nodes =
formatGeneric(node.children) { prev, next -> formatGeneric(node.children) { prev, next ->
if (prev.linesBetween(next) > 1) TWO_NEWLINES else ForceLine if (prev.linesBetween(next) > 1) TWO_NEWLINES else forceLine()
} }
return Nodes(nodes) return Nodes(nodes)
} }
@@ -179,7 +182,7 @@ internal class Builder(sourceText: String) {
private fun formatModuleDefinition(node: Node): FormatNode { private fun formatModuleDefinition(node: Node): FormatNode {
val (prefixes, nodes) = splitPrefixes(node.children) val (prefixes, nodes) = splitPrefixes(node.children)
val fnodes = val fnodes =
formatGenericWithGen(nodes, SpaceOrLine) { node, next -> formatGenericWithGen(nodes, spaceOrLine()) { node, next ->
if (next == null) { if (next == null) {
indent(format(node)) indent(format(node))
} else { } else {
@@ -191,7 +194,7 @@ internal class Builder(sourceText: String) {
res res
} else { } else {
val sep = getSeparator(prefixes.last(), nodes.first()) val sep = getSeparator(prefixes.last(), nodes.first())
Nodes(formatGeneric(prefixes, SpaceOrLine) + listOf(sep, res)) Nodes(formatGeneric(prefixes, spaceOrLine()) + listOf(sep, res))
} }
} }
@@ -217,10 +220,10 @@ internal class Builder(sourceText: String) {
// short circuit // short circuit
if (node.children.size == 1) return format(node.children[0]) if (node.children.size == 1) return format(node.children[0])
val first = listOf(format(node.children[0]), Line) val first = listOf(format(node.children[0]), line())
val nodes = val nodes =
formatGeneric(node.children.drop(1)) { n1, _ -> formatGeneric(node.children.drop(1)) { n1, _ ->
if (n1.type == NodeType.TERMINAL) null else Line if (n1.type == NodeType.TERMINAL) null else line()
} }
return Group(newId(), first + listOf(Indent(nodes))) return Group(newId(), first + listOf(Indent(nodes)))
} }
@@ -240,28 +243,28 @@ internal class Builder(sourceText: String) {
} }
private fun formatAmendsExtendsClause(node: Node): FormatNode { private fun formatAmendsExtendsClause(node: Node): FormatNode {
val prefix = formatGeneric(node.children.dropLast(1), SpaceOrLine) val prefix = formatGeneric(node.children.dropLast(1), spaceOrLine())
// string constant // string constant
val suffix = Indent(listOf(format(node.children.last()))) val suffix = Indent(listOf(format(node.children.last())))
return Group(newId(), prefix + listOf(SpaceOrLine) + suffix) return Group(newId(), prefix + listOf(spaceOrLine()) + suffix)
} }
private fun formatImport(node: Node): FormatNode { private fun formatImport(node: Node): FormatNode {
return Group( return Group(
newId(), newId(),
formatGenericWithGen(node.children, SpaceOrLine) { node, _ -> formatGenericWithGen(node.children, spaceOrLine()) { node, _ ->
if (node.isTerminal("import")) format(node) else indent(format(node)) if (node.isTerminal("import")) format(node) else indent(format(node))
}, },
) )
} }
private fun formatAnnotation(node: Node): FormatNode { private fun formatAnnotation(node: Node): FormatNode {
return Group(newId(), formatGeneric(node.children, SpaceOrLine)) return Group(newId(), formatGeneric(node.children, spaceOrLine()))
} }
private fun formatTypealias(node: Node): FormatNode { private fun formatTypealias(node: Node): FormatNode {
val nodes = val nodes =
groupNonPrefixes(node) { children -> Group(newId(), formatGeneric(children, SpaceOrLine)) } groupNonPrefixes(node) { children -> Group(newId(), formatGeneric(children, spaceOrLine())) }
return Nodes(nodes) return Nodes(nodes)
} }
@@ -270,19 +273,19 @@ internal class Builder(sourceText: String) {
} }
private fun formatTypealiasBody(node: Node): FormatNode { private fun formatTypealiasBody(node: Node): FormatNode {
return Indent(formatGeneric(node.children, SpaceOrLine)) return Indent(formatGeneric(node.children, spaceOrLine()))
} }
private fun formatClass(node: Node): FormatNode { private fun formatClass(node: Node): FormatNode {
return Nodes(formatGeneric(node.children, SpaceOrLine)) return Nodes(formatGeneric(node.children, spaceOrLine()))
} }
private fun formatClassHeader(node: Node): FormatNode { private fun formatClassHeader(node: Node): FormatNode {
return groupOnSpace(formatGeneric(node.children, SpaceOrLine)) return groupOnSpace(formatGeneric(node.children, spaceOrLine()))
} }
private fun formatClassHeaderExtends(node: Node): FormatNode { private fun formatClassHeaderExtends(node: Node): FormatNode {
return indent(Group(newId(), formatGeneric(node.children, SpaceOrLine))) return indent(Group(newId(), formatGeneric(node.children, spaceOrLine())))
} }
private fun formatClassBody(node: Node): FormatNode { private fun formatClassBody(node: Node): FormatNode {
@@ -291,14 +294,14 @@ internal class Builder(sourceText: String) {
// no members // no members
return Nodes(formatGeneric(children, null)) return Nodes(formatGeneric(children, null))
} }
return Group(newId(), formatGeneric(children, ForceLine)) return Group(newId(), formatGeneric(children, forceLine()))
} }
private fun formatClassBodyElements(node: Node): FormatNode { private fun formatClassBodyElements(node: Node): FormatNode {
val nodes = val nodes =
formatGeneric(node.children) { prev, next -> formatGeneric(node.children) { prev, next ->
val lineDiff = prev.linesBetween(next) val lineDiff = prev.linesBetween(next)
if (lineDiff > 1 || lineDiff == 0) TWO_NEWLINES else ForceLine if (lineDiff > 1 || lineDiff == 0) TWO_NEWLINES else forceLine()
} }
return Indent(nodes) return Indent(nodes)
} }
@@ -313,8 +316,9 @@ internal class Builder(sourceText: String) {
val nodes = val nodes =
groupNonPrefixes(node) { children -> groupNonPrefixes(node) { children ->
val nodes = val nodes =
formatGenericWithGen(children, { _, _ -> if (sameLine) Space else SpaceOrLine }) { node, _ formatGenericWithGen(children, { _, _ -> if (sameLine) Space else spaceOrLine() }) {
-> node,
_ ->
if ((node.isExpressionOrPropertyBody()) && !sameLine) { if ((node.isExpressionOrPropertyBody()) && !sameLine) {
indent(format(node)) indent(format(node))
} else format(node) } else format(node)
@@ -330,11 +334,11 @@ internal class Builder(sourceText: String) {
type == NodeType.OBJECT_PROPERTY_BODY type == NodeType.OBJECT_PROPERTY_BODY
private fun formatClassPropertyHeader(node: Node): FormatNode { private fun formatClassPropertyHeader(node: Node): FormatNode {
return Group(newId(), formatGeneric(node.children, SpaceOrLine)) return Group(newId(), formatGeneric(node.children, spaceOrLine()))
} }
private fun formatClassPropertyHeaderBegin(node: Node): FormatNode { private fun formatClassPropertyHeaderBegin(node: Node): FormatNode {
return Group(newId(), formatGeneric(node.children, SpaceOrLine)) return Group(newId(), formatGeneric(node.children, spaceOrLine()))
} }
private fun formatClassPropertyBody(node: Node): FormatNode { private fun formatClassPropertyBody(node: Node): FormatNode {
@@ -349,7 +353,7 @@ internal class Builder(sourceText: String) {
val idx = node.children.indexOfFirst { it.type == NodeType.CLASS_METHOD_HEADER } val idx = node.children.indexOfFirst { it.type == NodeType.CLASS_METHOD_HEADER }
val prefixNodes = node.children.subList(0, idx) val prefixNodes = node.children.subList(0, idx)
prefixes += formatGeneric(prefixNodes, null) prefixes += formatGeneric(prefixNodes, null)
prefixes += getSeparator(prefixNodes.last(), node.children[idx], ForceLine) prefixes += getSeparator(prefixNodes.last(), node.children[idx], forceLine())
node.children.subList(idx, node.children.size) node.children.subList(idx, node.children.size)
} }
@@ -359,7 +363,7 @@ internal class Builder(sourceText: String) {
val headerGroupId = newId() val headerGroupId = newId()
val methodGroupId = newId() val methodGroupId = newId()
val headerNodes = val headerNodes =
formatGenericWithGen(header, SpaceOrLine) { node, _ -> formatGenericWithGen(header, spaceOrLine()) { node, _ ->
if (node.type == NodeType.PARAMETER_LIST) { if (node.type == NodeType.PARAMETER_LIST) {
formatParameterList(node, id = headerGroupId) formatParameterList(node, id = headerGroupId)
} else { } else {
@@ -385,7 +389,7 @@ internal class Builder(sourceText: String) {
if (isSameLineBody) { if (isSameLineBody) {
formatGeneric(bodyNodes, Space) formatGeneric(bodyNodes, Space)
} else { } else {
formatGenericWithGen(bodyNodes, SpaceOrLine) { node, next -> formatGenericWithGen(bodyNodes, spaceOrLine()) { node, next ->
if (next == null) indent(format(node)) else format(node) if (next == null) indent(format(node)) else format(node)
} }
} }
@@ -409,7 +413,7 @@ internal class Builder(sourceText: String) {
private fun formatParameter(node: Node): FormatNode { private fun formatParameter(node: Node): FormatNode {
if (node.children.size == 1) return format(node.children[0]) // underscore if (node.children.size == 1) return format(node.children[0]) // underscore
return Group(newId(), formatGeneric(node.children, SpaceOrLine)) return Group(newId(), formatGeneric(node.children, spaceOrLine()))
} }
private fun formatParameterList(node: Node, id: Int? = null): FormatNode { private fun formatParameterList(node: Node, id: Int? = null): FormatNode {
@@ -420,9 +424,9 @@ internal class Builder(sourceText: String) {
if (prev.isTerminal("(") || next.isTerminal(")")) { if (prev.isTerminal("(") || next.isTerminal(")")) {
if (next.isTerminal(")")) { if (next.isTerminal(")")) {
// trailing comma // trailing comma
IfWrap(groupId, nodes(Text(","), Line), Line) ifWrap(groupId, nodes(Text(","), line()), line())
} else Line } else line()
} else SpaceOrLine } else spaceOrLine()
} }
return if (id != null) Nodes(nodes) else Group(groupId, nodes) return if (id != null) Nodes(nodes) else Group(groupId, nodes)
} }
@@ -436,12 +440,12 @@ internal class Builder(sourceText: String) {
node.children, node.children,
{ prev, next -> { prev, next ->
if (prev.isTerminal("(") || next.isTerminal(")")) { if (prev.isTerminal("(") || next.isTerminal(")")) {
val node = if (hasTrailingLambda) Empty else Line val node = if (hasTrailingLambda) Empty else line()
if (next.isTerminal(")") && !hasTrailingLambda) { if (next.isTerminal(")") && !hasTrailingLambda) {
// trailing comma // trailing comma
IfWrap(groupId, nodes(Text(","), node), node) ifWrap(groupId, nodes(Text(","), node), node)
} else node } else node
} else SpaceOrLine } else spaceOrLine()
}, },
) { node, _ -> ) { node, _ ->
if (node.type == NodeType.ARGUMENT_LIST_ELEMENTS) { if (node.type == NodeType.ARGUMENT_LIST_ELEMENTS) {
@@ -460,9 +464,9 @@ internal class Builder(sourceText: String) {
return if (twoBy2) { return if (twoBy2) {
val pairs = pairArguments(children) val pairs = pairArguments(children)
val nodes = val nodes =
formatGenericWithGen(pairs, SpaceOrLine) { node, _ -> formatGenericWithGen(pairs, spaceOrLine()) { node, _ ->
if (node.type == NodeType.ARGUMENT_LIST_ELEMENTS) { if (node.type == NodeType.ARGUMENT_LIST_ELEMENTS) {
Group(newId(), formatGeneric(node.children, SpaceOrLine)) Group(newId(), formatGeneric(node.children, spaceOrLine()))
} else { } else {
format(node) format(node)
} }
@@ -473,17 +477,17 @@ internal class Builder(sourceText: String) {
val splitIndex = children.indexOfLast { it.type in SAME_LINE_EXPRS } val splitIndex = children.indexOfLast { it.type in SAME_LINE_EXPRS }
val normalParams = children.subList(0, splitIndex) val normalParams = children.subList(0, splitIndex)
val lastParam = children.subList(splitIndex, children.size) val lastParam = children.subList(splitIndex, children.size)
val trailingNode = if (endsWithClosingBracket(children[splitIndex])) Empty else Line val trailingNode = if (endsWithClosingBracket(children[splitIndex])) Empty else line()
val lastNodes = formatGeneric(lastParam, SpaceOrLine) val lastNodes = formatGeneric(lastParam, spaceOrLine())
if (normalParams.isEmpty()) { if (normalParams.isEmpty()) {
nodes(Group(newId(), lastNodes), trailingNode) nodes(Group(newId(), lastNodes), trailingNode)
} else { } else {
val separator = getSeparator(normalParams.last(), lastParam[0], Space) val separator = getSeparator(normalParams.last(), lastParam[0], Space)
val paramNodes = formatGeneric(normalParams, SpaceOrLine) val paramNodes = formatGeneric(normalParams, spaceOrLine())
nodes(Group(newId(), paramNodes), separator, Group(newId(), lastNodes), trailingNode) nodes(Group(newId(), paramNodes), separator, Group(newId(), lastNodes), trailingNode)
} }
} else { } else {
Indent(formatGeneric(children, SpaceOrLine)) Indent(formatGeneric(children, spaceOrLine()))
} }
} }
@@ -531,7 +535,7 @@ internal class Builder(sourceText: String) {
} }
private fun formatParameterListElements(node: Node): FormatNode { private fun formatParameterListElements(node: Node): FormatNode {
return Indent(formatGeneric(node.children, SpaceOrLine)) return Indent(formatGeneric(node.children, spaceOrLine()))
} }
private fun formatTypeParameterList(node: Node): FormatNode { private fun formatTypeParameterList(node: Node): FormatNode {
@@ -542,9 +546,9 @@ internal class Builder(sourceText: String) {
if (prev.isTerminal("<") || next.isTerminal(">")) { if (prev.isTerminal("<") || next.isTerminal(">")) {
if (next.isTerminal(">")) { if (next.isTerminal(">")) {
// trailing comma // trailing comma
IfWrap(id, nodes(Text(","), Line), Line) ifWrap(id, nodes(Text(","), line()), line())
} else Line } else line()
} else SpaceOrLine } else spaceOrLine()
} }
return Group(id, nodes) return Group(id, nodes)
} }
@@ -552,10 +556,10 @@ internal class Builder(sourceText: String) {
private fun formatObjectParameterList(node: Node): FormatNode { private fun formatObjectParameterList(node: Node): FormatNode {
// object param lists don't have trailing commas, as they have a trailing -> // object param lists don't have trailing commas, as they have a trailing ->
val groupId = newId() val groupId = newId()
val nonWrappingNodes = Nodes(formatGeneric(node.children, SpaceOrLine)) val nonWrappingNodes = Nodes(formatGeneric(node.children, spaceOrLine()))
// double indent the params if they wrap // double indent the params if they wrap
val wrappingNodes = indent(Indent(listOf(Line) + nonWrappingNodes)) val wrappingNodes = indent(Indent(listOf(line()) + nonWrappingNodes))
return Group(groupId, listOf(IfWrap(groupId, wrappingNodes, nodes(Space, nonWrappingNodes)))) return Group(groupId, listOf(ifWrap(groupId, wrappingNodes, nodes(Space, nonWrappingNodes))))
} }
private fun formatObjectBody(node: Node): FormatNode { private fun formatObjectBody(node: Node): FormatNode {
@@ -568,8 +572,8 @@ internal class Builder(sourceText: String) {
if (next.type == NodeType.OBJECT_PARAMETER_LIST) Empty if (next.type == NodeType.OBJECT_PARAMETER_LIST) Empty
else if (prev.isTerminal("{") || next.isTerminal("}")) { else if (prev.isTerminal("{") || next.isTerminal("}")) {
val lines = prev.linesBetween(next) val lines = prev.linesBetween(next)
if (lines == 0) SpaceOrLine else ForceLine if (lines == 0) spaceOrLine() else forceSpaceyLine()
} else SpaceOrLine } else spaceOrLine()
}, },
) { node, _ -> ) { node, _ ->
if (node.type == NodeType.OBJECT_MEMBER_LIST) { if (node.type == NodeType.OBJECT_MEMBER_LIST) {
@@ -584,8 +588,8 @@ internal class Builder(sourceText: String) {
formatGeneric(node.children) { prev, next -> formatGeneric(node.children) { prev, next ->
val lines = prev.linesBetween(next) val lines = prev.linesBetween(next)
when (lines) { when (lines) {
0 -> IfWrap(groupId, Line, Text("; ")) 0 -> ifWrap(groupId, line(), Text("; "))
1 -> ForceLine 1 -> forceLine()
else -> TWO_NEWLINES else -> TWO_NEWLINES
} }
} }
@@ -593,7 +597,7 @@ internal class Builder(sourceText: String) {
} }
private fun formatObjectEntryHeader(node: Node): FormatNode { private fun formatObjectEntryHeader(node: Node): FormatNode {
return Group(newId(), formatGeneric(node.children, SpaceOrLine)) return Group(newId(), formatGeneric(node.children, spaceOrLine()))
} }
private fun formatForGenerator(node: Node): FormatNode { private fun formatForGenerator(node: Node): FormatNode {
@@ -603,7 +607,7 @@ internal class Builder(sourceText: String) {
prev.type == NodeType.FOR_GENERATOR_HEADER || next.type == NodeType.FOR_GENERATOR_HEADER prev.type == NodeType.FOR_GENERATOR_HEADER || next.type == NodeType.FOR_GENERATOR_HEADER
) { ) {
Space Space
} else SpaceOrLine } else spaceOrLine()
} }
return Group(newId(), nodes) return Group(newId(), nodes)
} }
@@ -611,7 +615,7 @@ internal class Builder(sourceText: String) {
private fun formatForGeneratorHeader(node: Node): FormatNode { private fun formatForGeneratorHeader(node: Node): FormatNode {
val nodes = val nodes =
formatGeneric(node.children) { prev, next -> formatGeneric(node.children) { prev, next ->
if (prev.isTerminal("(") || next.isTerminal(")")) Line else null if (prev.isTerminal("(") || next.isTerminal(")")) line() else null
} }
return Group(newId(), nodes) return Group(newId(), nodes)
} }
@@ -620,7 +624,7 @@ internal class Builder(sourceText: String) {
val nodes = val nodes =
formatGenericWithGen( formatGenericWithGen(
node.children, node.children,
{ _, next -> if (next.type in SAME_LINE_EXPRS) Space else SpaceOrLine }, { _, next -> if (next.type in SAME_LINE_EXPRS) Space else spaceOrLine() },
) { node, _ -> ) { node, _ ->
if (node.type.isExpression && node.type !in SAME_LINE_EXPRS) indent(format(node)) if (node.type.isExpression && node.type !in SAME_LINE_EXPRS) indent(format(node))
else format(node) else format(node)
@@ -629,7 +633,7 @@ internal class Builder(sourceText: String) {
} }
private fun formatForGeneratorHeaderDefinitionHeader(node: Node): FormatNode { private fun formatForGeneratorHeaderDefinitionHeader(node: Node): FormatNode {
val nodes = formatGeneric(node.children, SpaceOrLine) val nodes = formatGeneric(node.children, spaceOrLine())
return Group(newId(), nodes) return Group(newId(), nodes)
} }
@@ -643,7 +647,7 @@ internal class Builder(sourceText: String) {
) { ) {
Space Space
} else { } else {
SpaceOrLine spaceOrLine()
} }
} }
return Group(newId(), nodes) return Group(newId(), nodes)
@@ -653,7 +657,9 @@ internal class Builder(sourceText: String) {
val nodes = val nodes =
formatGenericWithGen( formatGenericWithGen(
node.children, node.children,
{ prev, next -> if (prev.isTerminal("(") || next.isTerminal(")")) Line else SpaceOrLine }, { prev, next ->
if (prev.isTerminal("(") || next.isTerminal(")")) line() else spaceOrLine()
},
) { node, _ -> ) { node, _ ->
if (!node.type.isAffix && node.type != NodeType.TERMINAL) { if (!node.type.isAffix && node.type != NodeType.TERMINAL) {
indent(format(node)) indent(format(node))
@@ -664,7 +670,7 @@ internal class Builder(sourceText: String) {
private fun formatMemberPredicate(node: Node): FormatNode { private fun formatMemberPredicate(node: Node): FormatNode {
val nodes = val nodes =
formatGenericWithGen(node.children, SpaceOrLine) { node, next -> formatGenericWithGen(node.children, spaceOrLine()) { node, next ->
if (next == null && node.type != NodeType.OBJECT_BODY) { if (next == null && node.type != NodeType.OBJECT_BODY) {
indent(format(node)) indent(format(node))
} else format(node) } else format(node)
@@ -672,8 +678,46 @@ internal class Builder(sourceText: String) {
return Group(newId(), nodes) return Group(newId(), nodes)
} }
private fun formatStringParts(nodes: List<Node>): List<FormatNode> {
return buildList {
var isInStringInterpolation = false
val cursor = nodes.iterator().peekable()
var prev: Node? = null
while (cursor.hasNext()) {
if (isInStringInterpolation) {
val prevNoNewlines = noNewlines
noNewlines = true
val elems = cursor.takeUntilBefore { it.isTerminal(")") }
getSeparator(prev!!, elems.first(), { _, _ -> null })?.let { add(it) }
val formatted =
try {
formatGeneric(elems, null)
} catch (_: CannotAvoidNewline) {
noNewlines = false
formatGeneric(elems, null)
}
addAll(formatted)
getSeparator(elems.last(), cursor.peek(), { _, _ -> null })?.let { add(it) }
noNewlines = prevNoNewlines
isInStringInterpolation = false
continue
}
val elem = cursor.next()
if (elem.type == NodeType.TERMINAL && elem.text().endsWith("(")) {
isInStringInterpolation = true
}
add(format(elem))
prev = elem
}
}
}
private fun formatSingleLineString(node: Node): FormatNode {
return Group(newId(), formatStringParts(node.children))
}
private fun formatMultilineString(node: Node): FormatNode { private fun formatMultilineString(node: Node): FormatNode {
val nodes = formatGeneric(node.children, null) val nodes = formatStringParts(node.children)
return MultilineStringGroup(node.children.last().span.colBegin, nodes) return MultilineStringGroup(node.children.last().span.colBegin, nodes)
} }
@@ -682,7 +726,7 @@ internal class Builder(sourceText: String) {
formatGeneric(node.children) { _, next -> formatGeneric(node.children) { _, next ->
if (next.type == NodeType.IF_ELSE_EXPR && next.children[0].type == NodeType.IF_EXPR) { if (next.type == NodeType.IF_ELSE_EXPR && next.children[0].type == NodeType.IF_EXPR) {
Space Space
} else SpaceOrLine } else spaceOrLine()
} }
return Group(newId(), nodes) return Group(newId(), nodes)
} }
@@ -690,7 +734,7 @@ internal class Builder(sourceText: String) {
private fun formatIfHeader(node: Node): FormatNode { private fun formatIfHeader(node: Node): FormatNode {
val nodes = val nodes =
formatGeneric(node.children) { _, next -> formatGeneric(node.children) { _, next ->
if (next.type == NodeType.IF_CONDITION) Space else SpaceOrLine if (next.type == NodeType.IF_CONDITION) Space else spaceOrLine()
} }
return Group(newId(), nodes) return Group(newId(), nodes)
} }
@@ -698,7 +742,7 @@ internal class Builder(sourceText: String) {
private fun formatIfCondition(node: Node): FormatNode { private fun formatIfCondition(node: Node): FormatNode {
val nodes = val nodes =
formatGeneric(node.children) { prev, next -> formatGeneric(node.children) { prev, next ->
if (prev.isTerminal("(") || next.isTerminal(")")) Line else SpaceOrLine if (prev.isTerminal("(") || next.isTerminal(")")) line() else spaceOrLine()
} }
return Group(newId(), nodes) return Group(newId(), nodes)
} }
@@ -723,12 +767,12 @@ internal class Builder(sourceText: String) {
} }
private fun formatNewExpr(node: Node): FormatNode { private fun formatNewExpr(node: Node): FormatNode {
val nodes = formatGeneric(node.children, SpaceOrLine) val nodes = formatGeneric(node.children, spaceOrLine())
return Group(newId(), nodes) return Group(newId(), nodes)
} }
private fun formatNewHeader(node: Node): FormatNode { private fun formatNewHeader(node: Node): FormatNode {
val nodes = formatGeneric(node.children, SpaceOrLine) val nodes = formatGeneric(node.children, spaceOrLine())
return Group(newId(), nodes) return Group(newId(), nodes)
} }
@@ -737,7 +781,9 @@ internal class Builder(sourceText: String) {
val nodes = val nodes =
formatGenericWithGen( formatGenericWithGen(
node.children, node.children,
{ prev, next -> if (prev.isTerminal("(") || next.isTerminal(")")) Line else SpaceOrLine }, { prev, next ->
if (prev.isTerminal("(") || next.isTerminal(")")) line() else spaceOrLine()
},
) { node, _ -> ) { node, _ ->
if (node.type.isExpression) indent(format(node)) else format(node) if (node.type.isExpression) indent(format(node)) else format(node)
} }
@@ -757,7 +803,7 @@ internal class Builder(sourceText: String) {
val expr = body.children.find { it.type.isExpression }!! val expr = body.children.find { it.type.isExpression }!!
isSameLineExpr(expr) isSameLineExpr(expr)
} }
val sep = if (sameLine) Space else SpaceOrLine val sep = if (sameLine) Space else spaceOrLine()
val bodySep = getSeparator(params.last(), rest.first(), sep) val bodySep = getSeparator(params.last(), rest.first(), sep)
val nodes = formatGeneric(params, sep) val nodes = formatGeneric(params, sep)
@@ -775,7 +821,7 @@ internal class Builder(sourceText: String) {
val nodes = val nodes =
formatGenericWithGen( formatGenericWithGen(
node.children, node.children,
{ _, next -> if (next.type == NodeType.LET_PARAMETER_DEFINITION) Space else SpaceOrLine }, { _, next -> if (next.type == NodeType.LET_PARAMETER_DEFINITION) Space else spaceOrLine() },
) { node, next -> ) { node, next ->
if (next == null) { if (next == null) {
if (node.type == NodeType.LET_EXPR) { if (node.type == NodeType.LET_EXPR) {
@@ -791,7 +837,7 @@ internal class Builder(sourceText: String) {
private fun formatLetParameterDefinition(node: Node): FormatNode { private fun formatLetParameterDefinition(node: Node): FormatNode {
val nodes = val nodes =
formatGeneric(node.children) { prev, next -> formatGeneric(node.children) { prev, next ->
if (prev.isTerminal("(") || next.isTerminal(")")) Line else SpaceOrLine if (prev.isTerminal("(") || next.isTerminal(")")) line() else spaceOrLine()
} }
return Group(newId(), nodes) return Group(newId(), nodes)
} }
@@ -810,17 +856,17 @@ internal class Builder(sourceText: String) {
when (prev.text()) { when (prev.text()) {
".", ".",
"?." -> null "?." -> null
"-" -> SpaceOrLine "-" -> spaceOrLine()
else -> Space else -> Space
} }
} else if (next.type == NodeType.OPERATOR) { } else if (next.type == NodeType.OPERATOR) {
when (next.text()) { when (next.text()) {
".", ".",
"?." -> if (hasMultipleLambdas) ForceLine else Line "?." -> if (hasMultipleLambdas) forceLine() else line()
"-" -> Space "-" -> Space
else -> SpaceOrLine else -> spaceOrLine()
} }
} else SpaceOrLine } else spaceOrLine()
} }
val shouldGroup = node.children.size == flat.size val shouldGroup = node.children.size == flat.size
return Group(newId(), indentAfterFirstNewline(nodes, shouldGroup)) return Group(newId(), indentAfterFirstNewline(nodes, shouldGroup))
@@ -843,7 +889,7 @@ internal class Builder(sourceText: String) {
val nodes = val nodes =
formatGenericWithGen( formatGenericWithGen(
node.children, node.children,
{ prev, next -> if (prev.isTerminal("(") || next.isTerminal(")")) Line else null }, { prev, next -> if (prev.isTerminal("(") || next.isTerminal(")")) line() else null },
) { node, _ -> ) { node, _ ->
if (node.type.isExpression) indent(format(node)) else format(node) if (node.type.isExpression) indent(format(node)) else format(node)
} }
@@ -851,13 +897,13 @@ internal class Builder(sourceText: String) {
} }
private fun formatDeclaredType(node: Node): FormatNode { private fun formatDeclaredType(node: Node): FormatNode {
return Nodes(formatGeneric(node.children, SpaceOrLine)) return Nodes(formatGeneric(node.children, spaceOrLine()))
} }
private fun formatConstrainedType(node: Node): FormatNode { private fun formatConstrainedType(node: Node): FormatNode {
val nodes = val nodes =
formatGeneric(node.children) { _, next -> formatGeneric(node.children) { _, next ->
if (next.type == NodeType.CONSTRAINED_TYPE_CONSTRAINT) null else SpaceOrLine if (next.type == NodeType.CONSTRAINED_TYPE_CONSTRAINT) null else spaceOrLine()
} }
return Group(newId(), nodes) return Group(newId(), nodes)
} }
@@ -866,7 +912,7 @@ internal class Builder(sourceText: String) {
val nodes = val nodes =
formatGeneric(node.children) { prev, next -> formatGeneric(node.children) { prev, next ->
when { when {
next.isTerminal("|") -> SpaceOrLine next.isTerminal("|") -> spaceOrLine()
prev.isTerminal("|") -> Space prev.isTerminal("|") -> Space
else -> null else -> null
} }
@@ -878,7 +924,9 @@ internal class Builder(sourceText: String) {
val nodes = val nodes =
formatGenericWithGen( formatGenericWithGen(
node.children, node.children,
{ prev, next -> if (prev.isTerminal("(") || next.isTerminal(")")) Line else SpaceOrLine }, { prev, next ->
if (prev.isTerminal("(") || next.isTerminal(")")) line() else spaceOrLine()
},
) { node, next -> ) { node, next ->
if (next == null) indent(format(node)) else format(node) if (next == null) indent(format(node)) else format(node)
} }
@@ -890,13 +938,13 @@ internal class Builder(sourceText: String) {
val groupId = newId() val groupId = newId()
val nodes = val nodes =
formatGeneric(node.children) { prev, next -> formatGeneric(node.children) { prev, next ->
if (prev.isTerminal("(") || next.isTerminal(")")) Line else SpaceOrLine if (prev.isTerminal("(") || next.isTerminal(")")) line() else spaceOrLine()
} }
return Group(groupId, nodes) return Group(groupId, nodes)
} }
private fun formatParenthesizedTypeElements(node: Node): FormatNode { private fun formatParenthesizedTypeElements(node: Node): FormatNode {
return indent(Group(newId(), formatGeneric(node.children, SpaceOrLine))) return indent(Group(newId(), formatGeneric(node.children, spaceOrLine())))
} }
private fun formatTypeAnnotation(node: Node): FormatNode { private fun formatTypeAnnotation(node: Node): FormatNode {
@@ -907,7 +955,7 @@ internal class Builder(sourceText: String) {
val nodes = mutableListOf<FormatNode>() val nodes = mutableListOf<FormatNode>()
val children = node.children.groupBy { it.type.isAffix } val children = node.children.groupBy { it.type.isAffix }
if (children[true] != null) { if (children[true] != null) {
nodes += formatGeneric(children[true]!!, SpaceOrLine) nodes += formatGeneric(children[true]!!, spaceOrLine())
} }
val modifiers = children[false]!!.sortedBy(::modifierPrecedence) val modifiers = children[false]!!.sortedBy(::modifierPrecedence)
nodes += formatGeneric(modifiers, Space) nodes += formatGeneric(modifiers, Space)
@@ -918,8 +966,8 @@ internal class Builder(sourceText: String) {
val nodes = mutableListOf<FormatNode>() val nodes = mutableListOf<FormatNode>()
val children = node.children.groupBy { it.type.isAffix } val children = node.children.groupBy { it.type.isAffix }
if (children[true] != null) { if (children[true] != null) {
nodes += formatGeneric(children[true]!!, SpaceOrLine) nodes += formatGeneric(children[true]!!, spaceOrLine())
nodes += ForceLine nodes += forceLine()
} }
val allImports = children[false]!! val allImports = children[false]!!
@@ -952,27 +1000,27 @@ internal class Builder(sourceText: String) {
if (absolute != null) { if (absolute != null) {
for ((i, imp) in absolute.withIndex()) { for ((i, imp) in absolute.withIndex()) {
if (i > 0) nodes += ForceLine if (i > 0) nodes += forceLine()
nodes += format(imp) nodes += format(imp)
} }
if (projects != null || relatives != null) nodes += ForceLine if (projects != null || relatives != null) nodes += forceLine()
shouldNewline = true shouldNewline = true
} }
if (projects != null) { if (projects != null) {
if (shouldNewline) nodes += ForceLine if (shouldNewline) nodes += forceLine()
for ((i, imp) in projects.withIndex()) { for ((i, imp) in projects.withIndex()) {
if (i > 0) nodes += ForceLine if (i > 0) nodes += forceLine()
nodes += format(imp) nodes += format(imp)
} }
if (relatives != null) nodes += ForceLine if (relatives != null) nodes += forceLine()
shouldNewline = true shouldNewline = true
} }
if (relatives != null) { if (relatives != null) {
if (shouldNewline) nodes += ForceLine if (shouldNewline) nodes += forceLine()
for ((i, imp) in relatives.withIndex()) { for ((i, imp) in relatives.withIndex()) {
if (i > 0) nodes += ForceLine if (i > 0) nodes += forceLine()
nodes += format(imp) nodes += format(imp)
} }
} }
@@ -1005,7 +1053,7 @@ internal class Builder(sourceText: String) {
// skip semicolons // skip semicolons
val children = children.filter { !it.isSemicolon() } val children = children.filter { !it.isSemicolon() }
// short circuit // short circuit
if (children.isEmpty()) return listOf(SpaceOrLine) if (children.isEmpty()) return listOf(spaceOrLine())
if (children.size == 1) return listOf(format(children[0])) if (children.size == 1) return listOf(format(children[0]))
val nodes = mutableListOf<FormatNode>() val nodes = mutableListOf<FormatNode>()
@@ -1046,19 +1094,19 @@ internal class Builder(sourceText: String) {
val prefixes = children.subList(0, index) val prefixes = children.subList(0, index)
val nodes = children.subList(index, children.size) val nodes = children.subList(index, children.size)
val res = mutableListOf<FormatNode>() val res = mutableListOf<FormatNode>()
res += formatGeneric(prefixes, SpaceOrLine) res += formatGeneric(prefixes, spaceOrLine())
res += getSeparator(prefixes.last(), nodes.first()) res += getSeparator(prefixes.last(), nodes.first())
res += groupFn(nodes) res += groupFn(nodes)
return res return res
} }
private fun getImportUrl(node: Node): String = private fun getImportUrl(node: Node): String =
node.findChildByType(NodeType.STRING_CONSTANT)!!.text().drop(1).dropLast(1) node.findChildByType(NodeType.STRING_CHARS)!!.text().drop(1).dropLast(1)
private fun getSeparator( private fun getSeparator(
prev: Node, prev: Node,
next: Node, next: Node,
separator: FormatNode = SpaceOrLine, separator: FormatNode = spaceOrLine(),
): FormatNode { ): FormatNode {
return getSeparator(prev, next) { _, _ -> separator }!! return getSeparator(prev, next) { _, _ -> separator }!!
} }
@@ -1073,19 +1121,21 @@ internal class Builder(sourceText: String) {
if (prev.linesBetween(next) > 1) { if (prev.linesBetween(next) > 1) {
TWO_NEWLINES TWO_NEWLINES
} else { } else {
ForceLine mustForceLine()
} }
} }
hasTrailingAffix(prev, next) -> Space hasTrailingAffix(prev, next) -> Space
prev.type == NodeType.DOC_COMMENT || prev.type == NodeType.ANNOTATION -> ForceLine prev.type == NodeType.DOC_COMMENT -> mustForceLine()
prev.type == NodeType.ANNOTATION -> forceLine()
prev.type in FORCE_LINE_AFFIXES || next.type.isAffix -> { prev.type in FORCE_LINE_AFFIXES || next.type.isAffix -> {
if (prev.linesBetween(next) > 1) { if (prev.linesBetween(next) > 1) {
TWO_NEWLINES TWO_NEWLINES
} else { } else {
ForceLine mustForceLine()
} }
} }
prev.type == NodeType.BLOCK_COMMENT -> if (prev.linesBetween(next) > 0) ForceLine else Space prev.type == NodeType.BLOCK_COMMENT ->
if (prev.linesBetween(next) > 0) forceSpaceyLine() else Space
next.type in EMPTY_SUFFIXES || next.type in EMPTY_SUFFIXES ||
prev.isTerminal("[", "!", "@", "[[") || prev.isTerminal("[", "!", "@", "[[") ||
next.isTerminal("]", "?", ",") -> null next.isTerminal("]", "?", ",") -> null
@@ -1098,6 +1148,30 @@ internal class Builder(sourceText: String) {
} }
} }
private fun line(): FormatNode {
return if (noNewlines) Empty else Line
}
private fun spaceOrLine(): FormatNode {
return if (noNewlines) Space else SpaceOrLine
}
private fun mustForceLine(): FormatNode {
return if (noNewlines) throw CannotAvoidNewline() else ForceLine
}
private fun forceLine(): FormatNode {
return if (noNewlines) Empty else ForceLine
}
private fun forceSpaceyLine(): FormatNode {
return if (noNewlines) Space else ForceLine
}
private fun ifWrap(id: Int, ifWrap: FormatNode, ifNotWrap: FormatNode): FormatNode {
return if (noNewlines) ifNotWrap else IfWrap(id, ifWrap, ifNotWrap)
}
private fun hasTrailingAffix(node: Node, next: Node): Boolean { private fun hasTrailingAffix(node: Node, next: Node): Boolean {
if (node.span.lineEnd < next.span.lineBegin) return false if (node.span.lineEnd < next.span.lineBegin) return false
var n: Node? = next var n: Node? = next
@@ -1201,8 +1275,8 @@ internal class Builder(sourceText: String) {
private class ImportComparator(private val source: CharArray) : Comparator<Node> { private class ImportComparator(private val source: CharArray) : Comparator<Node> {
override fun compare(o1: Node, o2: Node): Int { override fun compare(o1: Node, o2: Node): Int {
val import1 = o1.findChildByType(NodeType.STRING_CONSTANT)?.text(source) val import1 = o1.findChildByType(NodeType.STRING_CHARS)?.text(source)
val import2 = o2.findChildByType(NodeType.STRING_CONSTANT)?.text(source) val import2 = o2.findChildByType(NodeType.STRING_CHARS)?.text(source)
if (import1 == null || import2 == null) { if (import1 == null || import2 == null) {
// should never happen // should never happen
throw RuntimeException("ImportComparator: not an import") throw RuntimeException("ImportComparator: not an import")
@@ -1222,7 +1296,7 @@ internal class Builder(sourceText: String) {
// returns true if this node is not an affix or terminal // returns true if this node is not an affix or terminal
private fun Node.isProper(): Boolean = !type.isAffix && type != NodeType.TERMINAL private fun Node.isProper(): Boolean = !type.isAffix && type != NodeType.TERMINAL
private fun <T> List<T>.splitOn(pred: (T) -> Boolean): Pair<List<T>, List<T>> { private inline fun <T> List<T>.splitOn(pred: (T) -> Boolean): Pair<List<T>, List<T>> {
val index = indexOfFirst { pred(it) } val index = indexOfFirst { pred(it) }
return if (index == -1) { return if (index == -1) {
Pair(this, emptyList()) Pair(this, emptyList())
@@ -1243,6 +1317,52 @@ internal class Builder(sourceText: String) {
return false return false
} }
class PeekableIterator<T>(private val iterator: Iterator<T>) : Iterator<T> {
private var peek: T? = null
private var hasPeek = false
override fun next(): T {
return if (hasPeek) {
hasPeek = false
peek!!
} else {
iterator.next()
}
}
override fun hasNext(): Boolean {
return if (hasPeek) true else iterator.hasNext()
}
fun peek(): T {
if (!hasNext()) {
throw NoSuchElementException()
}
if (hasPeek) {
return peek!!
}
peek = iterator.next()
hasPeek = true
return peek!!
}
inline fun takeUntilBefore(predicate: (T) -> Boolean): List<T> {
return buildList {
while (true) {
if (!hasNext() || predicate(peek())) {
return@buildList
}
add(next())
}
}
}
}
private fun <T> Iterator<T>.peekable(): PeekableIterator<T> {
return PeekableIterator(this)
}
companion object { companion object {
private val ABSOLUTE_URL_REGEX = Regex("""\w+:.*""") private val ABSOLUTE_URL_REGEX = Regex("""\w+:.*""")
@@ -17,7 +17,6 @@ package org.pkl.formatter
import org.pkl.formatter.ast.Empty import org.pkl.formatter.ast.Empty
import org.pkl.formatter.ast.ForceLine import org.pkl.formatter.ast.ForceLine
import org.pkl.formatter.ast.ForceWrap
import org.pkl.formatter.ast.FormatNode import org.pkl.formatter.ast.FormatNode
import org.pkl.formatter.ast.Group import org.pkl.formatter.ast.Group
import org.pkl.formatter.ast.IfWrap import org.pkl.formatter.ast.IfWrap
@@ -56,11 +55,6 @@ internal class Generator {
} }
node.nodes.forEach { node(it, wrap) } node.nodes.forEach { node(it, wrap) }
} }
is ForceWrap -> {
wrapped += node.id
val wrap = Wrap.ENABLED
node.nodes.forEach { node(it, wrap) }
}
is IfWrap -> { is IfWrap -> {
if (wrapped.contains(node.id)) { if (wrapped.contains(node.id)) {
node(node.ifWrap, Wrap.ENABLED) node(node.ifWrap, Wrap.ENABLED)
@@ -30,14 +30,14 @@ sealed interface FormatNode {
is Nodes -> nodes.sumOf { it.width(wrapped) } is Nodes -> nodes.sumOf { it.width(wrapped) }
is Group -> nodes.sumOf { it.width(wrapped) } is Group -> nodes.sumOf { it.width(wrapped) }
is Indent -> nodes.sumOf { it.width(wrapped) } is Indent -> nodes.sumOf { it.width(wrapped) }
is ForceWrap -> nodes.sumOf { it.width(wrapped + id) }
is IfWrap -> if (id in wrapped) ifWrap.width(wrapped) else ifNotWrap.width(wrapped) is IfWrap -> if (id in wrapped) ifWrap.width(wrapped) else ifNotWrap.width(wrapped)
is Text -> text.length is Text -> text.length
is SpaceOrLine, SpaceOrLine,
is Space -> 1 Space -> 1
is ForceLine, ForceLine,
is MultilineStringGroup -> Generator.MAX is MultilineStringGroup -> Generator.MAX
else -> 0 Empty -> 0
Line -> 0
} }
} }
@@ -59,8 +59,6 @@ data class Nodes(val nodes: List<FormatNode>) : FormatNode
data class Group(val id: Int, val nodes: List<FormatNode>) : FormatNode data class Group(val id: Int, val nodes: List<FormatNode>) : FormatNode
data class ForceWrap(val id: Int, val nodes: List<FormatNode>) : FormatNode
data class MultilineStringGroup(val endQuoteCol: Int, val nodes: List<FormatNode>) : FormatNode data class MultilineStringGroup(val endQuoteCol: Int, val nodes: List<FormatNode>) : FormatNode
data class IfWrap(val id: Int, val ifWrap: FormatNode, val ifNotWrap: FormatNode) : FormatNode data class IfWrap(val id: Int, val ifWrap: FormatNode, val ifNotWrap: FormatNode) : FormatNode
@@ -0,0 +1,7 @@
foo1 = "some string"
foo2 = "some string with \( new { x = 1; y = 2 }) interpolation"
foo3 = "some reeeeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaally long string with \( new { x = 1; y = 2 } ) interpolation"
foo4 = "some reeeeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaally long string with \( foo.bar.baz() ) qualified access"
@@ -0,0 +1,51 @@
prop1 =
"""
asd \(new { bar = 1 }) asd
"""
prop2 =
"""
\(let (bar = 15) bar + new { qux = 15 }.toString())
"""
prop3 =
"""
\(new {
// some comment
foo = 1
// some comment
bar = 2
})
"""
prop4 =
"""
\(1 + /* block comment */ 2)
"""
prop5 =
"""
\("""
foo
bar
baz
""")
"""
prop6 = "\(// some line comment
/* some block comment */
"\("""
one
two
three
""")"
// some line comment again
)"
prop7 = "\(
5
// trailing line comment
)"
prop8 = "\(new { foo = 1 bar = 2 baz = 3 })"
@@ -1,8 +1,6 @@
foo = foo =
""" """
asd \(new { asd \(new { bar = 1 }) asd
bar = 1
}) asd
""" """
bar = bar =
@@ -0,0 +1,9 @@
foo1 = "some string"
foo2 = "some string with \(new { x = 1; y = 2 }) interpolation"
foo3 =
"some reeeeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaally long string with \(new { x = 1; y = 2 }) interpolation"
foo4 =
"some reeeeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaally long string with \(foo.bar.baz()) qualified access"
@@ -0,0 +1,52 @@
prop1 =
"""
asd \(new { bar = 1 }) asd
"""
prop2 =
"""
\(let (bar = 15) bar + new { qux = 15 }.toString())
"""
prop3 =
"""
\(new {
// some comment
foo = 1
// some comment
bar = 2
})
"""
prop4 =
"""
\(1 + /* block comment */ 2)
"""
prop5 =
"""
\("""
foo
bar
baz
""")
"""
prop6 =
"\( // some line comment
/* some block comment */
"\("""
one
two
three
""")"
// some line comment again
)"
prop7 =
"\(5
// trailing line comment
)"
prop8 = "\(new { foo = 1; bar = 2; baz = 3 })"
@@ -965,7 +965,7 @@ public class GenericParser {
case STRING_PART -> { case STRING_PART -> {
var tk = next(); var tk = next();
if (!tk.text(lexer).isEmpty()) { if (!tk.text(lexer).isEmpty()) {
children.add(make(NodeType.STRING_CONSTANT, tk.span)); children.add(make(NodeType.STRING_CHARS, tk.span));
} }
} }
case STRING_ESCAPE_NEWLINE, case STRING_ESCAPE_NEWLINE,
@@ -1004,7 +1004,7 @@ public class GenericParser {
case STRING_PART -> { case STRING_PART -> {
var tk = next(); var tk = next();
if (!tk.text(lexer).isEmpty()) { if (!tk.text(lexer).isEmpty()) {
children.add(make(NodeType.STRING_CONSTANT, tk.span)); children.add(make(NodeType.STRING_CHARS, tk.span));
} }
} }
case STRING_NEWLINE -> children.add(make(NodeType.STRING_NEWLINE, next().span)); case STRING_NEWLINE -> children.add(make(NodeType.STRING_NEWLINE, next().span));
@@ -1383,7 +1383,7 @@ public class GenericParser {
} }
} }
children.add(makeTerminal(next())); // string end children.add(makeTerminal(next())); // string end
return new Node(NodeType.STRING_CONSTANT, children); return new Node(NodeType.STRING_CHARS, children);
} }
private FullToken expect(Token type, String errorKey, Object... messageArgs) { private FullToken expect(Token type, String errorKey, Object... messageArgs) {
@@ -67,7 +67,7 @@ public enum NodeType {
TYPE_ARGUMENT_LIST_ELEMENTS, TYPE_ARGUMENT_LIST_ELEMENTS,
OBJECT_PARAMETER_LIST, OBJECT_PARAMETER_LIST,
TYPE_PARAMETER, TYPE_PARAMETER,
STRING_CONSTANT, STRING_CHARS,
OPERATOR, OPERATOR,
STRING_NEWLINE, STRING_NEWLINE,
STRING_ESCAPE, STRING_ESCAPE,
@@ -130,7 +130,7 @@ class GenericSexpRenderer(code: String) {
} }
private fun NodeType.isStringData(): Boolean = private fun NodeType.isStringData(): Boolean =
this == NodeType.STRING_CONSTANT || this == NodeType.STRING_ESCAPE this == NodeType.STRING_CHARS || this == NodeType.STRING_ESCAPE
private fun name(node: Node): String = private fun name(node: Node): String =
when (node.type) { when (node.type) {
@@ -142,7 +142,7 @@ class GenericSexpRenderer(code: String) {
NodeType.EXTENDS_CLAUSE, NodeType.EXTENDS_CLAUSE,
NodeType.AMENDS_CLAUSE -> "extendsOrAmendsClause" NodeType.AMENDS_CLAUSE -> "extendsOrAmendsClause"
NodeType.TYPEALIAS -> "typeAlias" NodeType.TYPEALIAS -> "typeAlias"
NodeType.STRING_ESCAPE -> "stringConstant" NodeType.STRING_ESCAPE -> "stringChars"
NodeType.READ_EXPR -> { NodeType.READ_EXPR -> {
val terminal = node.children.find { it.type == NodeType.TERMINAL }!!.text(source) val terminal = node.children.find { it.type == NodeType.TERMINAL }!!.text(source)
when (terminal) { when (terminal) {
@@ -430,7 +430,7 @@ class SexpRenderer {
renderExpr(part.expr) renderExpr(part.expr)
} else { } else {
buf.append('\n').append(tab) buf.append('\n').append(tab)
buf.append("(stringConstant)") buf.append("(stringChars)")
} }
} }
buf.append(')') buf.append(')')
@@ -736,7 +736,7 @@ class SexpRenderer {
fun renderStringConstant(str: StringConstant) { fun renderStringConstant(str: StringConstant) {
buf.append(tab) buf.append(tab)
buf.append("(stringConstant)") buf.append("(stringChars)")
} }
fun renderTypeAnnotation(typeAnnotation: TypeAnnotation) { fun renderTypeAnnotation(typeAnnotation: TypeAnnotation) {