diff --git a/config/spotbugs/filter.xml b/config/spotbugs/filter.xml
index 746e037d118..24d313006d0 100644
--- a/config/spotbugs/filter.xml
+++ b/config/spotbugs/filter.xml
@@ -154,6 +154,10 @@
+
+
+
+
diff --git a/smithy-cli/src/it/java/software/amazon/smithy/cli/DiffCommandTest.java b/smithy-cli/src/it/java/software/amazon/smithy/cli/DiffCommandTest.java
new file mode 100644
index 00000000000..15106bf0997
--- /dev/null
+++ b/smithy-cli/src/it/java/software/amazon/smithy/cli/DiffCommandTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.smithy.cli;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.UncheckedIOException;
+import java.nio.file.Path;
+import org.junit.jupiter.api.Test;
+import software.amazon.smithy.utils.ListUtils;
+
+public class DiffCommandTest {
+ @Test
+ public void passingDiffEventsExitZero() {
+ IntegUtils.withTempDir("diff", dir -> {
+ Path a = dir.resolve("a.smithy");
+ writeFile(a, "$version: \"2.0\"\nnamespace example\nstring A\n");
+
+ RunResult result = IntegUtils.run(dir, ListUtils.of("diff", "--old", a.toString(), "--new", a.toString()));
+ assertThat(result.getExitCode(), equalTo(0));
+ });
+ }
+
+ @Test
+ public void showsLabelForOldModelEvents() {
+ IntegUtils.withTempDir("diff", dir -> {
+ Path a = dir.resolve("a.smithy");
+ writeFile(a, "$version: \"2.0\"\nnamespace example\n@aaaaaa\nstring A\n");
+
+ RunResult result = IntegUtils.run(dir, ListUtils.of("diff", "--old", a.toString(), "--new", a.toString()));
+ assertThat(result.getExitCode(), equalTo(1));
+ assertThat(result.getOutput(), containsString("── OLD ERROR ──"));
+ });
+ }
+
+ @Test
+ public void showsLabelForNewModelEvents() {
+ IntegUtils.withTempDir("diff", dir -> {
+ Path a = dir.resolve("a.smithy");
+ writeFile(a, "$version: \"2.0\"\nnamespace example\nstring A\n");
+
+ Path b = dir.resolve("b.smithy");
+ writeFile(b, "$version: \"2.0\"\nnamespace example\n@aaaaaa\nstring A\n");
+
+ RunResult result = IntegUtils.run(dir, ListUtils.of("diff", "--old", a.toString(), "--new", b.toString()));
+ assertThat(result.getExitCode(), equalTo(1));
+ assertThat(result.getOutput(), containsString("── NEW ERROR ──"));
+ });
+ }
+
+ @Test
+ public void showsLabelForDiffEvents() {
+ IntegUtils.withTempDir("diff", dir -> {
+ Path a = dir.resolve("a.smithy");
+ writeFile(a, "$version: \"2.0\"\nnamespace example\nstring A\n");
+
+ Path b = dir.resolve("b.smithy");
+ writeFile(b, "$version: \"2.0\"\nnamespace example\nstring A\nstring B\n"); // Added B.
+
+ RunResult result = IntegUtils.run(dir, ListUtils.of(
+ "diff",
+ "--old", a.toString(),
+ "--new", b.toString(),
+ "--severity", "NOTE")); // Note that this is required since the default severity is WARNING.
+ assertThat(result.getExitCode(), equalTo(0));
+ assertThat(result.getOutput(), containsString("── DIFF NOTE ──"));
+ });
+ }
+
+ private void writeFile(Path path, String contents) {
+ try {
+ FileWriter fileWriter = new FileWriter(path.toString());
+ PrintWriter printWriter = new PrintWriter(fileWriter);
+ printWriter.print(contents);
+ printWriter.close();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+}
diff --git a/smithy-cli/src/it/java/software/amazon/smithy/cli/IntegUtils.java b/smithy-cli/src/it/java/software/amazon/smithy/cli/IntegUtils.java
index c68eb4ead78..b37b73b329e 100644
--- a/smithy-cli/src/it/java/software/amazon/smithy/cli/IntegUtils.java
+++ b/smithy-cli/src/it/java/software/amazon/smithy/cli/IntegUtils.java
@@ -91,7 +91,7 @@ private static List createSmithyCommand(List args) {
throw new RuntimeException("No SMITHY_BINARY location was set. Did you build the Smithy jlink CLI?");
}
- private static void withTempDir(String name, Consumer consumer) {
+ static void withTempDir(String name, Consumer consumer) {
try {
Path path = Files.createTempDirectory(name.replace("/", "_"));
try {
diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/Cli.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/Cli.java
index 111086cdd45..99113319a0c 100644
--- a/smithy-cli/src/main/java/software/amazon/smithy/cli/Cli.java
+++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/Cli.java
@@ -81,6 +81,8 @@ public int run(String[] args) {
Command.Env env = new Command.Env(colorFormatter, stdoutPrinter, stderrPrinter, classLoader);
return command.execute(arguments, env);
} catch (Exception e) {
+ stdoutPrinter.flush();
+ stderrPrinter.flush();
printException(e, standardOptions.stackTrace());
throw CliError.wrap(e);
} finally {
@@ -112,13 +114,13 @@ public void stderr(CliPrinter stderrPrinter) {
private void printException(Throwable e, boolean stacktrace) {
try (ColorBuffer buffer = ColorBuffer.of(colorFormatter, stderrPrinter)) {
if (!stacktrace) {
- colorFormatter.println(stderrPrinter, e.getMessage(), Style.RED);
+ colorFormatter.println(stderrPrinter, e.getMessage(), ColorTheme.ERROR);
} else {
StringWriter writer = new StringWriter();
e.printStackTrace(new PrintWriter(writer));
String result = writer.toString();
int positionOfName = result.indexOf(':');
- buffer.print(result.substring(0, positionOfName), Style.RED, Style.UNDERLINE);
+ buffer.print(result.substring(0, positionOfName), ColorTheme.ERROR);
buffer.println(result.substring(positionOfName));
}
}
diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/CliPrinter.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/CliPrinter.java
index 35c49c7e8f3..185fcdd4670 100644
--- a/smithy-cli/src/main/java/software/amazon/smithy/cli/CliPrinter.java
+++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/CliPrinter.java
@@ -26,7 +26,6 @@
/**
* Handles text output of the CLI.
*/
-@FunctionalInterface
public interface CliPrinter extends Appendable, Flushable {
@Override
@@ -38,12 +37,7 @@ default CliPrinter append(CharSequence csq) {
}
@Override
- default CliPrinter append(CharSequence csq, int start, int end) {
- for (int i = start; i < end; i++) {
- append(csq.charAt(i));
- }
- return this;
- }
+ CliPrinter append(CharSequence csq, int start, int end);
/**
* Prints text to the writer and appends a new line.
diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/ColorBuffer.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/ColorBuffer.java
index 7cd0a9a06f2..587a2adb822 100644
--- a/smithy-cli/src/main/java/software/amazon/smithy/cli/ColorBuffer.java
+++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/ColorBuffer.java
@@ -64,9 +64,7 @@ public static ColorBuffer of(ColorFormatter colors, Appendable sink) {
*/
public static ColorBuffer of(ColorFormatter colors, CliPrinter sink) {
StringBuilder buffer = new StringBuilder();
- return new ColorBuffer(colors, buffer, s -> {
- sink.append(s.toString());
- });
+ return new ColorBuffer(colors, buffer, s -> sink.append(s.toString()));
}
@Override
diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/ColorTheme.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/ColorTheme.java
new file mode 100644
index 00000000000..0e9e6e33f32
--- /dev/null
+++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/ColorTheme.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.smithy.cli;
+
+/**
+ * Standardizes on colors across commands.
+ */
+public final class ColorTheme {
+
+ public static final Style EM_UNDERLINE = Style.of(Style.BRIGHT_WHITE, Style.UNDERLINE);
+ public static final Style DEPRECATED = Style.of(Style.BG_YELLOW, Style.BLACK);
+
+ public static final Style MUTED = Style.of(Style.BRIGHT_BLACK);
+ public static final Style EVENT_SHAPE_ID = Style.of(Style.BRIGHT_MAGENTA);
+ public static final Style LITERAL = Style.of(Style.CYAN);
+
+ public static final Style ERROR_TITLE = Style.of(Style.BG_RED, Style.BLACK);
+ public static final Style ERROR = Style.of(Style.RED);
+
+ public static final Style DANGER_TITLE = Style.of(Style.BG_MAGENTA, Style.BLACK);
+ public static final Style DANGER = Style.of(Style.MAGENTA);
+
+ public static final Style WARNING_TITLE = Style.of(Style.BG_YELLOW, Style.BLACK);
+ public static final Style WARNING = Style.of(Style.YELLOW);
+
+ public static final Style NOTE_TITLE = Style.of(Style.BG_CYAN, Style.BLACK);
+ public static final Style NOTE = Style.of(Style.CYAN);
+
+ public static final Style SUPPRESSED_TITLE = Style.of(Style.BG_GREEN, Style.BLACK);
+ public static final Style SUPPRESSED = Style.of(Style.GREEN);
+
+ public static final Style SUCCESS = Style.of(Style.GREEN);
+
+ public static final Style DIFF_TITLE = Style.of(Style.BG_BRIGHT_BLACK, Style.WHITE);
+ public static final Style DIFF_EVENT_TITLE = Style.of(Style.BG_BRIGHT_BLUE, Style.BLACK);
+
+ private ColorTheme() {}
+}
diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/Command.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/Command.java
index 81b5352db3c..1bd802ff826 100644
--- a/smithy-cli/src/main/java/software/amazon/smithy/cli/Command.java
+++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/Command.java
@@ -86,6 +86,11 @@ public ClassLoader classLoader() {
return classLoader == null ? getClass().getClassLoader() : classLoader;
}
+ public void flush() {
+ stderr.flush();
+ stdout.flush();
+ }
+
public Env withClassLoader(ClassLoader classLoader) {
return classLoader == this.classLoader ? this : new Env(colors, stdout, stderr, classLoader);
}
diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/HelpPrinter.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/HelpPrinter.java
index c0a56e0ec17..52d6f188c28 100644
--- a/smithy-cli/src/main/java/software/amazon/smithy/cli/HelpPrinter.java
+++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/HelpPrinter.java
@@ -129,7 +129,7 @@ public void print(ColorFormatter colors, CliPrinter printer) {
LineWrapper builder = new LineWrapper(maxWidth);
builder.appendWithinLine("Usage: ")
- .appendWithinLine(colors.style(name, Style.BRIGHT_WHITE, Style.UNDERLINE))
+ .appendWithinLine(colors.style(name, ColorTheme.EM_UNDERLINE))
.space();
// Calculate the column manually to account for possible styles interfering with the current column number.
@@ -168,13 +168,13 @@ public void print(ColorFormatter colors, CliPrinter printer) {
private void writeArgHelp(ColorFormatter colors, LineWrapper builder, Arg arg) {
if (arg.longName != null) {
- builder.appendWithinLine(colors.style(arg.longName, Style.YELLOW));
+ builder.appendWithinLine(colors.style(arg.longName, ColorTheme.LITERAL));
if (arg.shortName != null) {
builder.appendWithinLine(", ");
}
}
if (arg.shortName != null) {
- builder.appendWithinLine(colors.style(arg.shortName, Style.YELLOW));
+ builder.appendWithinLine(colors.style(arg.shortName, ColorTheme.LITERAL));
}
if (arg.exampleValue != null) {
builder.space().appendWithinLine(arg.exampleValue);
@@ -213,13 +213,13 @@ String toShortArgs(ColorFormatter colors) {
StringBuilder builder = new StringBuilder();
builder.append('[');
if (longName != null) {
- builder.append(colors.style(longName, Style.YELLOW));
+ builder.append(colors.style(longName, ColorTheme.LITERAL));
if (shortName != null) {
builder.append(" | ");
}
}
if (shortName != null) {
- builder.append(colors.style(shortName, Style.YELLOW));
+ builder.append(colors.style(shortName, ColorTheme.LITERAL));
}
if (exampleValue != null) {
builder.append(' ').append(exampleValue);
diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/LoggingArgumentsHandler.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/LoggingArgumentsHandler.java
index dd9a537f7bb..9ee74077e35 100644
--- a/smithy-cli/src/main/java/software/amazon/smithy/cli/LoggingArgumentsHandler.java
+++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/LoggingArgumentsHandler.java
@@ -189,9 +189,9 @@ public void publish(LogRecord record) {
if (isLoggable(record)) {
String formatted = getFormatter().format(record);
if (record.getLevel().equals(Level.SEVERE)) {
- colors.println(printer, formatted, Style.RED);
+ colors.println(printer, formatted, ColorTheme.ERROR);
} else if (record.getLevel().equals(Level.WARNING)) {
- colors.println(printer, formatted, Style.YELLOW);
+ colors.println(printer, formatted, ColorTheme.WARNING);
} else {
printer.println(formatted);
}
diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/Style.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/Style.java
index b956ee7cfd8..321b4766af9 100644
--- a/smithy-cli/src/main/java/software/amazon/smithy/cli/Style.java
+++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/Style.java
@@ -15,6 +15,8 @@
package software.amazon.smithy.cli;
+import java.util.StringJoiner;
+
/**
* Colors and styles for use with {@link AnsiColorFormatter} or {@link CliPrinter}.
*
@@ -67,6 +69,14 @@ public interface Style {
String getAnsiColorCode();
+ static Style of(Style... styles) {
+ if (styles.length == 1) {
+ return styles[0];
+ } else {
+ return new Multi(styles);
+ }
+ }
+
final class Constant implements Style {
private final String ansiColorCode;
@@ -74,8 +84,30 @@ public Constant(String ansiColorCode) {
this.ansiColorCode = ansiColorCode;
}
+ @Override
public String getAnsiColorCode() {
return ansiColorCode;
}
}
+
+ final class Multi implements Style {
+ private final String ansiStyle;
+
+ public Multi(Style... styles) {
+ if (styles.length == 1) {
+ ansiStyle = styles[0].getAnsiColorCode();
+ } else {
+ StringJoiner joiner = new StringJoiner(";");
+ for (Style style : styles) {
+ joiner.add(style.getAnsiColorCode());
+ }
+ ansiStyle = joiner.toString();
+ }
+ }
+
+ @Override
+ public String getAnsiColorCode() {
+ return ansiStyle;
+ }
+ }
}
diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/BuildCommand.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/BuildCommand.java
index 38eb38dd402..d61a0f70418 100644
--- a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/BuildCommand.java
+++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/BuildCommand.java
@@ -37,6 +37,7 @@
import software.amazon.smithy.cli.CliPrinter;
import software.amazon.smithy.cli.ColorBuffer;
import software.amazon.smithy.cli.ColorFormatter;
+import software.amazon.smithy.cli.ColorTheme;
import software.amazon.smithy.cli.Command;
import software.amazon.smithy.cli.HelpPrinter;
import software.amazon.smithy.cli.StandardOptions;
@@ -44,6 +45,7 @@
import software.amazon.smithy.cli.dependencies.DependencyResolver;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.loader.ModelAssembler;
+import software.amazon.smithy.model.loader.sourcecontext.SourceContextLoader;
import software.amazon.smithy.model.validation.Severity;
final class BuildCommand implements Command {
@@ -122,6 +124,11 @@ private int runWithClassLoader(SmithyBuildConfig config, Arguments arguments, En
.validationPrinter(env.stderr())
.build();
+ if (!standardOptions.quiet()) {
+ env.colors().println(env.stderr(), "Validated model, now starting projections...", ColorTheme.MUTED);
+ env.stderr().println("");
+ }
+
Supplier modelAssemblerSupplier = () -> {
ModelAssembler assembler = Model.assembler(classLoader);
if (buildOptions.allowUnknownTraits()) {
@@ -151,25 +158,30 @@ private int runWithClassLoader(SmithyBuildConfig config, Arguments arguments, En
ResultConsumer resultConsumer = new ResultConsumer(env.colors(), env.stderr(), standardOptions.quiet());
smithyBuild.build(resultConsumer, resultConsumer);
+ env.flush();
+
if (!standardOptions.quiet()) {
- Style ansiColor = resultConsumer.failedProjections.isEmpty()
- ? Style.BRIGHT_GREEN
- : Style.BRIGHT_YELLOW;
- env.colors().println(env.stderr(),
- String.format("Smithy built %s projection(s), %s plugin(s), and %s artifacts",
- resultConsumer.projectionCount,
- resultConsumer.pluginCount,
- resultConsumer.artifactCount),
- Style.BOLD, ansiColor);
+ try (ColorBuffer buffer = ColorBuffer.of(env.colors(), env.stderr())) {
+ buffer.print("Summary", ColorTheme.EM_UNDERLINE);
+ buffer.println(String.format(": Smithy built %s projection(s), %s plugin(s), and %s artifacts",
+ resultConsumer.projectionCount,
+ resultConsumer.pluginCount,
+ resultConsumer.artifactCount));
+ }
}
// Throw an exception if any errors occurred.
if (!resultConsumer.failedProjections.isEmpty()) {
resultConsumer.failedProjections.sort(String::compareTo);
- throw new CliError(String.format(
- "The following %d Smithy build projection(s) failed: %s",
- resultConsumer.failedProjections.size(),
- resultConsumer.failedProjections));
+ StringBuilder error = new StringBuilder();
+ try (ColorBuffer buffer = ColorBuffer.of(env.colors(), error)) {
+ buffer.println();
+ buffer.println(String.format(
+ "The following %d Smithy build projection(s) failed: %s",
+ resultConsumer.failedProjections.size(),
+ resultConsumer.failedProjections));
+ }
+ throw new CliError(error.toString());
}
return 0;
@@ -196,45 +208,74 @@ public void accept(String name, Throwable exception) {
StringWriter writer = new StringWriter();
writer.write(String.format("%nProjection %s failed: %s%n", name, exception.toString()));
exception.printStackTrace(new PrintWriter(writer));
- colors.println(printer, writer.toString(), Style.RED);
+ colors.println(printer, writer.toString(), ColorTheme.ERROR);
}
@Override
public void accept(ProjectionResult result) {
try (ColorBuffer buffer = ColorBuffer.of(colors, printer)) {
+ String status;
+ Style statusStyle;
+
if (result.isBroken()) {
- // Write out validation errors as they occur.
failedProjections.add(result.getProjectionName());
- buffer
- .println()
- .print(result.getProjectionName(), Style.RED)
- .println(" has a model that failed validation");
- result.getEvents().forEach(event -> {
- if (event.getSeverity() == Severity.DANGER || event.getSeverity() == Severity.ERROR) {
- buffer.println(event.toString(), Style.RED);
- }
- });
+ statusStyle = ColorTheme.ERROR;
+ status = "Failed";
} else {
// Only increment the projection count if it succeeded.
projectionCount.incrementAndGet();
+ statusStyle = ColorTheme.SUCCESS;
+ status = "Completed";
}
pluginCount.addAndGet(result.getPluginManifests().size());
+ // Increment the total number of artifacts written.
+ for (FileManifest manifest : result.getPluginManifests().values()) {
+ artifactCount.addAndGet(manifest.getFiles().size());
+ }
+
// Get the base directory of the projection.
Iterator manifestIterator = result.getPluginManifests().values().iterator();
Path root = manifestIterator.hasNext() ? manifestIterator.next().getBaseDir().getParent() : null;
if (!quiet) {
- String message = String.format("Completed projection %s (%d shapes): %s",
- result.getProjectionName(), result.getModel().toSet().size(), root);
- buffer.println(message, Style.GREEN);
+ int remainingLength = 80 - 6 - result.getProjectionName().length();
+ buffer.style(w -> {
+ w.append("── ");
+ w.append(result.getProjectionName());
+ w.append(" ");
+ for (int i = 0; i < remainingLength; i++) {
+ w.append('─');
+ }
+ w.println();
+ }, statusStyle);
+ buffer
+ .print(status)
+ .append(" projection ")
+ .append(result.getProjectionName())
+ .append(" (")
+ .append(String.valueOf(result.getModel().toSet().size()))
+ .append("): ")
+ .append(String.valueOf(root))
+ .println();
}
- // Increment the total number of artifacts written.
- for (FileManifest manifest : result.getPluginManifests().values()) {
- artifactCount.addAndGet(manifest.getFiles().size());
+ if (result.isBroken()) {
+ SourceContextLoader loader = SourceContextLoader.createModelAwareLoader(result.getModel(), 4);
+ PrettyAnsiValidationFormatter formatter = PrettyAnsiValidationFormatter.builder()
+ .sourceContextLoader(loader)
+ .colors(colors)
+ .titleLabel(result.getProjectionName(), statusStyle)
+ .build();
+ result.getEvents().forEach(event -> {
+ if (event.getSeverity() == Severity.DANGER || event.getSeverity() == Severity.ERROR) {
+ buffer.println(formatter.format(event));
+ }
+ });
}
+
+ buffer.println();
}
}
}
diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/CodeFormatter.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/CodeFormatter.java
index e64b08f0f3c..d5dcd81ed77 100644
--- a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/CodeFormatter.java
+++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/CodeFormatter.java
@@ -18,7 +18,7 @@
import java.util.Collection;
import java.util.Iterator;
import software.amazon.smithy.cli.ColorBuffer;
-import software.amazon.smithy.cli.Style;
+import software.amazon.smithy.cli.ColorTheme;
import software.amazon.smithy.model.loader.sourcecontext.SourceContextLoader;
/**
@@ -81,7 +81,7 @@ private void writeColumnAndContent(int numberLength, int lineNumber, CharSequenc
}
writer.append("| ");
}
- }, Style.BRIGHT_BLACK);
+ }, ColorTheme.MUTED);
if (content.length() > 0) {
writeStringWithMaxWidth(content, numberLength);
@@ -96,12 +96,12 @@ private void writePointer(int numberLength, int cursorColumn) {
w.append(' ');
}
w.append("|");
- }, Style.BRIGHT_BLACK);
+ }, ColorTheme.MUTED);
for (int j = 0; j < cursorColumn; j++) {
writer.append(' ');
}
- writer.print("^", Style.RED);
+ writer.print("^", ColorTheme.ERROR);
writer.println();
}
diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/DiffCommand.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/DiffCommand.java
index 26d341b3fcf..00d15f01ecd 100644
--- a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/DiffCommand.java
+++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/DiffCommand.java
@@ -19,20 +19,16 @@
import java.util.List;
import java.util.function.Consumer;
import java.util.logging.Logger;
-import java.util.stream.Collectors;
import software.amazon.smithy.build.model.SmithyBuildConfig;
import software.amazon.smithy.cli.ArgumentReceiver;
import software.amazon.smithy.cli.Arguments;
import software.amazon.smithy.cli.CliError;
-import software.amazon.smithy.cli.ColorBuffer;
+import software.amazon.smithy.cli.ColorTheme;
import software.amazon.smithy.cli.Command;
import software.amazon.smithy.cli.HelpPrinter;
-import software.amazon.smithy.cli.StandardOptions;
-import software.amazon.smithy.cli.Style;
import software.amazon.smithy.cli.dependencies.DependencyResolver;
import software.amazon.smithy.diff.ModelDiff;
import software.amazon.smithy.model.Model;
-import software.amazon.smithy.model.loader.ModelAssembler;
import software.amazon.smithy.model.validation.Severity;
import software.amazon.smithy.model.validation.ValidatedResult;
import software.amazon.smithy.model.validation.ValidationEvent;
@@ -111,7 +107,6 @@ int runWithClassLoader(SmithyBuildConfig config, Arguments arguments, Env env) {
throw new CliError("Unexpected arguments: " + arguments.getPositional());
}
- StandardOptions standardOptions = arguments.getReceiver(StandardOptions.class);
Options options = arguments.getReceiver(Options.class);
ClassLoader classLoader = env.classLoader();
@@ -119,50 +114,30 @@ int runWithClassLoader(SmithyBuildConfig config, Arguments arguments, Env env) {
List newModels = options.newModels;
LOGGER.fine(() -> String.format("Setting old models to: %s; new models to: %s", oldModels, newModels));
- ModelAssembler assembler = ModelBuilder.createModelAssembler(classLoader);
- Model oldModel = loadModel("old", assembler, oldModels);
- assembler.reset();
- Model newModel = loadModel("new", assembler, newModels);
-
+ ModelBuilder modelBuilder = new ModelBuilder()
+ .config(config)
+ .arguments(arguments)
+ .env(env)
+ .validationPrinter(env.stderr())
+ .validationMode(Validator.Mode.DISABLE)
+ .severity(Severity.DANGER);
+ Model oldModel = modelBuilder
+ .models(oldModels)
+ .titleLabel("OLD", ColorTheme.DIFF_EVENT_TITLE)
+ .build();
+ Model newModel = modelBuilder
+ .models(newModels)
+ .titleLabel("NEW", ColorTheme.DIFF_EVENT_TITLE)
+ .build();
+
+ // Diff the models and report on the events, failing if necessary.
List events = ModelDiff.compare(classLoader, oldModel, newModel);
- boolean hasError = events.stream().anyMatch(event -> event.getSeverity() == Severity.ERROR);
- boolean hasDanger = events.stream().anyMatch(event -> event.getSeverity() == Severity.DANGER);
- boolean hasWarning = events.stream().anyMatch(event -> event.getSeverity() == Severity.DANGER);
- String result = events.stream().map(ValidationEvent::toString).collect(Collectors.joining("\n"));
-
- if (hasError) {
- throw new CliError(String.format("Model diff detected errors: %n%s", result));
- }
-
- if (!result.isEmpty()) {
- env.stdout().println(result);
- }
-
- // Print the "framing" style output to stderr only if !quiet.
- if (!standardOptions.quiet()) {
- try (ColorBuffer buffer = ColorBuffer.of(env.colors(), env.stderr())) {
- if (hasDanger) {
- buffer.println("Smithy diff detected danger", Style.BRIGHT_RED, Style.BOLD);
- } else if (hasWarning) {
- buffer.println("Smithy diff detected warnings", Style.BRIGHT_YELLOW, Style.BOLD);
- } else {
- buffer.println("Smithy diff complete", Style.BRIGHT_GREEN, Style.BOLD);
- }
- }
- }
+ modelBuilder
+ .titleLabel("DIFF", ColorTheme.DIFF_TITLE)
+ .validatedResult(new ValidatedResult<>(newModel, events))
+ .severity(null) // reset so it takes on standard option settings.
+ .build();
return 0;
}
-
- private Model loadModel(String descriptor, ModelAssembler assembler, List models) {
- models.forEach(assembler::addImport);
- ValidatedResult result = assembler.assemble();
- if (result.isBroken()) {
- throw new CliError("Error loading " + descriptor + " models: \n" + result.getValidationEvents().stream()
- .map(ValidationEvent::toString)
- .collect(Collectors.joining("\n")));
- }
-
- return result.unwrap();
- }
}
diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/MigrateCommand.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/MigrateCommand.java
index 5cbe13d9115..255be59c178 100644
--- a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/MigrateCommand.java
+++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/MigrateCommand.java
@@ -40,9 +40,9 @@
import software.amazon.smithy.build.model.SmithyBuildConfig;
import software.amazon.smithy.cli.Arguments;
import software.amazon.smithy.cli.CliError;
+import software.amazon.smithy.cli.ColorTheme;
import software.amazon.smithy.cli.Command;
import software.amazon.smithy.cli.StandardOptions;
-import software.amazon.smithy.cli.Style;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.SourceLocation;
import software.amazon.smithy.model.loader.ModelAssembler;
@@ -92,7 +92,7 @@ public String getSummary() {
public int execute(Arguments arguments, Env env) {
if (!arguments.getReceiver(StandardOptions.class).quiet()) {
env.colors().style(env.stderr(), "upgrade-1-to-2 is deprecated. Use the migrate command instead."
- + System.lineSeparator(), Style.BG_YELLOW, Style.BLACK);
+ + System.lineSeparator(), ColorTheme.DEPRECATED);
env.stderr().flush();
}
return command.execute(arguments, env);
diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ModelBuilder.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ModelBuilder.java
index fcf62b802d7..95f170b8f52 100644
--- a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ModelBuilder.java
+++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ModelBuilder.java
@@ -33,6 +33,7 @@
import software.amazon.smithy.cli.Command;
import software.amazon.smithy.cli.EnvironmentVariable;
import software.amazon.smithy.cli.StandardOptions;
+import software.amazon.smithy.cli.Style;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.loader.ModelAssembler;
import software.amazon.smithy.model.loader.sourcecontext.SourceContextLoader;
@@ -57,6 +58,9 @@ final class ModelBuilder {
private Command.Env env;
private SmithyBuildConfig config;
private Severity severity;
+ private ValidatedResult validatedResult;
+ private String titleLabel;
+ private Style[] titleLabelStyles;
public ModelBuilder arguments(Arguments arguments) {
this.arguments = arguments;
@@ -64,6 +68,7 @@ public ModelBuilder arguments(Arguments arguments) {
}
public ModelBuilder models(List models) {
+ validatedResult = null;
this.models = models;
return this;
}
@@ -93,6 +98,17 @@ public ModelBuilder severity(Severity severity) {
return this;
}
+ public ModelBuilder validatedResult(ValidatedResult validatedResult) {
+ this.validatedResult = validatedResult;
+ return this;
+ }
+
+ public ModelBuilder titleLabel(String titleLabel, Style... styles) {
+ this.titleLabel = titleLabel;
+ this.titleLabelStyles = styles;
+ return this;
+ }
+
public Model build() {
SmithyBuilder.requiredState("arguments", arguments);
SmithyBuilder.requiredState("models", models);
@@ -104,7 +120,6 @@ public Model build() {
DiscoveryOptions discoveryOptions = arguments.getReceiver(DiscoveryOptions.class);
Severity minSeverity = resolveMinSeverity(standardOptions);
ClassLoader classLoader = env.classLoader();
- ModelAssembler assembler = createModelAssembler(classLoader);
ColorFormatter colors = env.colors();
CliPrinter stderr = env.stderr();
@@ -116,31 +131,38 @@ public Model build() {
validationMode = Validator.Mode.from(standardOptions);
}
- if (validationMode == Validator.Mode.DISABLE) {
- assembler.disableValidation();
- }
-
- // Emit status updates.
- AtomicInteger issueCount = new AtomicInteger();
- assembler.validationEventListener(createStatusUpdater(standardOptions, colors, stderr, issueCount));
+ if (validatedResult == null) {
+ ModelAssembler assembler = createModelAssembler(classLoader);
- handleModelDiscovery(discoveryOptions, assembler, classLoader, config);
- handleUnknownTraitsOption(buildOptions, assembler);
- config.getSources().forEach(assembler::addImport);
- models.forEach(assembler::addImport);
- config.getImports().forEach(assembler::addImport);
- ValidatedResult result = assembler.assemble();
+ if (validationMode == Validator.Mode.DISABLE) {
+ assembler.disableValidation();
+ }
- clearStatusUpdateIfPresent(issueCount, stderr);
+ // Emit status updates.
+ AtomicInteger issueCount = new AtomicInteger();
+ assembler.validationEventListener(createStatusUpdater(standardOptions, colors, stderr, issueCount));
+
+ handleModelDiscovery(discoveryOptions, assembler, classLoader, config);
+ handleUnknownTraitsOption(buildOptions, assembler);
+ config.getSources().forEach(assembler::addImport);
+ models.forEach(assembler::addImport);
+ config.getImports().forEach(assembler::addImport);
+ validatedResult = assembler.assemble();
+ clearStatusUpdateIfPresent(issueCount, stderr);
+ }
// Sort events by file so that we can efficiently read files for context sequentially.
- List sortedEvents = new ArrayList<>(result.getValidationEvents());
+ List sortedEvents = new ArrayList<>(validatedResult.getValidationEvents());
sortedEvents.sort(Comparator.comparing(ValidationEvent::getSourceLocation));
- SourceContextLoader sourceContextLoader = result.getResult()
+ SourceContextLoader sourceContextLoader = validatedResult.getResult()
.map(model -> SourceContextLoader.createModelAwareLoader(model, DEFAULT_CODE_LINES))
.orElseGet(() -> SourceContextLoader.createLineBasedLoader(DEFAULT_CODE_LINES));
- PrettyAnsiValidationFormatter formatter = new PrettyAnsiValidationFormatter(sourceContextLoader, colors);
+ PrettyAnsiValidationFormatter formatter = PrettyAnsiValidationFormatter.builder()
+ .sourceContextLoader(sourceContextLoader)
+ .colors(colors)
+ .titleLabel(titleLabel, titleLabelStyles)
+ .build();
for (ValidationEvent event : sortedEvents) {
// Only log events that are >= --severity. Note that setting --quiet inherently
@@ -150,14 +172,12 @@ public Model build() {
}
}
+ env.flush();
// Note: disabling validation will still show a summary of failures if the model can't be loaded.
- Validator.validate(validationMode != Validator.Mode.ENABLE, colors, stderr, result);
-
- // Flush outputs to ensure there is no interleaving with subsequent command output.
- env.stderr().flush();
- env.stdout().flush();
+ Validator.validate(validationMode != Validator.Mode.ENABLE, colors, stderr, validatedResult);
+ env.flush();
- return result.getResult().orElseThrow(() -> new RuntimeException("Expected Validator to throw"));
+ return validatedResult.getResult().orElseThrow(() -> new RuntimeException("Expected Validator to throw"));
}
static Consumer createStatusUpdater(
diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/PrettyAnsiValidationFormatter.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/PrettyAnsiValidationFormatter.java
index 4a9cfcf1aa4..4241ee94954 100644
--- a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/PrettyAnsiValidationFormatter.java
+++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/PrettyAnsiValidationFormatter.java
@@ -21,11 +21,13 @@
import java.util.regex.Pattern;
import software.amazon.smithy.cli.ColorBuffer;
import software.amazon.smithy.cli.ColorFormatter;
+import software.amazon.smithy.cli.ColorTheme;
import software.amazon.smithy.cli.Style;
import software.amazon.smithy.model.SourceLocation;
import software.amazon.smithy.model.loader.sourcecontext.SourceContextLoader;
import software.amazon.smithy.model.validation.ValidationEvent;
import software.amazon.smithy.model.validation.ValidationEventFormatter;
+import software.amazon.smithy.utils.SmithyBuilder;
import software.amazon.smithy.utils.StringUtils;
final class PrettyAnsiValidationFormatter implements ValidationEventFormatter {
@@ -35,10 +37,18 @@ final class PrettyAnsiValidationFormatter implements ValidationEventFormatter {
private final SourceContextLoader sourceContextLoader;
private final ColorFormatter colors;
private final String rootPath = Paths.get("").normalize().toAbsolutePath().toString();
+ private final String titleLabel;
+ private final Style[] titleLabelStyles;
+
+ PrettyAnsiValidationFormatter(Builder builder) {
+ this.sourceContextLoader = SmithyBuilder.requiredState("sourceContextLoader", builder.sourceContextLoader);
+ this.colors = SmithyBuilder.requiredState("colors", builder.colors);
+ this.titleLabel = SmithyBuilder.requiredState("titleLabel", builder.titleLabel);
+ this.titleLabelStyles = builder.titleLabelStyles;
+ }
- PrettyAnsiValidationFormatter(SourceContextLoader loader, ColorFormatter colors) {
- this.sourceContextLoader = loader;
- this.colors = colors;
+ static Builder builder() {
+ return new Builder();
}
@Override
@@ -49,26 +59,26 @@ public String format(ValidationEvent event) {
switch (event.getSeverity()) {
case WARNING:
- printTitle(writer, event, Style.YELLOW, Style.BG_YELLOW, Style.BLACK);
+ printTitle(writer, event, ColorTheme.WARNING, ColorTheme.WARNING_TITLE);
break;
case ERROR:
- printTitle(writer, event, Style.RED, Style.BG_RED, Style.BLACK);
+ printTitle(writer, event, ColorTheme.ERROR, ColorTheme.ERROR_TITLE);
break;
case DANGER:
- printTitle(writer, event, Style.MAGENTA, Style.BG_MAGENTA, Style.BLACK);
+ printTitle(writer, event, ColorTheme.DANGER, ColorTheme.DANGER_TITLE);
break;
case NOTE:
- printTitle(writer, event, Style.CYAN, Style.BG_CYAN, Style.BLACK);
+ printTitle(writer, event, ColorTheme.NOTE, ColorTheme.NOTE_TITLE);
break;
case SUPPRESSED:
default:
- printTitle(writer, event, Style.GREEN, Style.BG_GREEN, Style.BLACK);
+ printTitle(writer, event, ColorTheme.SUPPRESSED, ColorTheme.SUPPRESSED_TITLE);
}
// Only write an event ID if there is an associated ID.
event.getShapeId().ifPresent(id -> {
- colors.style(writer, "Shape: ", Style.BRIGHT_BLACK);
- colors.style(writer, id.toString(), Style.BLUE);
+ colors.style(writer, "Shape: ", ColorTheme.MUTED);
+ colors.style(writer, id.toString(), ColorTheme.EVENT_SHAPE_ID);
writer.append(ls);
});
@@ -78,8 +88,8 @@ public String format(ValidationEvent event) {
String humanReadableFilename = getHumanReadableFilename(event.getSourceLocation().getFilename());
int line = event.getSourceLocation().getLine();
int column = event.getSourceLocation().getColumn();
- colors.style(writer, "File: ", Style.BRIGHT_BLACK);
- colors.style(writer, humanReadableFilename + ':' + line + ':' + column, Style.BLUE);
+ colors.style(writer, "File: ", ColorTheme.MUTED);
+ colors.style(writer, humanReadableFilename + ':' + line + ':' + column, ColorTheme.MUTED);
writer.append(ls).append(ls);
try {
@@ -88,7 +98,7 @@ public String format(ValidationEvent event) {
new CodeFormatter(writer, LINE_LENGTH).writeCode(line, column, lines);
}
} catch (UncheckedIOException e) {
- colors.style(writer, "Invalid source file", Style.UNDERLINE);
+ colors.style(writer, "Invalid source file", ColorTheme.EM_UNDERLINE);
writer.append(": ");
writeMessage(writer, e.getCause().getMessage());
writer.append(ls).append(ls);
@@ -102,31 +112,38 @@ public String format(ValidationEvent event) {
}
}
- private void printTitle(ColorBuffer writer, ValidationEvent event, Style borderColor, Style... styles) {
- colors.style(writer, "── ", borderColor);
- String severity = ' ' + event.getSeverity().toString() + ' ';
- colors.style(writer, severity, styles);
+ private void printTitle(ColorBuffer writer, ValidationEvent event, Style border, Style styles) {
+ colors.style(writer, "── ", border);
+
+ if (!titleLabel.isEmpty()) {
+ colors.style(writer, ' ' + titleLabel + ' ', titleLabelStyles);
+ }
+
+ colors.style(writer, ' ' + event.getSeverity().toString() + ' ', styles);
+
+ // dash+padding, [padding + titleLabel + padding + padding], severity, padding+dash, padding, padding.
+ int prefixLength = 3 + (titleLabel.isEmpty() ? 0 : (titleLabel.length() + 2))
+ + 1 + event.getSeverity().toString().length() + 1 + 3 + 1;
writer.style(w -> {
w.append(" ──");
- int currentLength = severity.length() + 3 + 3 + 1; // severity, dash+padding, padding+dash, padding.
- int remainingLength = LINE_LENGTH - currentLength;
+ int remainingLength = LINE_LENGTH - prefixLength;
int padding = remainingLength - event.getId().length();
for (int i = 0; i < padding; i++) {
w.append("─");
}
w.append(' ');
- }, borderColor);
+ }, border);
writer.append(event.getId()).append(System.lineSeparator());
}
// Converts Markdown style ticks to use color highlights if colors are enabled.
private void writeMessage(ColorBuffer writer, String message) {
- String content = StringUtils.wrap(message, 80, System.lineSeparator(), false);
+ String content = StringUtils.wrap(message, LINE_LENGTH, System.lineSeparator(), false);
if (colors.isColorEnabled()) {
- content = TICK_PATTERN.matcher(content).replaceAll(colors.style("$1", Style.CYAN));
+ content = TICK_PATTERN.matcher(content).replaceAll(colors.style("$1", ColorTheme.LITERAL));
}
writer.append(content);
@@ -147,4 +164,31 @@ private String getHumanReadableFilename(String filename) {
return filename;
}
+
+ static final class Builder {
+ private SourceContextLoader sourceContextLoader;
+ private ColorFormatter colors;
+ private String titleLabel = "";
+ private Style[] titleLabelStyles;
+
+ Builder sourceContextLoader(SourceContextLoader sourceContextLoader) {
+ this.sourceContextLoader = sourceContextLoader;
+ return this;
+ }
+
+ Builder colors(ColorFormatter colors) {
+ this.colors = colors;
+ return this;
+ }
+
+ Builder titleLabel(String titleLabel, Style... styles) {
+ this.titleLabel = titleLabel == null ? "" : titleLabel;
+ titleLabelStyles = styles;
+ return this;
+ }
+
+ PrettyAnsiValidationFormatter build() {
+ return new PrettyAnsiValidationFormatter(this);
+ }
+ }
}
diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/SmithyCommand.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/SmithyCommand.java
index 0e51652c2a3..167615aa1e4 100644
--- a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/SmithyCommand.java
+++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/SmithyCommand.java
@@ -22,11 +22,11 @@
import software.amazon.smithy.cli.CliError;
import software.amazon.smithy.cli.CliPrinter;
import software.amazon.smithy.cli.ColorFormatter;
+import software.amazon.smithy.cli.ColorTheme;
import software.amazon.smithy.cli.Command;
import software.amazon.smithy.cli.EnvironmentVariable;
import software.amazon.smithy.cli.SmithyCli;
import software.amazon.smithy.cli.StandardOptions;
-import software.amazon.smithy.cli.Style;
import software.amazon.smithy.cli.dependencies.DependencyResolver;
public final class SmithyCommand implements Command {
@@ -65,7 +65,7 @@ public String getSummary() {
private void printHelp(ColorFormatter colors, CliPrinter printer) {
printer.println(String.format("Usage: %s [-h | --help] [--version] []",
- colors.style("smithy", Style.BRIGHT_WHITE, Style.UNDERLINE)));
+ colors.style("smithy", ColorTheme.EM_UNDERLINE)));
printer.println("");
printer.println("Available commands:");
@@ -81,7 +81,7 @@ private void printHelp(ColorFormatter colors, CliPrinter printer) {
for (Command command : commands) {
if (!command.isHidden()) {
printer.println(String.format(" %-" + longestName + "s %s",
- colors.style(command.getName(), Style.YELLOW),
+ colors.style(command.getName(), ColorTheme.LITERAL),
command.getSummary()));
}
}
diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/Validator.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/Validator.java
index 4fa24d971ea..686c3ba13bc 100644
--- a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/Validator.java
+++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/Validator.java
@@ -20,8 +20,8 @@
import software.amazon.smithy.cli.CliPrinter;
import software.amazon.smithy.cli.ColorBuffer;
import software.amazon.smithy.cli.ColorFormatter;
+import software.amazon.smithy.cli.ColorTheme;
import software.amazon.smithy.cli.StandardOptions;
-import software.amazon.smithy.cli.Style;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.validation.Severity;
import software.amazon.smithy.model.validation.ValidatedResult;
@@ -52,9 +52,9 @@ static void validate(boolean quiet, ColorFormatter colors, CliPrinter printer, V
try (ColorBuffer output = ColorBuffer.of(colors, new StringBuilder())) {
if (isFailed) {
- output.append(colors.style("FAILURE: ", Style.RED, Style.BOLD));
+ output.append(colors.style("FAILURE: ", ColorTheme.ERROR));
} else {
- output.append(colors.style("SUCCESS: ", Style.GREEN, Style.BOLD));
+ output.append(colors.style("SUCCESS: ", ColorTheme.SUCCESS));
}
output.append("Validated " + shapeCount).append(" shapes");
diff --git a/smithy-cli/src/test/java/software/amazon/smithy/cli/BufferPrinter.java b/smithy-cli/src/test/java/software/amazon/smithy/cli/BufferPrinter.java
index 5d72b41553a..d6d38cd37b1 100644
--- a/smithy-cli/src/test/java/software/amazon/smithy/cli/BufferPrinter.java
+++ b/smithy-cli/src/test/java/software/amazon/smithy/cli/BufferPrinter.java
@@ -15,6 +15,12 @@ public BufferPrinter println(String text) {
return append(text + "\n");
}
+ @Override
+ public CliPrinter append(CharSequence csq, int start, int end) {
+ builder.append(csq, start, end);
+ return this;
+ }
+
@Override
public BufferPrinter append(CharSequence text) {
synchronized (this) {
diff --git a/smithy-cli/src/test/java/software/amazon/smithy/cli/CliPrinterTest.java b/smithy-cli/src/test/java/software/amazon/smithy/cli/CliPrinterTest.java
index 0e323d7e5c9..ed973628326 100644
--- a/smithy-cli/src/test/java/software/amazon/smithy/cli/CliPrinterTest.java
+++ b/smithy-cli/src/test/java/software/amazon/smithy/cli/CliPrinterTest.java
@@ -24,9 +24,18 @@ public class CliPrinterTest {
@Test
public void printsWithNewlineByDefault() {
StringBuilder builder = new StringBuilder();
- CliPrinter printer = c -> {
- builder.append(c);
- return null;
+ CliPrinter printer = new CliPrinter() {
+ @Override
+ public CliPrinter append(char c) {
+ builder.append(c);
+ return this;
+ }
+
+ @Override
+ public CliPrinter append(CharSequence csq, int start, int end) {
+ builder.append(csq, start, end);
+ return this;
+ }
};
printer.println("Hi");
diff --git a/smithy-cli/src/test/java/software/amazon/smithy/cli/commands/PrettyAnsiValidationFormatterTest.java b/smithy-cli/src/test/java/software/amazon/smithy/cli/commands/PrettyAnsiValidationFormatterTest.java
index 937dcff6f51..4d3c9a314e9 100644
--- a/smithy-cli/src/test/java/software/amazon/smithy/cli/commands/PrettyAnsiValidationFormatterTest.java
+++ b/smithy-cli/src/test/java/software/amazon/smithy/cli/commands/PrettyAnsiValidationFormatterTest.java
@@ -23,6 +23,7 @@
import org.junit.jupiter.api.Test;
import software.amazon.smithy.cli.AnsiColorFormatter;
import software.amazon.smithy.cli.ColorFormatter;
+import software.amazon.smithy.cli.Style;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.SourceLocation;
import software.amazon.smithy.model.loader.sourcecontext.SourceContextLoader;
@@ -57,8 +58,8 @@ public void formatsEventsWithColors() {
assertThat(formatted, equalTo(
"\n"
+ "\u001B[31m── \u001B[0m\u001B[41;30m ERROR \u001B[0m\u001B[31m ───────────────────────────────────────────────────────────────── \u001B[0mFoo\n"
- + "\u001B[90mShape: \u001B[0m\u001B[34msmithy.example#Foo\u001B[0m\n"
- + "\u001B[90mFile: \u001B[0m\u001B[34mbuild/resources/test/software/amazon/smithy/cli/commands/valid-model.smithy:5:1\u001B[0m\n"
+ + "\u001B[90mShape: \u001B[0m\u001B[95msmithy.example#Foo\u001B[0m\n"
+ + "\u001B[90mFile: \u001B[0m\u001B[90mbuild/resources/test/software/amazon/smithy/cli/commands/valid-model.smithy:5:1\u001B[0m\n"
+ "\n"
+ "\u001B[90m4| \u001B[0m\n"
+ "\u001B[90m5| \u001B[0mresource Foo {\n"
@@ -111,7 +112,7 @@ public void wrapsLongLines() {
private PrettyAnsiValidationFormatter createFormatter(ColorFormatter colors) {
SourceContextLoader loader = SourceContextLoader.createLineBasedLoader(2);
- return new PrettyAnsiValidationFormatter(loader, colors);
+ return PrettyAnsiValidationFormatter.builder().sourceContextLoader(loader).colors(colors).build();
}
private String formatTestEventWithSeverity(PrettyAnsiValidationFormatter pretty, Severity severity) {
@@ -135,7 +136,10 @@ public void toleratesInvalidSourceFiles() {
SourceContextLoader loader = s -> {
throw new UncheckedIOException(new IOException("Error!!!"));
};
- PrettyAnsiValidationFormatter pretty = new PrettyAnsiValidationFormatter(loader, colors);
+ PrettyAnsiValidationFormatter pretty = PrettyAnsiValidationFormatter.builder()
+ .sourceContextLoader(loader)
+ .colors(colors)
+ .build();
ValidationEvent event = ValidationEvent.builder()
.id("Foo")
.severity(Severity.NOTE)
@@ -154,4 +158,29 @@ public void toleratesInvalidSourceFiles() {
+ "\n"
+ "Hello\n"));
}
+
+ @Test
+ public void showsTitleLabelsWhenPresent() {
+ PrettyAnsiValidationFormatter pretty = PrettyAnsiValidationFormatter.builder()
+ .sourceContextLoader(SourceContextLoader.createLineBasedLoader(4))
+ .colors(AnsiColorFormatter.FORCE_COLOR)
+ .titleLabel("FOO", Style.BG_BLUE, Style.BLACK)
+ .build();
+ ValidationEvent event = ValidationEvent.builder()
+ .id("Hello")
+ .severity(Severity.WARNING)
+ .shapeId(ShapeId.from("smithy.example#Foo"))
+ .message("hello")
+ .build();
+
+ String formatted = normalizeLinesAndFiles(pretty.format(event));
+
+ assertThat(formatted, equalTo(
+ "\n"
+ + "\u001B[33m── \u001B[0m\u001B[44;30m FOO \u001B[0m\u001B[43;30m "
+ + "WARNING \u001B[0m\u001B[33m ──────────────────────────────────────────────────────── \u001B[0mHello\n"
+ + "\u001B[90mShape: \u001B[0m\u001B[95msmithy.example#Foo\u001B[0m\n"
+ + "\n"
+ + "hello\n"));
+ }
}