diff --git a/twinkle-ansi/src/main/java/org/codejive/twinkle/ansi/Ansi.java b/twinkle-ansi/src/main/java/org/codejive/twinkle/ansi/Ansi.java
index a00b59b..8cb2b35 100644
--- a/twinkle-ansi/src/main/java/org/codejive/twinkle/ansi/Ansi.java
+++ b/twinkle-ansi/src/main/java/org/codejive/twinkle/ansi/Ansi.java
@@ -1,5 +1,7 @@
package org.codejive.twinkle.ansi;
+import java.io.IOException;
+
public class Ansi {
public static final char ESC = '\u001B';
@@ -68,22 +70,26 @@ public static String style(Object... styles) {
if (styles == null || styles.length == 0) {
return "";
}
- return style(new StringBuilder(), styles).toString();
+ try {
+ return style(new StringBuilder(), styles).toString();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
}
- public static StringBuilder style(StringBuilder sb, Object... styles) {
+ public static Appendable style(Appendable appendable, Object... styles) throws IOException {
if (styles == null || styles.length == 0) {
- return sb;
+ return appendable;
}
- sb.append(CSI);
+ appendable.append(CSI);
for (int i = 0; i < styles.length; i++) {
- sb.append(styles[i]);
+ appendable.append(styles[i].toString());
if (i < styles.length - 1) {
- sb.append(";");
+ appendable.append(";");
}
}
- sb.append("m");
- return sb;
+ appendable.append("m");
+ return appendable;
}
public static String foreground(int index) {
diff --git a/twinkle-ansi/src/main/java/org/codejive/twinkle/ansi/Printable.java b/twinkle-ansi/src/main/java/org/codejive/twinkle/ansi/Printable.java
new file mode 100644
index 0000000..dd2c3fb
--- /dev/null
+++ b/twinkle-ansi/src/main/java/org/codejive/twinkle/ansi/Printable.java
@@ -0,0 +1,70 @@
+package org.codejive.twinkle.ansi;
+
+import java.io.IOException;
+import org.jspecify.annotations.NonNull;
+
+public interface Printable {
+ /**
+ * Converts the object to an ANSI string, including ANSI escape codes for styles. This method
+ * resets the current style to default at the start of the string.
+ *
+ * @return The ANSI string representation of the object.
+ */
+ @NonNull String toAnsiString();
+
+ /**
+ * Outputs the object as an ANSI string, including ANSI escape codes for styles. This method
+ * resets the current style to default at the start of the output.
+ *
+ * @param appendable The Appendable to write the ANSI output to.
+ * @return The Appendable passed as parameter.
+ */
+ @NonNull Appendable toAnsi(Appendable appendable) throws IOException;
+
+ /**
+ * Converts the object to an ANSI string, including ANSI escape codes for styles. This method
+ * takes into account the provided current style to generate a result that is as efficient as
+ * possible in terms of ANSI codes.
+ *
+ * @param currentStyle The current style to start with.
+ * @return The ANSI string representation of the object.
+ */
+ default @NonNull String toAnsiString(Style currentStyle) {
+ return toAnsiString(currentStyle.state());
+ }
+
+ /**
+ * Outputs the object as an ANSI string, including ANSI escape codes for styles. This method
+ * takes into account the provided current style to generate a result that is as efficient as
+ * possible in terms of ANSI codes.
+ *
+ * @param appendable The Appendable to write the ANSI output to.
+ * @param currentStyle The current style to start with.
+ * @return The Appendable passed as parameter.
+ */
+ default @NonNull Appendable toAnsi(Appendable appendable, Style currentStyle)
+ throws IOException {
+ return toAnsi(appendable, currentStyle.state());
+ }
+
+ /**
+ * Converts the object to an ANSI string, including ANSI escape codes for styles. This method
+ * takes into account the provided current style to generate a result that is as efficient as
+ * possible in terms of ANSI codes.
+ *
+ * @param currentStyleState The current style to start with.
+ * @return The ANSI string representation of the object.
+ */
+ @NonNull String toAnsiString(long currentStyleState);
+
+ /**
+ * Outputs the object as an ANSI string, including ANSI escape codes for styles. This method
+ * takes into account the provided current style to generate a result that is as efficient as
+ * possible in terms of ANSI codes.
+ *
+ * @param appendable The Appendable to write the ANSI output to.
+ * @param currentStyleState The current style to start with.
+ * @return The Appendable passed as parameter.
+ */
+ @NonNull Appendable toAnsi(Appendable appendable, long currentStyleState) throws IOException;
+}
diff --git a/twinkle-ansi/src/main/java/org/codejive/twinkle/ansi/Style.java b/twinkle-ansi/src/main/java/org/codejive/twinkle/ansi/Style.java
index 843eb0f..80a78f8 100644
--- a/twinkle-ansi/src/main/java/org/codejive/twinkle/ansi/Style.java
+++ b/twinkle-ansi/src/main/java/org/codejive/twinkle/ansi/Style.java
@@ -1,10 +1,11 @@
package org.codejive.twinkle.ansi;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.jspecify.annotations.NonNull;
-public class Style {
+public class Style implements Printable {
private final long state;
private static final long IDX_BOLD = 0;
@@ -347,27 +348,32 @@ public String toString() {
return sb.toString();
}
- public String toAnsiString() {
+ @Override
+ public @NonNull String toAnsiString() {
return toAnsiString(UNSTYLED);
}
- public StringBuilder toAnsiString(StringBuilder sb) {
- return toAnsiString(sb, Style.UNSTYLED);
- }
-
- public String toAnsiString(Style currentStyle) {
- return toAnsiString(currentStyle.state());
- }
-
- public StringBuilder toAnsiString(StringBuilder sb, Style currentStyle) {
- return toAnsiString(sb, currentStyle.state());
+ @Override
+ public @NonNull Appendable toAnsi(Appendable appendable) throws IOException {
+ try {
+ return toAnsi(appendable, Style.UNSTYLED);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
}
- public String toAnsiString(long currentStyleState) {
- return toAnsiString(new StringBuilder(), currentStyleState).toString();
+ @Override
+ public @NonNull String toAnsiString(long currentStyleState) {
+ try {
+ return toAnsi(new StringBuilder(), currentStyleState).toString();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
}
- public StringBuilder toAnsiString(StringBuilder sb, long currentStyleState) {
+ @Override
+ public @NonNull Appendable toAnsi(Appendable appendable, long currentStyleState)
+ throws IOException {
List