Skip to content
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
484 changes: 318 additions & 166 deletions twinkle-ansi/src/main/java/org/codejive/twinkle/ansi/Style.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ private void primeNext() {
if (cp == Ansi.ESC) {
String ansiSequence = delegate.sequence();
if (ansiSequence.startsWith(Ansi.CSI) && ansiSequence.endsWith("m")) {
currentStyle = Style.of(Style.parse(currentStyle.state(), ansiSequence));
currentStyle = currentStyle.apply(Style.parse(ansiSequence));
}
} else {
nextCodePoint = cp;
Expand Down
146 changes: 136 additions & 10 deletions twinkle-ansi/src/test/java/org/codejive/twinkle/ansi/TestStyle.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
public class TestStyle {
@Test
public void testStyleCreation() {
Style style1 = Style.of(Style.F_BOLD);
Style style1 = Style.UNSTYLED.bold();
Style style2 = Style.BOLD;

assertThat(style1).isEqualTo(style2);
Expand All @@ -30,6 +30,14 @@ public void testStyleCombination() {
.inverse()
.hidden()
.strikethrough();
assertThat(style.affectsBold()).isTrue();
assertThat(style.affectsFaint()).isTrue();
assertThat(style.affectsItalic()).isTrue();
assertThat(style.affectsUnderlined()).isTrue();
assertThat(style.affectsBlink()).isTrue();
assertThat(style.affectsInverse()).isTrue();
assertThat(style.affectsHidden()).isTrue();
assertThat(style.affectsStrikethrough()).isTrue();
assertThat(style.isBold()).isTrue();
assertThat(style.isFaint()).isTrue();
assertThat(style.isItalic()).isTrue();
Expand All @@ -47,7 +55,56 @@ public void testStyleCombination() {
.inverseOff()
.hiddenOff()
.strikethroughOff();
assertThat(style).isEqualTo(Style.UNSTYLED);
assertThat(style.affectsBold()).isTrue();
assertThat(style.affectsFaint()).isTrue();
assertThat(style.affectsItalic()).isTrue();
assertThat(style.affectsUnderlined()).isTrue();
assertThat(style.affectsBlink()).isTrue();
assertThat(style.affectsInverse()).isTrue();
assertThat(style.affectsHidden()).isTrue();
assertThat(style.affectsStrikethrough()).isTrue();
assertThat(style.isBold()).isFalse();
assertThat(style.isFaint()).isFalse();
assertThat(style.isItalic()).isFalse();
assertThat(style.isUnderlined()).isFalse();
assertThat(style.isBlink()).isFalse();
assertThat(style.isInverse()).isFalse();
assertThat(style.isHidden()).isFalse();
assertThat(style.isStrikethrough()).isFalse();
}

@Test
public void testUnsetStyle() {
Style style = Style.UNSTYLED.underlinedOff();
assertThat(style.affectsUnderlined()).isTrue();
assertThat(style.isUnderlined()).isFalse();
assertThat(style.toAnsiString()).isEqualTo(Ansi.style(Ansi.NOTUNDERLINED));
}

@Test
public void testUnsetStyleAnd() {
Style style1 = Style.UNSTYLED.blink().underlined();
Style style2 = Style.UNSTYLED.underlinedOff();

Style style3 = style1.and(style2);

assertThat(style3.affectsBlink()).isTrue();
assertThat(style3.isBlink()).isTrue();
assertThat(style3.affectsUnderlined()).isTrue();
assertThat(style3.isUnderlined()).isFalse();
}

@Test
public void testUnsetStyleApply() {
Style style1 = Style.UNSTYLED.blink().underlined();
Style style2 = Style.UNSTYLED.underlinedOff();

Style style3 = style1.apply(style2);

assertThat(style3.affectsBlink()).isTrue();
assertThat(style3.isBlink()).isTrue();
assertThat(style3.affectsUnderlined()).isFalse();
assertThat(style3.isUnderlined()).isFalse();
}

@Test
Expand Down Expand Up @@ -91,19 +148,48 @@ public void testMixedStyles() {
.blink()
.inverse()
.hidden()
.strikethrough();
style = style.fgColor(Color.BasicColor.BLUE);
style = style.bgColor(Color.indexed(128));
.strikethrough()
.fgColor(Color.BasicColor.BLUE)
.bgColor(Color.indexed(128));
style =
style.normal()
.italicOff()
.underlinedOff()
.blinkOff()
.inverseOff()
.hiddenOff()
.strikethroughOff();
style = style.fgColor(Color.DEFAULT);
style = style.bgColor(Color.DEFAULT);
.strikethroughOff()
.fgColor(Color.DEFAULT)
.bgColor(Color.DEFAULT);
assertThat(style).isEqualTo(Style.DEFAULT);
}

@Test
public void testMixedStylesApply() {
Style style =
Style.UNSTYLED
.bold()
.faint()
.italic()
.underlined()
.blink()
.inverse()
.hidden()
.strikethrough()
.fgColor(Color.BasicColor.BLUE)
.bgColor(Color.indexed(128));
style =
style.apply(
Style.UNSTYLED
.normal()
.italicOff()
.underlinedOff()
.blinkOff()
.inverseOff()
.hiddenOff()
.strikethroughOff()
.fgColor(Color.DEFAULT)
.bgColor(Color.DEFAULT));
assertThat(style).isEqualTo(Style.UNSTYLED);
}

Expand Down Expand Up @@ -140,6 +226,32 @@ public void testToAnsiStringAllStyles() {
Ansi.CROSSEDOUT));
}

@Test
public void testToAnsiStringAllStylesWithDefault() {
Style style =
Style.UNSTYLED
.bold()
.faint()
.italic()
.underlined()
.blink()
.inverse()
.hidden()
.strikethrough();
String ansiCode = style.toAnsiString(Style.DEFAULT);
assertThat(ansiCode)
.isEqualTo(
Ansi.style(
Ansi.BOLD,
Ansi.FAINT,
Ansi.ITALICIZED,
Ansi.UNDERLINED,
Ansi.BLINK,
Ansi.INVERSE,
Ansi.INVISIBLE,
Ansi.CROSSEDOUT));
}

@Test
public void testToAnsiStringAllStylesWithCurrent() {
Style style =
Expand All @@ -157,8 +269,6 @@ public void testToAnsiStringAllStylesWithCurrent() {
assertThat(ansiCode)
.isEqualTo(
Ansi.style(
Ansi.NORMAL,
Ansi.BOLD,
Ansi.FAINT,
Ansi.ITALICIZED,
Ansi.BLINK,
Expand Down Expand Up @@ -190,4 +300,20 @@ public void testToAnsiStringAllStylesWithCurrent2() {
Ansi.INVISIBLE,
Ansi.CROSSEDOUT));
}

@Test
public void testToAnsiStringNormal() {
Style style = Style.UNSTYLED.faint();
Style currentStyle = Style.UNSTYLED.bold();
String ansiCode = style.toAnsiString(currentStyle);
assertThat(ansiCode).isEqualTo(Ansi.style(Ansi.NORMAL, Ansi.FAINT));
}

@Test
public void testToAnsiStringNoNormal() {
Style style = Style.UNSTYLED.bold().faint();
Style currentStyle = Style.UNSTYLED.bold();
String ansiCode = style.toAnsiString(currentStyle);
assertThat(ansiCode).isEqualTo(Ansi.style(Ansi.FAINT));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.assertj.core.api.Assertions.assertThat;

import org.codejive.twinkle.ansi.Ansi;
import org.codejive.twinkle.ansi.Style;
import org.junit.jupiter.api.Test;

Expand Down Expand Up @@ -30,7 +31,7 @@ public void testPlainSequence() {

@Test
public void testStyledSequence() {
String red = "\u001B[31m";
String red = Ansi.CSI + "31m";
SequenceIterator seqIter = SequenceIterator.of(red + "a");
StyledIterator it = new StyledIterator(seqIter);

Expand All @@ -47,7 +48,7 @@ public void testStyledSequence() {

@Test
public void testSkipNonStyleAnsi() {
String up = "\u001B[1A"; // Cursor Up
String up = Ansi.cursorMove(Ansi.CURSOR_UP);
SequenceIterator seqIter = SequenceIterator.of(up + "a");
StyledIterator it = new StyledIterator(seqIter);

Expand All @@ -61,8 +62,8 @@ public void testSkipNonStyleAnsi() {

@Test
public void testMixedSequences() {
String red = "\u001B[31m";
String reset = "\u001B[0m";
String red = Ansi.CSI + "31m";
String reset = Ansi.STYLE_RESET;
SequenceIterator seqIter = SequenceIterator.of("a" + red + "b" + reset + "c");
StyledIterator it = new StyledIterator(seqIter);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static org.codejive.twinkle.core.text.LineBuffer.REPLACEMENT_CHAR;

import java.io.IOException;
import org.codejive.twinkle.ansi.Ansi;
import org.codejive.twinkle.ansi.Style;
import org.codejive.twinkle.core.util.Rect;
import org.codejive.twinkle.core.util.Size;
Expand Down Expand Up @@ -245,6 +246,10 @@ public String toString() {
@Override
public @NonNull Appendable toAnsi(Appendable appendable, Style currentStyle)
throws IOException {
if (currentStyle == Style.UNKNOWN) {
currentStyle = Style.DEFAULT;
appendable.append(Ansi.STYLE_RESET);
}
for (int y = 0; y < size().height(); y++) {
line(y).toAnsi(appendable, currentStyle);
currentStyle = line(y).styleAt(size().width() - 1);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.codejive.twinkle.core.text;

import java.io.IOException;
import org.codejive.twinkle.ansi.Ansi;
import org.codejive.twinkle.ansi.Style;
import org.codejive.twinkle.util.Printable;
import org.codejive.twinkle.util.StyledIterator;
Expand Down Expand Up @@ -132,9 +133,7 @@ private void setCharAt_(int index, @NonNull Style style, char ch) {
// TODO log warning about surrogate characters not being supported
ch = REPLACEMENT_CHAR;
}
cpBuffer[index] = ch;
graphemeBuffer[index] = null;
styleBuffer[index] = style.state();
setCharAt_(index, style.state(), ch, null);
}

@Override
Expand All @@ -146,9 +145,7 @@ public void setCharAt(int index, @NonNull Style style, int cp) {
}

private void setCharAt_(int index, @NonNull Style style, int cp) {
cpBuffer[index] = cp;
graphemeBuffer[index] = null;
styleBuffer[index] = style.state();
setCharAt_(index, style.state(), cp, null);
}

@Override
Expand All @@ -163,9 +160,21 @@ private void setCharAt_(int index, @NonNull Style style, @NonNull CharSequence g
if (grapheme.length() == 0) {
return;
}
cpBuffer[index] = -1;
graphemeBuffer[index] = grapheme.toString();
styleBuffer[index] = style.state();
setCharAt_(index, style.state(), -1, grapheme.toString());
}

private boolean shouldSkipAt(int index) {
return cpBuffer[index] == -1 && graphemeBuffer[index] == null && styleBuffer[index] == -1;
}

private void setSkipAt(int index) {
setCharAt_(index, -1, -1, null);
}

private void setCharAt_(int index, long styleState, int cp, String grapheme) {
cpBuffer[index] = cp;
graphemeBuffer[index] = grapheme;
styleBuffer[index] = styleState;
}

@Override
Expand Down Expand Up @@ -213,12 +222,6 @@ public int putStringAt(int index, @NonNull StyledIterator iter) {
return cnt;
}

private void setSkipAt(int index) {
cpBuffer[index] = -1;
graphemeBuffer[index] = null;
styleBuffer[index] = -1;
}

@Override
public @NonNull LineBufferImpl subSequence(int start, int end) {
if (start < 0 || end > length() || start > end) {
Expand Down Expand Up @@ -296,6 +299,9 @@ private boolean outside(int index, int length) {
int initialCapacity = length();
StringBuilder sb = new StringBuilder(initialCapacity);
for (int i = 0; i < length(); i++) {
if (shouldSkipAt(i)) {
continue;
}
if (graphemeBuffer[i] != null) {
sb.append(graphemeBuffer[i]);
} else {
Expand Down Expand Up @@ -325,7 +331,14 @@ private boolean outside(int index, int length) {
@Override
public @NonNull Appendable toAnsi(Appendable appendable, Style currentStyle)
throws IOException {
if (currentStyle == Style.UNKNOWN) {
currentStyle = Style.DEFAULT;
appendable.append(Ansi.STYLE_RESET);
}
for (int i = 0; i < length(); i++) {
if (shouldSkipAt(i)) {
continue;
}
if (styleBuffer[i] != currentStyle.state()) {
Style style = Style.of(styleBuffer[i]);
style.toAnsi(appendable, currentStyle);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public String toString() {
@Override
public @NonNull Appendable toAnsi(Appendable appendable, Style currentStyle)
throws IOException {
style.toAnsi(appendable, currentStyle);
currentStyle.diff(style).toAnsi(appendable, currentStyle);
appendable.append(text);
return appendable;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public void render(Canvas canvas) {
}
if (title != null) {
Canvas view = canvas.view(2, 0, canvas.size().width() - 4, 1);
view.putStringAt(0, 0, Style.UNKNOWN, title.toAnsiString());
view.putStringAt(0, 0, Style.DEFAULT, title.toAnsiString());
}
if (widget != null) {
widget.render(canvas.view(Rect.of(canvas.size()).grow(-1, -1, -1, -1)));
Expand Down
Loading
Loading