Skip to content

Commit c17157e

Browse files
milesziemerSteven Yuan
authored and
Steven Yuan
committed
Fix indentation when formatting text blocks (smithy-lang#1875)
Updates the formatter to indent the node value of a node kvp when they are text blocks. A test case was added for a text block as the node value of a node kvp, and a test case was updated for when text blocks are in traits.
1 parent fe33cbb commit c17157e

File tree

5 files changed

+103
-3
lines changed

5 files changed

+103
-3
lines changed

smithy-syntax/src/main/java/software/amazon/smithy/syntax/Formatter.java

+27-3
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717

1818
import com.opencastsoftware.prettier4j.Doc;
1919
import java.util.ArrayList;
20+
import java.util.Arrays;
2021
import java.util.Iterator;
2122
import java.util.List;
2223
import java.util.Optional;
2324
import java.util.function.Function;
25+
import java.util.stream.Collectors;
2426
import java.util.stream.Stream;
2527
import software.amazon.smithy.model.loader.ModelSyntaxException;
2628
import software.amazon.smithy.model.shapes.ShapeId;
@@ -178,6 +180,7 @@ private Doc visit(TreeCursor cursor) {
178180
case APPLY_STATEMENT:
179181
case NODE_VALUE:
180182
case NODE_KEYWORD:
183+
case NODE_STRING_VALUE:
181184
case SIMPLE_TYPE_NAME:
182185
case ENUM_TYPE_NAME:
183186
case AGGREGATE_TYPE_NAME:
@@ -408,12 +411,25 @@ private Doc visit(TreeCursor cursor) {
408411
}
409412

410413
case NODE_OBJECT_KVP: {
414+
// Since text blocks span multiple lines, when they are the NODE_VALUE for NODE_OBJECT_KVP,
415+
// they have to be indented. Since we only format valid models, NODE_OBJECT_KVP is guaranteed to
416+
// have a NODE_VALUE child.
417+
TreeCursor nodeValue = cursor.getFirstChild(TreeType.NODE_VALUE);
418+
boolean isTextBlock = Optional.ofNullable(nodeValue.getFirstChild(TreeType.NODE_STRING_VALUE))
419+
.map(nodeString -> nodeString.getFirstChild(TreeType.TEXT_BLOCK))
420+
.isPresent();
421+
Doc nodeValueDoc = visit(nodeValue);
422+
if (isTextBlock) {
423+
nodeValueDoc = nodeValueDoc.indent(4);
424+
}
425+
426+
411427
// Hoist awkward comments in the KVP *before* the KVP rather than between the values and colon.
412428
// If there is an awkward comment before the TRAIT value, hoist it above the statement.
413429
return skippedComments(cursor, false)
414430
.append(visit(cursor.getFirstChild(TreeType.NODE_OBJECT_KEY)))
415431
.append(Doc.text(": "))
416-
.append(visit(cursor.getFirstChild(TreeType.NODE_VALUE)));
432+
.append(nodeValueDoc);
417433
}
418434

419435
case NODE_OBJECT_KEY: {
@@ -427,9 +443,17 @@ private Doc visit(TreeCursor cursor) {
427443
: Doc.text(tree.concatTokens());
428444
}
429445

446+
case TEXT_BLOCK: {
447+
// Dispersing the lines of the text block preserves any indentation applied from formatting parent
448+
// nodes.
449+
List<Doc> lines = Arrays.stream(tree.concatTokens().split(System.lineSeparator()))
450+
.map(String::trim)
451+
.map(Doc::text)
452+
.collect(Collectors.toList());
453+
return Doc.intersperse(Doc.line(), lines);
454+
}
455+
430456
case TOKEN:
431-
case TEXT_BLOCK:
432-
case NODE_STRING_VALUE:
433457
case QUOTED_TEXT:
434458
case NUMBER:
435459
case SHAPE_ID:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
$version: "2.0"
2+
3+
metadata validators = [
4+
{
5+
name: "EmitEachSelector"
6+
id: "MissingStringInputLengthValidation"
7+
severity: "DANGER"
8+
message: "This string is missing required length trait"
9+
configuration: {
10+
selector: """
11+
operation -[input]-> structure > member
12+
:test(member > string:not([trait|enum]))
13+
:test(member > string:not([trait|length]))
14+
:test(member > string:not([trait|aws.api#arnReference]))
15+
:test(member > string:not([trait|aws.api#providesPassRole]))
16+
"""
17+
}
18+
}
19+
]
20+
21+
namespace smithy.example
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
$version: "2.0"
2+
3+
metadata validators = [
4+
{
5+
name: "EmitEachSelector"
6+
id: "MissingStringInputLengthValidation"
7+
severity: "DANGER"
8+
message: "This string is missing required length trait"
9+
configuration: {
10+
selector: """
11+
operation -[input]-> structure > member
12+
:test(member > string:not([trait|enum]))
13+
:test(member > string:not([trait|length]))
14+
:test(member > string:not([trait|aws.api#arnReference]))
15+
:test(member > string:not([trait|aws.api#providesPassRole]))
16+
"""
17+
}
18+
}
19+
]
20+
21+
namespace smithy.example
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
$version: "2.0"
2+
3+
namespace smithy.example
4+
5+
@documentation(
6+
"""
7+
This is the documentation for Foo.
8+
Lorem ipsum dolor."""
9+
)
10+
string Foo
11+
12+
@documentation(
13+
"""
14+
This is the documentation for Bar.
15+
Lorem ipsum dolor.
16+
"""
17+
)
18+
string Bar
19+
20+
@documentation(
21+
"""
22+
This is the documentation for Baz.
23+
Lorem ipsum dolor.
24+
"""
25+
)
26+
string Baz

smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/trait-textblock.smithy

+8
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,11 @@ string Foo
1616
"""
1717
)
1818
string Bar
19+
20+
@documentation(
21+
"""
22+
This is the documentation for Baz.
23+
Lorem ipsum dolor.
24+
"""
25+
)
26+
string Baz

0 commit comments

Comments
 (0)