Skip to content

Commit 4b9e889

Browse files
authored
Merge pull request #43 from projectfluent/fix-special-chars-start-inline
Don't drop to newline when serializing patterns starting with special chars
2 parents 97a0eea + bee1715 commit 4b9e889

File tree

4 files changed

+167
-8
lines changed

4 files changed

+167
-8
lines changed

fluent.syntax/src/main/kotlin/org/projectfluent/syntax/serializer/Serializer.kt

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,33 @@ package org.projectfluent.syntax.serializer
22

33
import org.projectfluent.syntax.ast.* // ktlint-disable no-wildcard-imports
44

5-
private fun indent(content: CharSequence) = content.split("\n").joinToString("\n ")
5+
private fun indentExceptFirstLine(content: CharSequence) =
6+
content.split("\n").joinToString("\n ")
7+
8+
private fun PatternElement.includesLine() =
9+
this is TextElement && value.contains("\n")
10+
11+
private fun PatternElement.isSelectExpr() =
12+
this is Placeable && expression is SelectExpression
13+
14+
private fun Pattern.shouldStartOnNewLine(): Boolean {
15+
val isMultiline = this.elements.any { it.isSelectExpr() || it.includesLine() }
16+
if (isMultiline) {
17+
val firstElement = this.elements.elementAtOrNull(0)
18+
if (firstElement is TextElement) {
19+
val firstChar = firstElement.value.elementAtOrNull(0)
20+
// Due to the indentation requirement the following characters may not appear
21+
// as the first character on a new line.
22+
if (firstChar == '[' || firstChar == '.' || firstChar == '*') {
23+
return false
24+
}
25+
}
626

7-
private fun PatternElement.includesLine() = this is TextElement && value.contains("\n")
27+
return true
28+
}
829

9-
private fun PatternElement.isSelectExpr() = this is Placeable && expression is SelectExpression
30+
return false
31+
}
1032

1133
/**
1234
* Serialize Fluent nodes to `CharSequence`.
@@ -117,16 +139,15 @@ class FluentSerializer(private val withJunk: Boolean = false) {
117139
}
118140

119141
private fun serializeAttribute(attribute: Attribute): CharSequence {
120-
val value = indent(serializePattern(attribute.value))
142+
val value = indentExceptFirstLine(serializePattern(attribute.value))
121143
return "\n .${attribute.id.name} =$value"
122144
}
123145

124146
private fun serializePattern(pattern: Pattern): CharSequence {
125-
val startOnLine = pattern.elements.any { it.isSelectExpr() || it.includesLine() }
126147
val elements = pattern.elements.map(::serializeElement)
127-
val content = indent(elements.joinToString(""))
148+
val content = indentExceptFirstLine(elements.joinToString(""))
128149

129-
return if (startOnLine) {
150+
return if (pattern.shouldStartOnNewLine()) {
130151
"\n $content"
131152
} else {
132153
" $content"
@@ -187,7 +208,7 @@ class FluentSerializer(private val withJunk: Boolean = false) {
187208

188209
private fun serializeVariant(variant: Variant): CharSequence {
189210
val key = serializeVariantKey(variant.key)
190-
val value = indent(serializePattern(variant.value))
211+
val value = indentExceptFirstLine(serializePattern(variant.value))
191212

192213
return if (variant.default) {
193214
"\n *[$key]$value"

fluent.syntax/src/test/kotlin/org/projectfluent/syntax/serializer/SerializeResourceTest.kt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,35 @@ class SerializeResourceTest {
202202
assertEquals(input, this.pretty(input))
203203
}
204204

205+
@Test
206+
fun multiline_starting_inline() {
207+
val input =
208+
"""
209+
foo = Foo
210+
Baz
211+
212+
""".trimIndent()
213+
val output =
214+
"""
215+
foo =
216+
Foo
217+
Baz
218+
219+
""".trimIndent()
220+
assertEquals(output, this.pretty(input))
221+
}
222+
223+
@Test
224+
fun multiline_starting_inline_with_a_special_char() {
225+
val input =
226+
"""
227+
foo = *Foo
228+
Baz
229+
230+
""".trimIndent()
231+
assertEquals(input, this.pretty(input))
232+
}
233+
205234
@Test
206235
fun multiline_with_placeable() {
207236
val input =
@@ -378,6 +407,19 @@ class SerializeResourceTest {
378407
assertEquals(expected, this.pretty(input))
379408
}
380409

410+
@Test
411+
fun select_expression_in_inline_pattern_starting_with_a_special_char() {
412+
val input =
413+
"""
414+
foo = .Foo { ${'$'}sel ->
415+
*[a] A
416+
[b] B
417+
}
418+
419+
""".trimIndent()
420+
assertEquals(input, this.pretty(input))
421+
}
422+
381423
@Test
382424
fun select_expression_in_multiline_pattern() {
383425
val input =
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
## OK
2+
3+
bracket-inline = [Value]
4+
dot-inline = .Value
5+
star-inline = *Value
6+
7+
## ERRORS
8+
9+
bracket-newline =
10+
[Value]
11+
dot-newline =
12+
.Value
13+
star-newline =
14+
*Value
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
{
2+
"type": "Resource",
3+
"body": [
4+
{
5+
"type": "GroupComment",
6+
"content": "OK"
7+
},
8+
{
9+
"type": "Message",
10+
"id": {
11+
"type": "Identifier",
12+
"name": "bracket-inline"
13+
},
14+
"value": {
15+
"type": "Pattern",
16+
"elements": [
17+
{
18+
"type": "TextElement",
19+
"value": "[Value]"
20+
}
21+
]
22+
},
23+
"attributes": [],
24+
"comment": null
25+
},
26+
{
27+
"type": "Message",
28+
"id": {
29+
"type": "Identifier",
30+
"name": "dot-inline"
31+
},
32+
"value": {
33+
"type": "Pattern",
34+
"elements": [
35+
{
36+
"type": "TextElement",
37+
"value": ".Value"
38+
}
39+
]
40+
},
41+
"attributes": [],
42+
"comment": null
43+
},
44+
{
45+
"type": "Message",
46+
"id": {
47+
"type": "Identifier",
48+
"name": "star-inline"
49+
},
50+
"value": {
51+
"type": "Pattern",
52+
"elements": [
53+
{
54+
"type": "TextElement",
55+
"value": "*Value"
56+
}
57+
]
58+
},
59+
"attributes": [],
60+
"comment": null
61+
},
62+
{
63+
"type": "GroupComment",
64+
"content": "ERRORS"
65+
},
66+
{
67+
"type": "Junk",
68+
"annotations": [],
69+
"content": "bracket-newline =\n [Value]\n"
70+
},
71+
{
72+
"type": "Junk",
73+
"annotations": [],
74+
"content": "dot-newline =\n .Value\n"
75+
},
76+
{
77+
"type": "Junk",
78+
"annotations": [],
79+
"content": "star-newline =\n *Value\n"
80+
}
81+
]
82+
}

0 commit comments

Comments
 (0)