Skip to content

Commit c3e2b30

Browse files
respencer-nclclaude
andcommitted
Fix PrettifyPass formatting: indent, newlines, commas, schema
Fix four systemic formatting issues in PrettifyPass output: - emitMetaData closing brace now uses addIndent for proper indent - closeDef/closeUseCase/closeFunction/doAuthor emit trailing newlines - emitFields uses zipWithIndex for correct comma placement with metadata - doSchema adds proper indentation for of/index/link clauses Three new regression tests added to RiddlTest. All 273 passes tests and 238 commands tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 26079c1 commit c3e2b30

4 files changed

Lines changed: 147 additions & 16 deletions

File tree

NOTEBOOK.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,33 @@ This is the central engineering notebook for the RIDDL project. It tracks curren
66

77
## Current Status
88

9-
**Last Updated**: February 16, 2026 (evening)
9+
**Last Updated**: February 17, 2026
10+
11+
### PrettifyPass Formatting Fixes (Feb 17, 2026)
12+
13+
Fixed four systemic formatting issues in PrettifyPass output:
14+
15+
1. **Closing `}` at wrong indent**`emitMetaData()` used
16+
`add("}")` instead of `addIndent("}")`, placing with-block
17+
closing braces at column 0. Fixed in `RiddlFileEmitter.scala`.
18+
19+
2. **Missing newlines between siblings**`closeDef()` emitted
20+
no newline when metadata was empty, causing consecutive
21+
definitions to run together. Fixed `closeDef`, `closeUseCase`,
22+
`closeFunction`, and `doAuthor` to always end with newline.
23+
24+
3. **Comma placement with field metadata**`emitFields()` used
25+
`foldLeft` + `deleteCharAt` which broke when fields had
26+
`with` blocks. Replaced with `zipWithIndex` approach that
27+
inserts commas correctly relative to metadata blocks.
28+
29+
4. **Schema indentation**`doSchema()` had no indentation for
30+
`of`/`index`/`link` clauses. Added two indent levels: `of`
31+
clauses one level deeper than `schema`, `index`/`link` two
32+
levels deeper.
33+
34+
All 273 passes tests + 238 commands tests pass. Three new
35+
regression tests added to `RiddlTest.scala`.
1036

1137
**Scala Version**: 3.7.4 (overrides sbt-ossuminc's 3.3.7 LTS
1238
default due to compiler infinite loop bug with opaque

passes/jvm-native/src/test/scala/com/ossuminc/riddl/passes/RiddlTest.scala

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,88 @@ class RiddlTest extends AbstractTestingBasis {
8585
Timer.time("toRiddlText", true) {
8686
Riddl.toRiddlText(root)
8787
}
88-
converted must be(input)
88+
converted.stripTrailing() must be(input)
89+
}
90+
91+
"indent closing braces correctly" in {
92+
val input =
93+
"""domain foo is {
94+
| context bar is {
95+
| handler baz is { ??? }
96+
| }
97+
|}""".stripMargin
98+
val rpi = RiddlParserInput(input, "")
99+
Riddl.parse(rpi) match
100+
case Left(messages) => fail(messages.format)
101+
case Right(root) =>
102+
val output = Riddl.toRiddlText(root)
103+
val lines = output.split("\n")
104+
// context's closing } should be indented 2 spaces
105+
lines must contain(" }")
106+
// domain's closing } should be at column 0
107+
lines.last.trim must be("}")
108+
lines.last.indexOf("}") must be(0)
109+
}
110+
111+
"emit newlines between sibling on clauses" in {
112+
val input =
113+
"""domain foo is {
114+
| context bar is {
115+
| entity baz is {
116+
| handler main is {
117+
| on command com.DoA {
118+
| tell command com.DoA to entity baz
119+
| }
120+
| on command com.DoB {
121+
| tell command com.DoB to entity baz
122+
| }
123+
| }
124+
| }
125+
| }
126+
|}""".stripMargin
127+
val rpi = RiddlParserInput(input, "")
128+
Riddl.parse(rpi) match
129+
case Left(messages) => fail(messages.format)
130+
case Right(root) =>
131+
val output = Riddl.toRiddlText(root)
132+
// Each on clause should start on its own line
133+
val onLines = output.split("\n").filter(_.trim.startsWith("on "))
134+
onLines.length must be(2)
135+
// No two closing braces should be on the same line (no run-together)
136+
output must not include "}on"
137+
output must not include "} on"
138+
}
139+
140+
"indent schema of/index clauses correctly" in {
141+
val input =
142+
"""domain foo is {
143+
| context bar is {
144+
| repository baz is {
145+
| schema Stuff is relational
146+
| of things as type foo.bar.Thing
147+
| index on field foo.bar.Thing.id
148+
| }
149+
| }
150+
|}""".stripMargin
151+
val rpi = RiddlParserInput(input, "")
152+
Riddl.parse(rpi) match
153+
case Left(messages) => fail(messages.format)
154+
case Right(root) =>
155+
val output = Riddl.toRiddlText(root)
156+
val lines = output.split("\n")
157+
val schemaLine = lines.find(_.trim.startsWith("schema "))
158+
val ofLine = lines.find(_.trim.startsWith("of "))
159+
val indexLine = lines.find(_.trim.startsWith("index "))
160+
schemaLine mustBe defined
161+
ofLine mustBe defined
162+
indexLine mustBe defined
163+
// of should be indented more than schema
164+
val schemaIndent = schemaLine.get.indexOf("schema")
165+
val ofIndent = ofLine.get.indexOf("of")
166+
val indexIndent = indexLine.get.indexOf("index")
167+
ofIndent must be > schemaIndent
168+
// index should be indented more than of
169+
indexIndent must be > ofIndent
89170
}
90171

91172
}

