Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add some utility methods to CodeWriter #624

Merged
merged 1 commit into from
Nov 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,30 @@ private String getTrimmedPoppedStateContents(State state) {
* a non-null, non-empty string, allowing for empty placeholders to be
* added that don't affect the resulting layout of the code.
*
* <pre>{@code
* CodeWriter writer = new CodeWriter();
*
* // Prepend text to a section named "foo".
* writer.onSectionPrepend("foo", () -> writer.write("A"));
*
* // Write text to a section, and ensure that the original
* // text is written too.
* writer.onSection("foo", text -> {
* // Write the original text of the section.
* writer.write(text);
* // Write more text to the section.
* writer.write("C");
* });
*
* // Append text to a section.
* writer.onSectionAppend("foo", () -> writer.write("D"));
*
* // Create the section, write to it, then close the section.
* writer.pushState("foo").write("B").popState();
*
* assert(writer.toString().equals("A\nB\nC\nD\n"));
* }</pre>
*
* @param sectionName The name of the section to intercept.
* @param interceptor The function to intercept with.
* @return Returns the CodeWriter.
Expand All @@ -710,6 +734,48 @@ public CodeWriter onSection(String sectionName, Consumer<Object> interceptor) {
return this;
}

/**
* Prepends to the contents of a named section.
*
* <pre>{@code
* writer.onSectionPrepend("foo", () -> {
* writer.write("This text is added before the rest of the section.");
* });
* }</pre>
*
* @param sectionName The name of the section to intercept.
* @param writeBefore A runnable that prepends to a section by mutating the writer.
* @return Returns the CodeWriter.
* @see #onSection
*/
public final CodeWriter onSectionPrepend(String sectionName, Runnable writeBefore) {
return onSection(sectionName, contents -> {
writeBefore.run();
write(contents);
});
}

/**
* Appends to the contents of a named section.
*
* <pre>{@code
* writer.onSectionAppend("foo", () -> {
* writer.write("This text is added after the rest of the section.");
* });
* }</pre>
*
* @param sectionName The name of the section to intercept.
* @param writeAfter A runnable that appends to a section by mutating the writer.
* @return Returns the CodeWriter.
* @see #onSection
*/
public final CodeWriter onSectionAppend(String sectionName, Runnable writeAfter) {
return onSection(sectionName, contents -> {
write(contents);
writeAfter.run();
});
}

/**
* Disables the automatic appending of newlines in the current state.
*
Expand Down Expand Up @@ -1093,6 +1159,36 @@ public final CodeWriter writeWithNoFormatting(Object content) {
return this;
}

/**
* Creates a formatted string using formatter expressions and variadic
* arguments.
*
* <p>Important: if the formatters that are executed while formatting the
* given {@code content} string mutate the CodeWriter, it could leave the
* CodeWriter in an inconsistent state. For example, some CodeWriter
* implementations manage imports and dependencies automatically based on
* code that is referenced by formatters. If such an expression is used
* with this format method but the returned String is never written to the
* CodeWriter, then the CodeWriter might be mutated to track dependencies
* that aren't actually necessary.
*
* <pre>{@code
* CodeWriter writer = new CodeWriter();
* String name = "Person";
* String formatted = writer.format("Hello, $L", name);
* assert(formatted.equals("Hello, Person"));
* }</pre>
*
* @param content Content to format.
* @param args String arguments to use for formatting.
* @return Returns the formatted string.
* @see #write
* @see #putFormatter
*/
public final String format(Object content, Object... args) {
return formatter.format(currentState.expressionStart, content, currentState.indentText, this, args);
}

/**
* Writes text to the CodeWriter and appends a newline.
*
Expand All @@ -1107,7 +1203,7 @@ public final CodeWriter writeWithNoFormatting(Object content) {
* @return Returns the CodeWriter.
*/
public final CodeWriter write(Object content, Object... args) {
String value = formatter.format(currentState.expressionStart, content, currentState.indentText, this, args);
String value = format(content, args);
currentState.writeLine(value);
return this;
}
Expand All @@ -1128,7 +1224,7 @@ public final CodeWriter write(Object content, Object... args) {
* @return Returns the CodeWriter.
*/
public final CodeWriter writeInline(Object content, Object... args) {
String value = formatter.format(currentState.expressionStart, content, currentState.indentText, this, args);
String value = format(content, args);
currentState.write(value);
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,20 @@ public void hasSections() {
assertThat(w.toString(), equalTo("Si: Yes: Original!\n123\n"));
}

@Test
public void canPrependAndAppendToSection() {
CodeWriter w = CodeWriter.createDefault().putContext("testing", "123");
w.onSectionPrepend("foo", () -> w.write("A"));
w.onSection("foo", text -> {
w.write(text);
w.write("C");
});
w.onSectionAppend("foo", () -> w.write("D"));
w.pushState("foo").write("B").popState();

assertThat(w.toString(), equalTo("A\nB\nC\nD\n"));
}

@Test
public void allowsForCustomFormatters() {
// Setup the code writer and section interceptors.
Expand Down