styling: introduce support for code blocks
This commit is contained in:
parent
2e3b5de6b6
commit
e9587f73ce
|
@ -35,19 +35,29 @@ import java.util.List;
|
||||||
|
|
||||||
public class ImStyleParser {
|
public class ImStyleParser {
|
||||||
|
|
||||||
final static List<Character> KEYWORDS = Arrays.asList('*', '_', '~', '`');
|
private final static List<Character> KEYWORDS = Arrays.asList('*', '_', '~', '`');
|
||||||
final static List<Character> NO_SUB_PARSING_KEYWORDS = Arrays.asList('`');
|
private final static List<Character> NO_SUB_PARSING_KEYWORDS = Arrays.asList('`');
|
||||||
final static boolean ALLOW_EMPTY = false;
|
private final static List<Character> BLOCK_KEYWORDS = Arrays.asList('`');
|
||||||
|
private final static boolean ALLOW_EMPTY = false;
|
||||||
|
|
||||||
public static List<Style> parse(CharSequence text) {
|
public static List<Style> parse(CharSequence text) {
|
||||||
return parse(text, 0, text.length() - 1);
|
return parse(text, 0, text.length() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Style> parse(CharSequence text, int start, int end) {
|
private static List<Style> parse(CharSequence text, int start, int end) {
|
||||||
List<Style> styles = new ArrayList<>();
|
List<Style> styles = new ArrayList<>();
|
||||||
for (int i = start; i <= end; ++i) {
|
for (int i = start; i <= end; ++i) {
|
||||||
char c = text.charAt(i);
|
char c = text.charAt(i);
|
||||||
if (KEYWORDS.contains(c) && precededByWhiteSpace(text, i, start) && !followedByWhitespace(text, i, end)) {
|
if (KEYWORDS.contains(c) && precededByWhiteSpace(text, i, start) && !followedByWhitespace(text, i, end)) {
|
||||||
|
if (BLOCK_KEYWORDS.contains(c) && isCharRepeatedTwoTimes(text, c, i + 1, end)) {
|
||||||
|
int to = seekEndBlock(text, c, i + 3, end);
|
||||||
|
if (to != -1 && (to != i + 5 || ALLOW_EMPTY)) {
|
||||||
|
String keyword = String.valueOf(c) + String.valueOf(c) + String.valueOf(c);
|
||||||
|
styles.add(new Style(keyword, i, to));
|
||||||
|
i = to;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
int to = seekEnd(text, c, i + 1, end);
|
int to = seekEnd(text, c, i + 1, end);
|
||||||
if (to != -1 && (to != i + 1 || ALLOW_EMPTY)) {
|
if (to != -1 && (to != i + 1 || ALLOW_EMPTY)) {
|
||||||
styles.add(new Style(c, i, to));
|
styles.add(new Style(c, i, to));
|
||||||
|
@ -61,6 +71,10 @@ public class ImStyleParser {
|
||||||
return styles;
|
return styles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isCharRepeatedTwoTimes(CharSequence text, char c, int index, int end) {
|
||||||
|
return index + 1 <= end && text.charAt(index) == c && text.charAt(index) == c;
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean precededByWhiteSpace(CharSequence text, int index, int start) {
|
private static boolean precededByWhiteSpace(CharSequence text, int index, int start) {
|
||||||
return index == start || Character.isWhitespace(text.charAt(index - 1));
|
return index == start || Character.isWhitespace(text.charAt(index - 1));
|
||||||
}
|
}
|
||||||
|
@ -81,20 +95,34 @@ public class ImStyleParser {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int seekEndBlock(CharSequence text, char needle, int start, int end) {
|
||||||
|
for (int i = start; i <= end; ++i) {
|
||||||
|
char c = text.charAt(i);
|
||||||
|
if (c == needle && isCharRepeatedTwoTimes(text, needle, i + 1, end)) {
|
||||||
|
return i + 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
public static class Style {
|
public static class Style {
|
||||||
|
|
||||||
private final char c;
|
private final String keyword;
|
||||||
private final int start;
|
private final int start;
|
||||||
private final int end;
|
private final int end;
|
||||||
|
|
||||||
public Style(char c, int start, int end) {
|
public Style(char character, int start, int end) {
|
||||||
this.c = c;
|
this(String.valueOf(character), start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Style(String keyword, int start, int end) {
|
||||||
|
this.keyword = keyword;
|
||||||
this.start = start;
|
this.start = start;
|
||||||
this.end = end;
|
this.end = end;
|
||||||
}
|
}
|
||||||
|
|
||||||
public char getCharacter() {
|
public String getKeyword() {
|
||||||
return c;
|
return keyword;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getStart() {
|
public int getStart() {
|
||||||
|
|
|
@ -67,21 +67,23 @@ public class StylingHelper {
|
||||||
|
|
||||||
public static void format(final Editable editable, @ColorInt int textColor) {
|
public static void format(final Editable editable, @ColorInt int textColor) {
|
||||||
for (ImStyleParser.Style style : ImStyleParser.parse(editable)) {
|
for (ImStyleParser.Style style : ImStyleParser.parse(editable)) {
|
||||||
editable.setSpan(createSpanForStyle(style), style.getStart() + 1, style.getEnd(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
final int keywordLength = style.getKeyword().length();
|
||||||
makeKeywordOpaque(editable, style.getStart(), style.getStart() + 1, textColor);
|
editable.setSpan(createSpanForStyle(style), style.getStart() + keywordLength, style.getEnd() - keywordLength + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
makeKeywordOpaque(editable, style.getEnd(), style.getEnd() + 1, textColor);
|
makeKeywordOpaque(editable, style.getStart(), style.getStart() + keywordLength, textColor);
|
||||||
|
makeKeywordOpaque(editable, style.getEnd() - keywordLength + 1, style.getEnd() + 1, textColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ParcelableSpan createSpanForStyle(ImStyleParser.Style style) {
|
private static ParcelableSpan createSpanForStyle(ImStyleParser.Style style) {
|
||||||
switch (style.getCharacter()) {
|
switch (style.getKeyword()) {
|
||||||
case '*':
|
case "*":
|
||||||
return new StyleSpan(Typeface.BOLD);
|
return new StyleSpan(Typeface.BOLD);
|
||||||
case '_':
|
case "_":
|
||||||
return new StyleSpan(Typeface.ITALIC);
|
return new StyleSpan(Typeface.ITALIC);
|
||||||
case '~':
|
case "~":
|
||||||
return new StrikethroughSpan();
|
return new StrikethroughSpan();
|
||||||
case '`':
|
case "`":
|
||||||
|
case "```":
|
||||||
return new TypefaceSpan("monospace");
|
return new TypefaceSpan("monospace");
|
||||||
default:
|
default:
|
||||||
throw new AssertionError("Unknown Style");
|
throw new AssertionError("Unknown Style");
|
||||||
|
|
Loading…
Reference in New Issue