passes/shared/src/main/scala/com/ossuminc/riddl/passes/prettify/PrettifyVisitor.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ class PrettifyVisitor(options: PrettifyPass.Options)(using PlatformContext) exte
8383
state.withCurrent { (rfe: RiddlFileEmitter) =>
8484
rfe.decr.addIndent("}")
8585
rfe.emitMetaData(useCase.metadata)
86+
if useCase.metadata.isEmpty then rfe.nl
8687
}
8788
end closeUseCase
8889

@@ -96,6 +97,8 @@ class PrettifyVisitor(options: PrettifyPass.Options)(using PlatformContext) exte
9697
def closeFunction(function: Function, parents: Parents): Unit =
9798
state.withCurrent { rfe =>
9899
rfe.decr.addIndent("}")
100+
rfe.emitMetaData(function.metadata)
101+
if function.metadata.isEmpty then rfe.nl
99102
}
100103
end closeFunction
101104

@@ -195,6 +198,7 @@ class PrettifyVisitor(options: PrettifyPass.Options)(using PlatformContext) exte
195198
author.title.map(title => rfe.addIndent(s"title = ${title.format}\n"))
196199
rfe.decr.addIndent("}")
197200
rfe.emitMetaData(author.metadata)
201+
if author.metadata.isEmpty then rfe.nl
198202
}
199203
end doAuthor
200204

@@ -268,9 +272,11 @@ class PrettifyVisitor(options: PrettifyPass.Options)(using PlatformContext) exte
268272
.add(" is ")
269273
.emitSchemaKind(schema.schemaKind)
270274
.nl
275+
rfe.incr
271276
schema.data.toSeq.sortBy(_._1.value).foreach { (id: Identifier, typeRef: TypeRef) =>
272277
rfe.addIndent("of ").add(id.format).add(" as ").add(typeRef.format).nl
273278
}
279+
rfe.incr
274280
schema.links.toSeq.sortBy(_._1.value).foreach { (id: Identifier, tr: (FieldRef, FieldRef)) =>
275281
rfe
276282
.addIndent("link ")
@@ -284,6 +290,9 @@ class PrettifyVisitor(options: PrettifyPass.Options)(using PlatformContext) exte
284290
schema.indices.foreach { fieldRef =>
285291
rfe.addIndent("index on ").add(fieldRef.format).nl
286292
}
293+
rfe.decr.decr
294+
rfe.emitMetaData(schema.metadata)
295+
if schema.metadata.isEmpty then rfe.nl
287296
}
288297
end doSchema
289298

passes/shared/src/main/scala/com/ossuminc/riddl/passes/prettify/RiddlFileEmitter.scala

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,12 @@ case class RiddlFileEmitter(url: URL)(using PlatformContext) extends FileBuilder
4848
def closeDef(
4949
definition: Definition
5050
): this.type = {
51-
if definition.nonEmpty then decr.addIndent("}")
52-
emitMetaData(definition.metadata)
51+
if definition.nonEmpty then
52+
decr.addIndent("}")
53+
emitMetaData(definition.metadata)
54+
if definition.metadata.isEmpty then nl
55+
end if
56+
this
5357
}
5458

5559
def emitMetaData(meta: Contents[MetaData]): this.type =
@@ -66,7 +70,7 @@ case class RiddlFileEmitter(url: URL)(using PlatformContext) extends FileBuilder
6670
case fa: FileAttachment => emitFileAttachment(fa)
6771
case ua: ULIDAttachment => emitULIDAttachment(ua)
6872
}
69-
decr.add("}").nl
73+
decr.addIndent("}").nl
7074
end if
7175
this
7276
end emitMetaData
@@ -185,20 +189,31 @@ case class RiddlFileEmitter(url: URL)(using PlatformContext) extends FileBuilder
185189
of.headOption match {
186190
case None => this.add("{ ??? }")
187191
case Some(field) if of.size == 1 =>
188-
add(s"{ ")
189-
.emitField(field)
190-
.add(" }")
191-
.nl
192+
if field.metadata.nonEmpty then
193+
add("{").nl.incr
194+
add(spc).emitField(field)
195+
decr.addIndent("}").nl
196+
else
197+
add(s"{ ")
198+
.emitField(field)
199+
.add(" }")
200+
.nl
192201
case Some(_) =>
193202
this.add("{").nl.incr
194-
of.foldLeft(this) { case (s, f) =>
195-
s.add(spc)
196-
.emitField(f)
197-
.add(",")
198-
.nl
203+
val lastIdx = of.size - 1
204+
of.zipWithIndex.foreach { case (f, idx) =>
205+
add(spc).emitField(f)
206+
if idx < lastIdx then
207+
if f.metadata.nonEmpty then
208+
// metadata ended with }\n; insert comma before trailing newline
209+
sb.insert(sb.length - new_line.length, ",")
210+
else
211+
add(",").nl
212+
else
213+
// last field — no comma
214+
if f.metadata.isEmpty then nl
199215
}
200-
sb.deleteCharAt(sb.length - 2)
201-
decr.add(s"$spc} ")
216+
decr.addIndent("}").nl
202217
}
203218
this
204219
}

0 commit comments

Comments
 (0)