Skip to content

Commit

Permalink
Refactor CLI color printing and buffering
Browse files Browse the repository at this point in the history
This commit cleans up how ColorFormatter, CliPrinter, and
ColorBuffer all interact.
  • Loading branch information
mtdowling committed Apr 3, 2023
1 parent 8e7f437 commit 95f7c59
Show file tree
Hide file tree
Showing 18 changed files with 483 additions and 473 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Objects;
import java.util.function.Consumer;

/**
* Styles text using ANSI color codes.
Expand All @@ -29,91 +28,51 @@ public enum AnsiColorFormatter implements ColorFormatter {
* Does not write any color.
*/
NO_COLOR {
@Override
public String style(String text, Style... styles) {
return text;
}

@Override
public void style(Appendable appendable, String text, Style... styles) {
try {
appendable.append(text);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

@Override
public boolean isColorEnabled() {
return false;
}

@Override
public <T extends Appendable> void style(T appendable, Consumer<T> consumer, Style... styles) {
consumer.accept(appendable);
}
public void startStyle(Appendable appendable, Style... style) { }

@Override
public void endStyle(Appendable appendable) { }
},

/**
* Writes with ANSI colors.
*/
FORCE_COLOR {
@Override
public String style(String text, Style... styles) {
StringBuilder builder = new StringBuilder();
style(builder, text, styles);
return builder.toString();
}

@Override
public void style(Appendable appendable, String text, Style... styles) {
try {
startStyle(appendable, styles);
appendable.append(text);
} catch (IOException e) {
throw new UncheckedIOException(e);
} finally {
closeStyle(appendable, styles);
}
}

@Override
public boolean isColorEnabled() {
return true;
}

@Override
public <T extends Appendable> void style(T appendable, Consumer<T> consumer, Style... styles) {
try {
startStyle(appendable, styles);
consumer.accept(appendable);
} catch (IOException e) {
throw new UncheckedIOException(e);
} finally {
closeStyle(appendable, styles);
}
}

private void startStyle(Appendable appendable, Style... styles) throws IOException {
public void startStyle(Appendable appendable, Style... styles) {
if (styles.length > 0) {
appendable.append("\033[");
boolean isAfterFirst = false;
for (Style style : styles) {
if (isAfterFirst) {
appendable.append(';');
try {
appendable.append("\033[");
boolean isAfterFirst = false;
for (Style style : styles) {
if (isAfterFirst) {
appendable.append(';');
}
appendable.append(style.getAnsiColorCode());
isAfterFirst = true;
}
appendable.append(style.getAnsiColorCode());
isAfterFirst = true;
appendable.append('m');
} catch (IOException e) {
throw new UncheckedIOException(e);
}
appendable.append('m');
}
}

private void closeStyle(Appendable appendable, Style... styles) {
@Override
public void endStyle(Appendable appendable) {
try {
if (styles.length > 0) {
appendable.append("\033[0m");
}
appendable.append("\033[0m");
} catch (IOException e) {
throw new UncheckedIOException(e);
}
Expand All @@ -131,6 +90,11 @@ public String style(String text, Style... styles) {
return delegate.style(text, styles);
}

@Override
public void println(Appendable appendable, String text, Style... styles) {
delegate.println(appendable, text, styles);
}

@Override
public void style(Appendable appendable, String text, Style... styles) {
delegate.style(appendable, text, styles);
Expand All @@ -142,8 +106,13 @@ public boolean isColorEnabled() {
}

@Override
public <T extends Appendable> void style(T appendable, Consumer<T> consumer, Style... styles) {
delegate.style(appendable, consumer, styles);
public void startStyle(Appendable appendable, Style... style) {
delegate.startStyle(appendable, style);
}

@Override
public void endStyle(Appendable appendable) {
delegate.endStyle(appendable);
}
};

Expand Down
23 changes: 16 additions & 7 deletions smithy-cli/src/main/java/software/amazon/smithy/cli/Cli.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
Expand Down Expand Up @@ -111,10 +110,10 @@ public void stderr(CliPrinter stderrPrinter) {
}

private void printException(Throwable e, boolean stacktrace) {
if (!stacktrace) {
colorFormatter.println(stderrPrinter, e.getMessage(), Style.RED);
} else {
try (ColorFormatter.PrinterBuffer buffer = colorFormatter.printerBuffer(stderrPrinter)) {
try (ColorBuffer buffer = ColorBuffer.of(colorFormatter, stderrPrinter)) {
if (!stacktrace) {
colorFormatter.println(stderrPrinter, e.getMessage(), Style.RED);
} else {
StringWriter writer = new StringWriter();
e.printStackTrace(new PrintWriter(writer));
String result = writer.toString();
Expand Down Expand Up @@ -143,8 +142,18 @@ public boolean isColorEnabled() {
}

@Override
public <T extends Appendable> void style(T appendable, Consumer<T> consumer, Style... styles) {
delegateSupplier.get().style(appendable, consumer, styles);
public void println(Appendable appendable, String text, Style... styles) {
delegateSupplier.get().println(appendable, text, styles);
}

@Override
public void startStyle(Appendable appendable, Style... style) {
delegateSupplier.get().startStyle(appendable, style);
}

@Override
public void endStyle(Appendable appendable) {
delegateSupplier.get().endStyle(appendable);
}
};
}
Expand Down
86 changes: 55 additions & 31 deletions smithy-cli/src/main/java/software/amazon/smithy/cli/CliPrinter.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,43 @@

package software.amazon.smithy.cli;

import java.io.BufferedWriter;
import java.io.Flushable;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UncheckedIOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
* Handles text output of the CLI.
*/
@FunctionalInterface
public interface CliPrinter extends Flushable {
public interface CliPrinter extends Appendable, Flushable {

/**
* Prints text to the writer.
*
* @param text Text to write.
*/
void print(String text);
@Override
CliPrinter append(char c);

@Override
default CliPrinter append(CharSequence csq) {
return append(csq, 0, csq.length());
}

@Override
default CliPrinter append(CharSequence csq, int start, int end) {
for (int i = start; i < end; i++) {
append(csq.charAt(i));
}
return this;
}

/**
* Prints text to the writer and appends a new line.
*
* @param text Text to print.
*/
default void println(String text) {
print(text + System.lineSeparator());
default CliPrinter println(String text) {
return append(text + System.lineSeparator());
}

/**
Expand All @@ -51,39 +60,54 @@ default void println(String text) {
default void flush() {}

/**
* Create a new CliPrinter from a PrintWriter.
* Create a new CliPrinter from an OutputStream.
*
* @param printWriter PrintWriter to write to.
* @param stream OutputStream to write to.
* @return Returns the created CliPrinter.
*/
static CliPrinter fromPrintWriter(PrintWriter printWriter) {
static CliPrinter fromOutputStream(OutputStream stream) {
Charset charset = StandardCharsets.UTF_8;
OutputStreamWriter writer = new OutputStreamWriter(stream, charset);

return new CliPrinter() {
@Override
public void println(String text) {
printWriter.println(text);
public CliPrinter append(char c) {
try {
writer.append(c);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return this;
}

@Override
public void print(String text) {
printWriter.print(text);
public CliPrinter append(CharSequence csq) {
try {
writer.append(csq);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return this;
}

@Override
public CliPrinter append(CharSequence csq, int start, int end) {
try {
writer.append(csq, start, end);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return this;
}

@Override
public void flush() {
printWriter.flush();
try {
writer.flush();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
};
}

/**
* Create a new CliPrinter from an OutputStream.
*
* @param stream OutputStream to write to.
* @return Returns the created CliPrinter.
*/
static CliPrinter fromOutputStream(OutputStream stream) {
Charset charset = StandardCharsets.UTF_8;
PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(stream, charset)), false);
return fromPrintWriter(writer);
}
}
Loading

0 comments on commit 95f7c59

Please sign in to comment.