diff --git a/smithy-utils/src/main/java/software/amazon/smithy/utils/CodeWriter.java b/smithy-utils/src/main/java/software/amazon/smithy/utils/CodeWriter.java index 0426853294d..6805da15e50 100644 --- a/smithy-utils/src/main/java/software/amazon/smithy/utils/CodeWriter.java +++ b/smithy-utils/src/main/java/software/amazon/smithy/utils/CodeWriter.java @@ -1263,6 +1263,49 @@ public final CodeWriter writeOptional(Object content) { } } + /** + * Remove the most recent text written to the CodeWriter if and only + * if the last written text is exactly equal to the given expanded + * content string. + * + *
This can be useful, for example, for use cases like removing + * trailing commas from lists of values. + * + *
For example, the following will remove ", there." from the + * end of the CodeWriter: + * + *
{@code + * CodeWriter writer = new CodeWriter(); + * writer.writeInline("Hello, there."); + * writer.unwrite(", there."); + * assert(writer.toString().equals("Hello\n")); + * }+ * + *
However, the following call to unwrite will do nothing because + * the last text written to the CodeWriter does not match: + * + *
{@code + * CodeWriter writer = new CodeWriter(); + * writer.writeInline("Hello."); + * writer.unwrite("there."); + * assert(writer.toString().equals("Hello.\n")); + * }+ * + * @param content Content to write. + * @param args String arguments to use for formatting. + * @return Returns the CodeWriter. + */ + public final CodeWriter unwrite(Object content, Object... args) { + String value = format(content, args); + int currentLength = currentState.builder.length(); + + if (currentState.builder.lastIndexOf(value) == currentLength - value.length()) { + currentState.builder.setLength(currentLength - value.length()); + } + + return this; + } + /** * Allows calling out to arbitrary code for things like looping or * conditional writes without breaking method chaining. diff --git a/smithy-utils/src/test/java/software/amazon/smithy/utils/CodeWriterTest.java b/smithy-utils/src/test/java/software/amazon/smithy/utils/CodeWriterTest.java index c25cd08b77e..ed6492013e1 100644 --- a/smithy-utils/src/test/java/software/amazon/smithy/utils/CodeWriterTest.java +++ b/smithy-utils/src/test/java/software/amazon/smithy/utils/CodeWriterTest.java @@ -776,4 +776,49 @@ public void sectionWithWriteInline() { assertThat(writer.toString(), equalTo("inline addition\n")); } + + @Test + public void canUnwriteMatchingStrings() { + CodeWriter writer = new CodeWriter().insertTrailingNewline(false); + writer.writeInline("Hello there"); + writer.unwrite(" there"); + + assertThat(writer.toString(), equalTo("Hello")); + } + + @Test + public void unwriteDoesNothingWhenNoMatch() { + CodeWriter writer = new CodeWriter().insertTrailingNewline(false); + writer.writeInline("Hello there"); + writer.unwrite(" nope"); + + assertThat(writer.toString(), equalTo("Hello there")); + } + + @Test + public void canUnwriteWhenSubstringTooLong() { + CodeWriter writer = new CodeWriter().insertTrailingNewline(false); + writer.writeInline(""); + writer.unwrite("nope"); + + assertThat(writer.toString(), equalTo("")); + } + + @Test + public void canUnwriteWithTemplates() { + CodeWriter writer = new CodeWriter().insertTrailingNewline(false); + writer.writeInline("Hi.Hello"); + writer.unwrite("$L", "Hello"); + + assertThat(writer.toString(), equalTo("Hi.")); + } + + @Test + public void canUnwriteWithTemplatesThatExpandToNothing() { + CodeWriter writer = new CodeWriter().insertTrailingNewline(false); + writer.writeInline("Hi.Hello"); + writer.unwrite("$L", ""); + + assertThat(writer.toString(), equalTo("Hi.Hello")); + } }