diff --git a/build.gradle b/build.gradle index 6c9dc4a8..be941d6f 100644 --- a/build.gradle +++ b/build.gradle @@ -10,6 +10,9 @@ buildscript { allprojects { repositories { + if (project.hasProperty('LOCAL_MAVEN_URL')) { + maven { url LOCAL_MAVEN_URL } + } jcenter() google() } @@ -46,4 +49,7 @@ ext { ANDROID_SVG = 'com.caverock:androidsvg:1.2.1' ANDROID_GIF = 'pl.droidsonroids.gif:android-gif-drawable:1.2.14' OK_HTTP = 'com.squareup.okhttp3:okhttp:3.9.0' + + PRISM_4J = 'ru.noties:prism4j:1.0.0' + PRISM_4J_BUNDLER = 'ru.noties:prism4j-bundler:1.0.0' } diff --git a/library-syntax/build.gradle b/library-syntax/build.gradle new file mode 100644 index 00000000..ea8156d0 --- /dev/null +++ b/library-syntax/build.gradle @@ -0,0 +1,24 @@ +apply plugin: 'com.android.library' + +android { + + compileSdkVersion TARGET_SDK + buildToolsVersion BUILD_TOOLS + + defaultConfig { + minSdkVersion MIN_SDK + targetSdkVersion TARGET_SDK + versionCode 1 + versionName version + } +} + +dependencies { + api SUPPORT_ANNOTATIONS + api PRISM_4J + api project(':library') +} + +afterEvaluate { + generateReleaseBuildConfig.enabled = false +} diff --git a/library-syntax/src/main/AndroidManifest.xml b/library-syntax/src/main/AndroidManifest.xml new file mode 100644 index 00000000..e758e4c1 --- /dev/null +++ b/library-syntax/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/library-syntax/src/main/java/ru/noties/markwon/syntax/Prism4jSyntaxHighlight.java b/library-syntax/src/main/java/ru/noties/markwon/syntax/Prism4jSyntaxHighlight.java new file mode 100644 index 00000000..adfb81dd --- /dev/null +++ b/library-syntax/src/main/java/ru/noties/markwon/syntax/Prism4jSyntaxHighlight.java @@ -0,0 +1,105 @@ +package ru.noties.markwon.syntax; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; + +import ru.noties.markwon.SpannableBuilder; +import ru.noties.markwon.SyntaxHighlight; +import ru.noties.prism4j.Prism4j; + +public class Prism4jSyntaxHighlight implements SyntaxHighlight { + + @NonNull + public static Prism4jSyntaxHighlight create( + @NonNull Prism4j prism4j, + @NonNull Prism4jTheme theme) { + return null; + } + + @NonNull + public static Prism4jSyntaxHighlight create( + @NonNull Prism4j prism4j, + @NonNull Prism4jTheme theme, + @Nullable String fallback) { + return null; + } + + private final Prism4j prism4j; + private final Prism4jTheme theme; + private final String fallback; + + protected Prism4jSyntaxHighlight( + @NonNull Prism4j prism4j, + @NonNull Prism4jTheme theme, + @Nullable String fallback) { + this.prism4j = prism4j; + this.theme = theme; + this.fallback = fallback; + } + + @NonNull + @Override + public CharSequence highlight(@Nullable String info, @NonNull String code) { + // if info is null, do not highlight -> LICENCE footer very commonly wrapped inside code + // block without syntax name specified (so, do not highlight) + return info == null + ? highlightNoLanguageInfo(code) + : highlightWithLanguageInfo(info, code); + } + + @NonNull + protected CharSequence highlightNoLanguageInfo(@NonNull String code) { + return code; + } + + @NonNull + protected CharSequence highlightWithLanguageInfo(@NonNull String info, @NonNull String code) { + + final CharSequence out; + + final String language; + final Prism4j.Grammar grammar; + { + String _language = info; + Prism4j.Grammar _grammar = prism4j.grammar(info); + if (_grammar == null && !TextUtils.isEmpty(fallback)) { + _language = fallback; + _grammar = prism4j.grammar(fallback); + } + language = _language; + grammar = _grammar; + } + + if (grammar != null) { + out = highlight(language, grammar, code); + } else { + out = code; + } + + return out; + } + + @NonNull + protected CharSequence highlight(@NonNull String language, @NonNull Prism4j.Grammar grammar, @NonNull String code) { + final SpannableBuilder builder = new SpannableBuilder(); + final Prism4jSyntaxVisitor visitor = new Prism4jSyntaxVisitor(language, theme, builder); + visitor.visit(prism4j.tokenize(code, grammar)); + return builder.text(); + } + + @NonNull + protected Prism4j prism4j() { + return prism4j; + } + + @NonNull + protected Prism4jTheme theme() { + return theme; + } + + @Nullable + protected String fallback() { + return fallback; + } +} diff --git a/library-syntax/src/main/java/ru/noties/markwon/syntax/Prism4jSyntaxVisitor.java b/library-syntax/src/main/java/ru/noties/markwon/syntax/Prism4jSyntaxVisitor.java new file mode 100644 index 00000000..93bf2646 --- /dev/null +++ b/library-syntax/src/main/java/ru/noties/markwon/syntax/Prism4jSyntaxVisitor.java @@ -0,0 +1,40 @@ +package ru.noties.markwon.syntax; + +import android.support.annotation.NonNull; + +import ru.noties.markwon.SpannableBuilder; +import ru.noties.prism4j.AbsVisitor; +import ru.noties.prism4j.Prism4j; + +class Prism4jSyntaxVisitor extends AbsVisitor { + + private final String language; + private final Prism4jTheme theme; + private final SpannableBuilder builder; + + Prism4jSyntaxVisitor( + @NonNull String language, + @NonNull Prism4jTheme theme, + @NonNull SpannableBuilder builder) { + this.language = language; + this.theme = theme; + this.builder = builder; + } + + @Override + protected void visitText(@NonNull Prism4j.Text text) { + builder.append(text.literal()); + } + + @Override + protected void visitSyntax(@NonNull Prism4j.Syntax syntax) { + + final int start = builder.length(); + visit(syntax.children()); + final int end = builder.length(); + + if (end != start) { + theme.apply(language, syntax, builder, start, end); + } + } +} diff --git a/library-syntax/src/main/java/ru/noties/markwon/syntax/Prism4jTheme.java b/library-syntax/src/main/java/ru/noties/markwon/syntax/Prism4jTheme.java new file mode 100644 index 00000000..b80c152b --- /dev/null +++ b/library-syntax/src/main/java/ru/noties/markwon/syntax/Prism4jTheme.java @@ -0,0 +1,17 @@ +package ru.noties.markwon.syntax; + +import android.support.annotation.NonNull; + +import ru.noties.markwon.SpannableBuilder; +import ru.noties.prism4j.Prism4j; + +public interface Prism4jTheme { + + void apply( + @NonNull String language, + @NonNull Prism4j.Syntax syntax, + @NonNull SpannableBuilder builder, + int start, + int end + ); +} diff --git a/settings.gradle b/settings.gradle index fbd8c3c4..29dc38f9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app', ':library', ':library-image-loader', ':library-view', ':sample-custom-extension' +include ':app', ':library', ':library-image-loader', ':library-view', ':sample-custom-extension', ':library-syntax'