diff --git a/library-syntax/src/main/java/ru/noties/markwon/syntax/Prism4jThemeBase.java b/library-syntax/src/main/java/ru/noties/markwon/syntax/Prism4jThemeBase.java new file mode 100644 index 00000000..77428b7b --- /dev/null +++ b/library-syntax/src/main/java/ru/noties/markwon/syntax/Prism4jThemeBase.java @@ -0,0 +1,140 @@ +package ru.noties.markwon.syntax; + +import android.support.annotation.ColorInt; +import android.support.annotation.FloatRange; +import android.support.annotation.IntRange; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.style.ForegroundColorSpan; + +import java.util.HashMap; + +import ru.noties.markwon.SpannableBuilder; +import ru.noties.prism4j.Prism4j; + +public abstract class Prism4jThemeBase implements Prism4jTheme { + + @ColorInt + protected static int applyAlpha(@IntRange(from = 0, to = 255) int alpha, @ColorInt int color) { + return (color & 0x00FFFFFF) | (alpha << 24); + } + + @ColorInt + protected static int applyAlpha(@FloatRange(from = .0F, to = 1.F) float alpha, @ColorInt int color) { + return applyAlpha((int) (255 * alpha + .5F), color); + } + + protected static boolean isOfType(@NonNull String expected, @NonNull String type, @Nullable String alias) { + return expected.equals(type) || expected.equals(alias); + } + + private final ColorHashMap colorHashMap; + + protected Prism4jThemeBase() { + this.colorHashMap = init(); + } + + @NonNull + protected abstract ColorHashMap init(); + + @ColorInt + protected int color(@NonNull String language, @NonNull String type, @Nullable String alias) { + + Color color = colorHashMap.get(type); + if (color == null + && alias != null) { + color = colorHashMap.get(alias); + } + + return color != null + ? color.color + : 0; + } + + @Override + public void apply( + @NonNull String language, + @NonNull Prism4j.Syntax syntax, + @NonNull SpannableBuilder builder, + int start, + int end) { + + final String type = syntax.type(); + final String alias = syntax.alias(); + + final int color = color(language, type, alias); + if (color != 0) { + builder.setSpan(new ForegroundColorSpan(color), start, end); + applyColor(language, type, alias, color, builder, start, end); + } + } + + @SuppressWarnings("unused") + protected void applyColor( + @NonNull String language, + @NonNull String type, + @Nullable String alias, + @ColorInt int color, + @NonNull SpannableBuilder builder, + int start, + int end) { + builder.setSpan(new ForegroundColorSpan(color), start, end); + } + + protected static class Color { + + @NonNull + public static Color of(@ColorInt int color) { + return new Color(color); + } + + @ColorInt + protected final int color; + + protected Color(@ColorInt int color) { + this.color = color; + } + } + + protected static class ColorHashMap extends HashMap { + + @NonNull + protected ColorHashMap add(@ColorInt int color, String name) { + put(name, Color.of(color)); + return this; + } + + @NonNull + protected ColorHashMap add( + @ColorInt int color, + @NonNull String name1, + @NonNull String name2) { + final Color c = Color.of(color); + put(name1, c); + put(name2, c); + return this; + } + + @NonNull + protected ColorHashMap add( + @ColorInt int color, + @NonNull String name1, + @NonNull String name2, + @NonNull String name3) { + final Color c = Color.of(color); + put(name1, c); + put(name2, c); + put(name3, c); + return this; + } + + @NonNull + protected ColorHashMap add(@ColorInt int color, String... names) { + final Color c = Color.of(color); + for (String name : names) { + put(name, c); + } + return this; + } + } +} diff --git a/library-syntax/src/main/java/ru/noties/markwon/syntax/Prism4jThemeDefault.java b/library-syntax/src/main/java/ru/noties/markwon/syntax/Prism4jThemeDefault.java new file mode 100644 index 00000000..2099842e --- /dev/null +++ b/library-syntax/src/main/java/ru/noties/markwon/syntax/Prism4jThemeDefault.java @@ -0,0 +1,64 @@ +package ru.noties.markwon.syntax; + +import android.support.annotation.ColorInt; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.style.BackgroundColorSpan; + +import ru.noties.markwon.SpannableBuilder; +import ru.noties.markwon.spans.EmphasisSpan; +import ru.noties.markwon.spans.StrongEmphasisSpan; + +public class Prism4jThemeDefault extends Prism4jThemeBase { + + @NonNull + public static Prism4jThemeDefault create() { + return new Prism4jThemeDefault(); + } + + @NonNull + @Override + protected ColorHashMap init() { + return new ColorHashMap() + .add(0xFF708090, "comment", "prolog", "doctype", "cdata") + .add(0xFF999999, "punctuation") + .add(0xFF990055, "property", "tag", "boolean", "number", "constant", "symbol", "deleted") + .add(0xFF669900, "selector", "attr-name", "string", "char", "builtin", "inserted") + .add(0xFF9a6e3a, "operator", "entity", "url") + .add(0xFF0077aa, "atrule", "attr-value", "keyword") + .add(0xFFDD4A68, "function", "class-name") + .add(0xFFee9900, "regex", "important", "variable"); + } + + @Override + protected void applyColor( + @NonNull String language, + @NonNull String type, + @Nullable String alias, + @ColorInt int color, + @NonNull SpannableBuilder builder, + int start, + int end) { + + if ("css".equals(language) && isOfType("string", type, alias)) { + super.applyColor(language, type, alias, 0xFF9a6e3a, builder, start, end); + builder.setSpan(new BackgroundColorSpan(0x80ffffff), start, end); + return; + } + + if (isOfType("namespace", type, alias)) { + color = applyAlpha(.7F, color); + } + + super.applyColor(language, type, alias, color, builder, start, end); + + if (isOfType("important", type, alias) + || isOfType("bold", type, alias)) { + builder.setSpan(new StrongEmphasisSpan(), start, end); + } + + if (isOfType("italic", type, alias)) { + builder.setSpan(new EmphasisSpan(), start, end); + } + } +}