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
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public static void main(String[] args) throws Exception {
Fluent f = writer.fluent();
f.at(2, 0).markup("{green}[ {white}%s{green} ]", size);
f.at(size.width() / 2 - 3, 0)
.markup("{green}[ {blue}{ul}{%s}Twinkle{/}{/ul}{green} ]", URL);
.markup("{green}[ {blue}{ul}{$1}Twinkle{/}{/ul}{green} ]", URL);
f.at(size.width() - 12, 0)
.markup("{green}[ {+}{white}fps %s{-} ]", Math.round(fps.average()));
f.at(textX, textY).color(textColor).text(text).done();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
/** An interface for possible markup parser implementations that can be used for the `Fluent` */
public interface MarkupParser {

void parse(Fluent fluent, String textWithMarkup);
void parse(Fluent fluent, String textWithMarkup, Object... args);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.codejive.twinkle.fluent.impl;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.codejive.twinkle.ansi.Color;
Expand All @@ -20,30 +22,23 @@
public class DefaultMarkupParser implements MarkupParser {
private static Map<String, Color.BasicColor> colors;

private static final Pattern markupPattern = Pattern.compile("(?<!\\{)\\{([^{}]*)}");
private static final Pattern markupPattern = Pattern.compile("(?<!\\{)\\{([^{}]+)}");
private static final Pattern varPattern = Pattern.compile("(?<!\\$)\\$(/?\\d+)");

@Override
public void parse(Fluent fluent, String textWithMarkup) {
public void parse(Fluent fluent, String textWithMarkup, Object... args) {
// Call handleMarkup() for each markup pattern found in the text
int lastIndex = 0;
Matcher matcher = markupPattern.matcher(textWithMarkup);
while (matcher.find()) {
// Append text before the markup
if (matcher.start() > lastIndex) {
append(fluent, textWithMarkup.substring(lastIndex, matcher.start()));
}
// Handle the markup content
String markupContent = matcher.group(1);
handleMarkup(fluent, markupContent);
lastIndex = matcher.end();
}
// Append any remaining text after the last markup
if (lastIndex < textWithMarkup.length()) {
append(fluent, textWithMarkup.substring(lastIndex, textWithMarkup.length()));
}
applyPattern(
fluent::plain,
markupPattern,
"{",
textWithMarkup,
markup -> handleMarkup(fluent, markup, args));
}

protected void handleMarkup(Fluent fluent, String markup) {
protected void handleMarkup(Fluent fluent, String markup, Object... args) {
markup = applySubstitutions(markup, args);

if (tryStyles(fluent, markup)) {
return;
}
Expand Down Expand Up @@ -89,6 +84,48 @@ protected void handleMarkup(Fluent fluent, String markup) {
}
}

private String applySubstitutions(String markup, Object... args) {
if (markup.contains("$")) {
StringBuilder sb = new StringBuilder();
applyPattern(sb::append, varPattern, "$", markup, vn -> applyVar(sb, vn, args));
return sb.toString();
}
return markup;
}

protected void applyVar(Appendable appendable, String varName, Object... args) {
try {
if (varName.startsWith("/")) {
appendable.append("/").append(getVar(varName.substring(1), args));
} else {
appendable.append(getVar(varName, args));
}
} catch (IOException e) {
// Ignore
}
}

protected String getVar(String varName, Object... args) {
try {
int num = Integer.parseInt(varName) - 1;
if (num >= args.length) {
throw new IndexOutOfBoundsException(
"Variable substitution index out of bounds: "
+ num
+ " is larger than the number of provided arguments: "
+ args.length);
}
if (num < 0) {
throw new IndexOutOfBoundsException(
"Variable substitution index must be 1 or higher");
}
return String.valueOf(args[num]);
} catch (NumberFormatException e) {
// Ignore
}
return "";
}

private boolean tryStyles(NegatableCommands fluentNeg, String markup) {
switch (markup.toLowerCase()) {
case "bold":
Expand Down Expand Up @@ -281,11 +318,31 @@ private Color tryColorByName(String markup) {
return colors.get(lmarkup);
}

protected void append(Fluent fluent, String text) {
try {
fluent.plain(text);
} catch (Exception e) {
// We simply ignore errors
protected void applyPattern(
Consumer<String> appendable,
Pattern pattern,
String token,
String textWithMarkup,
Consumer<String> handler) {
int lastIndex = 0;
Matcher matcher = pattern.matcher(textWithMarkup);
while (matcher.find()) {
// Append text before the markup
if (matcher.start() > lastIndex) {
String txt = textWithMarkup.substring(lastIndex, matcher.start());
txt = txt.replace(token + token, token);
appendable.accept(txt);
}
// Handle the markup content
String markupContent = matcher.group(1);
handler.accept(markupContent);
lastIndex = matcher.end();
}
// Append any remaining text after the last markup
if (lastIndex < textWithMarkup.length()) {
String txt = textWithMarkup.substring(lastIndex);
txt = txt.replace(token + token, token);
appendable.accept(txt);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public FluentImpl plain(@NonNull String text) {
public Fluent markup(@NonNull Object obj, Object... args) {
StringBuilder sb = new StringBuilder();
FluentImpl f = new FluentImpl(sb, currentStyle);
f.markupParser.parse(f, obj.toString());
f.markupParser.parse(f, obj.toString(), args);

Formatter fmt = new Formatter(appendable);
fmt.format(sb.toString(), args);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ private static class Setup {
final FluentImpl fluent = FluentImpl.of(sb, Style.DEFAULT);
final DefaultMarkupParser markup = new DefaultMarkupParser();

void parse(String text) {
markup.parse(fluent, text);
void parse(String text, Object... args) {
markup.parse(fluent, text, args);
}

String result() {
Expand Down Expand Up @@ -441,4 +441,11 @@ public void testTextWithFormatting() {
s.parse("{i}%s{/i}");
assertThat(s.result()).isEqualTo(Ansi.italic() + "%s" + Ansi.italicOff());
}

@Test
public void testTextWithArgumentInMarkup() {
Setup s = new Setup();
s.parse("{i}{$1}{/i}", "bold");
assertThat(s.result()).isEqualTo(Ansi.italic() + Ansi.bold() + Ansi.italicOff());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,23 @@ public void testFormatMarkupOrdering() {
assertThat(stringFluent.toString()).isEqualTo("");
}

@Test
public void testTextWithFormattingInMarkup() {
Fluent stringFluent = Fluent.string();
// Markup is invalid so will be ignored (removed)
stringFluent.markup("{i}{%s}{/i}", "bold");
assertThat(stringFluent.toString()).isEqualTo(Ansi.italic() + Ansi.italicOff());
}

@Test
public void testTextWithArgumentInMarkup() {
Fluent stringFluent = Fluent.string();
// Markup is invalid so will be ignored (removed)
stringFluent.markup("{i}{$1}{/i}", "bold");
assertThat(stringFluent.toString())
.isEqualTo(Ansi.italic() + Ansi.bold() + Ansi.italicOff());
}

@Test
public void testMarkupAppendable() {
RecordingAppendable recapp = new RecordingAppendable();
Expand Down
Loading