mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
Lib | Code syntax highlighter. First iteration (#1033)
This commit is contained in:
parent
2a8ffedd8c
commit
20767b3d1c
28 changed files with 575 additions and 3 deletions
|
@ -85,6 +85,7 @@ dependencies {
|
|||
implementation project(':library-kanban-widget')
|
||||
implementation project(':library-page-icon-picker-widget')
|
||||
implementation project(':library-emojifier')
|
||||
implementation project(':library-syntax-highlighter')
|
||||
implementation project(':analytics')
|
||||
|
||||
def applicationDependencies = rootProject.ext.mainApplication
|
||||
|
|
10
app/src/main/assets/syntax/color_scheme.json
Normal file
10
app/src/main/assets/syntax/color_scheme.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"keyword": "#d73a49",
|
||||
"comment": "#6a737d",
|
||||
"function": "#6f42c1",
|
||||
"number": "#0366d6",
|
||||
"boolean": "#0366d6",
|
||||
"operator": "#0366d6",
|
||||
"string": "#05264c",
|
||||
"property": "#0366d6"
|
||||
}
|
32
app/src/main/assets/syntax/css.json
Normal file
32
app/src/main/assets/syntax/css.json
Normal file
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"keywords": [
|
||||
],
|
||||
"operators": [],
|
||||
"other": [
|
||||
{
|
||||
"pattern": "[-a-z0-9]+(?=\\()",
|
||||
"color": "#6f42c1",
|
||||
"key": "function"
|
||||
},
|
||||
{
|
||||
"pattern": "[-_a-z\\xA0-\\uFFFF][-\\w\\xA0-\\uFFFF]*(?=\\s*:)",
|
||||
"color": "#0366d6",
|
||||
"key": "property"
|
||||
},
|
||||
{
|
||||
"pattern": "!important\\b",
|
||||
"color": "#d73a49",
|
||||
"key": "important"
|
||||
},
|
||||
{
|
||||
"pattern": "\\/\\*[\\s\\S]*?\\*\\/",
|
||||
"color": "#6a737d",
|
||||
"key": "comment"
|
||||
},
|
||||
{
|
||||
"pattern": "(\"|')(?:\\\\(?:\\r\\n|[\\s\\S])|(?!\\1)[^\\\\\\r\\n])*\\1",
|
||||
"color": "#05264c",
|
||||
"key": "string"
|
||||
}
|
||||
]
|
||||
}
|
38
app/src/main/assets/syntax/go.json
Normal file
38
app/src/main/assets/syntax/go.json
Normal file
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"keywords": [
|
||||
{
|
||||
"pattern": "\\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(?:to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\\b",
|
||||
"color": "#d73a49",
|
||||
"key": "keyword"
|
||||
}
|
||||
],
|
||||
"operators": [
|
||||
{
|
||||
"pattern": "[*\\/%^!=]=?|\\+[=+]?|-[=-]?|\\|[=|]?|&(?:=|&|\\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\\.\\.\\.",
|
||||
"color": "#0366d6",
|
||||
"key": "operator"
|
||||
}
|
||||
],
|
||||
"other": [
|
||||
{
|
||||
"pattern": "([a-zA-Z_{1}][a-zA-Z0-9_]+)(?=\\()",
|
||||
"color": "#6f42c1",
|
||||
"key": "function"
|
||||
},
|
||||
{
|
||||
"pattern": "\\b(?:_|iota|nil|true|false)\\b",
|
||||
"color": "#0366d6",
|
||||
"key": "boolean"
|
||||
},
|
||||
{
|
||||
"pattern": "(?:\\b0x[a-f\\d]+|(?:\\b\\d+\\.?\\d*|\\B\\.\\d+)(?:e[-+]?\\d+)?)i?",
|
||||
"color": "#0366d6",
|
||||
"key": "number"
|
||||
},
|
||||
{
|
||||
"pattern": "([\"'`])(?:\\\\[\\s\\S]|(?!\\1)[^\\\\])*\\1",
|
||||
"color": "#05264c",
|
||||
"key": "string"
|
||||
}
|
||||
]
|
||||
}
|
37
app/src/main/assets/syntax/java.json
Normal file
37
app/src/main/assets/syntax/java.json
Normal file
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"keywords": [
|
||||
{
|
||||
"pattern": "\\b(?:abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|exports|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|module|native|new|non-sealed|null|open|opens|package|permits|private|protected|provides|public|record|requires|return|sealed|short|static|strictfp|super|switch|synchronized|this|throw|throws|to|transient|transitive|try|uses|var|void|volatile|while|with|yield)\\b",
|
||||
"color": "#bd2c00",
|
||||
"key": "keyword"
|
||||
}
|
||||
],
|
||||
"operators": [],
|
||||
"other": [
|
||||
{
|
||||
"pattern": "\\(|\\)|\\[|\\]|:|;|\\.|\\||;|\\&|\\{|\\}",
|
||||
"color": "#b71c1c",
|
||||
"key": ""
|
||||
},
|
||||
{
|
||||
"pattern": "@\\w+",
|
||||
"color": "#b87333",
|
||||
"key": "annotation"
|
||||
},
|
||||
{
|
||||
"pattern": "\\b\\d+[\\.]?\\d*([eE]\\-?\\d+)?[lLdDfF]?\\b|\\b0x[a-fA-F\\d]+\\b",
|
||||
"color": "#f4511e",
|
||||
"key:": ""
|
||||
},
|
||||
{
|
||||
"pattern": "(\\\"(.*)\\\"|\\\"(.*)\\\")",
|
||||
"color": "#ff0000",
|
||||
"key": ""
|
||||
},
|
||||
{
|
||||
"pattern": "\\b[A-Z](?:\\w*[a-z]\\w*)?\\b",
|
||||
"color": "#6e5494",
|
||||
"key": "class"
|
||||
}
|
||||
]
|
||||
}
|
48
app/src/main/assets/syntax/javascript.json
Normal file
48
app/src/main/assets/syntax/javascript.json
Normal file
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"keywords": [
|
||||
{
|
||||
"pattern": "(^|[^.]|\\.\\.\\.\\s*)\\b(?:as|async(?=\\s*(?:function\\b|\\(|[$\\w\\xA0-\\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|for|from|function|(?:get|set)(?=\\s*[\\[$\\w\\xA0-\\uFFFF])|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\\b",
|
||||
"color": "#bd2c00",
|
||||
"key": "keyword"
|
||||
}
|
||||
],
|
||||
"operators": [
|
||||
{
|
||||
"pattern": "--|\\+\\+|\\*\\*=?|=>|&&=?|\\|\\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\\.{3}|\\?\\?=?|\\?\\.?|[~:]",
|
||||
"color": "#bd2c00",
|
||||
"key": "keyword"
|
||||
}
|
||||
],
|
||||
"other": [
|
||||
{
|
||||
"pattern": "#?[_$a-zA-Z\\xA0-\\uFFFF][$\\w\\xA0-\\uFFFF]*(?=\\s*(?:\\.\\s*(?:apply|bind|call)\\s*)?\\()",
|
||||
"color": "#6e5494",
|
||||
"key": "function"
|
||||
},
|
||||
{
|
||||
"pattern": "\\(|\\)|\\[|\\]|:|;|\\.|\\||;|\\&|\\{|\\}",
|
||||
"color": "#b71c1c",
|
||||
"key": ""
|
||||
},
|
||||
{
|
||||
"pattern": "@\\w+",
|
||||
"color": "#b87333",
|
||||
"key": "annotation"
|
||||
},
|
||||
{
|
||||
"pattern": "\\b(?:(?:0[xX](?:[\\dA-Fa-f](?:_[\\dA-Fa-f])?)+|0[bB](?:[01](?:_[01])?)+|0[oO](?:[0-7](?:_[0-7])?)+)n?|(?:\\d(?:_\\d)?)+n|NaN|Infinity)\\b|(?:\\b(?:\\d(?:_\\d)?)+\\.?(?:\\d(?:_\\d)?)*|\\B\\.(?:\\d(?:_\\d)?)+)(?:[Ee][+-]?(?:\\d(?:_\\d)?)+)?",
|
||||
"color": "#f4511e",
|
||||
"key": "number"
|
||||
},
|
||||
{
|
||||
"pattern": "(\\\"(.*)\\\"|\\\"(.*)\\\")",
|
||||
"color": "#4078c0",
|
||||
"key": ""
|
||||
},
|
||||
{
|
||||
"pattern": "(^|[^$\\w\\xA0-\\uFFFF])[_$A-Z\\xA0-\\uFFFF][$\\w\\xA0-\\uFFFF]*(?=\\.(?:prototype|constructor))",
|
||||
"color": "#6e5494",
|
||||
"key": "class"
|
||||
}
|
||||
]
|
||||
}
|
41
app/src/main/assets/syntax/json.json
Normal file
41
app/src/main/assets/syntax/json.json
Normal file
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"keywords": [],
|
||||
"operators": [],
|
||||
"other": [
|
||||
{
|
||||
"pattern": "\"(?:\\\\.|[^\\\\\"\\r\\n])*\"(?=\\s*:)",
|
||||
"color": "#b71c1c",
|
||||
"key": "property"
|
||||
},
|
||||
{
|
||||
"pattern": "\"(?!\\s*:)(?:\\\\.|[^\\\\\"\\r\\n])*\"(?!\\s*:)",
|
||||
"color": "#4078c0",
|
||||
"key": "string"
|
||||
},
|
||||
{
|
||||
"pattern": ":",
|
||||
"color": "#6e5494",
|
||||
"key": "operator"
|
||||
},
|
||||
{
|
||||
"pattern": "-?\\b\\d+(?:\\.\\d+)?(?:e[+-]?\\d+)?\\b",
|
||||
"color": "#6e5494",
|
||||
"key": "number"
|
||||
},
|
||||
{
|
||||
"pattern": "\\b(?:true|false)\\b",
|
||||
"color": "#6e5494",
|
||||
"key": "boolean"
|
||||
},
|
||||
{
|
||||
"pattern": "\\bnull\\b",
|
||||
"color": "#6e5494",
|
||||
"key": "null"
|
||||
},
|
||||
{
|
||||
"pattern": "[{}[\\\\],]",
|
||||
"color": "#b8b8b8",
|
||||
"key": "punctuation"
|
||||
}
|
||||
]
|
||||
}
|
37
app/src/main/assets/syntax/kotlin.json
Normal file
37
app/src/main/assets/syntax/kotlin.json
Normal file
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"keywords": [
|
||||
{
|
||||
"pattern": "\\w+(?=\\s*\\()",
|
||||
"color": "#6f42c1",
|
||||
"key": "function"
|
||||
},
|
||||
{
|
||||
"pattern": "(^|[^.])\\b(?:abstract|actual|annotation|as|break|by|catch|class|companion|const|constructor|continue|crossinline|data|do|dynamic|else|enum|expect|external|final|finally|for|fun|get|if|import|in|infix|init|inline|inner|interface|internal|is|lateinit|noinline|null|object|open|operator|out|override|package|private|protected|public|reified|return|sealed|set|super|suspend|tailrec|this|throw|to|try|typealias|val|var|vararg|when|where|while)\\b",
|
||||
"color": "#d73a49",
|
||||
"key": "keyword"
|
||||
}
|
||||
],
|
||||
"operators": [],
|
||||
"other": [
|
||||
{
|
||||
"pattern": "@\\w+",
|
||||
"color": "#b87333",
|
||||
"key": "annotation"
|
||||
},
|
||||
{
|
||||
"pattern": "\\b\\d+[\\.]?\\d*([eE]\\-?\\d+)?[lLdDfF]?\\b|\\b0x[a-fA-F\\d]+\\b",
|
||||
"color": "#0366d6",
|
||||
"key": "number"
|
||||
},
|
||||
{
|
||||
"pattern": "(\\\"(.*)\\\"|\\\"(.*)\\\")",
|
||||
"color": "#ff0000",
|
||||
"key": ""
|
||||
},
|
||||
{
|
||||
"pattern": "\\b[A-Z](?:\\w*[a-z]\\w*)?\\b",
|
||||
"color": "#6e5494",
|
||||
"key": "class"
|
||||
}
|
||||
]
|
||||
}
|
38
app/src/main/assets/syntax/python.json
Normal file
38
app/src/main/assets/syntax/python.json
Normal file
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"keywords": [
|
||||
{
|
||||
"pattern": "\\b(?:and|as|assert|async|await|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\\b",
|
||||
"color": "#d73a49",
|
||||
"key": "keyword"
|
||||
}
|
||||
],
|
||||
"operators": [
|
||||
{
|
||||
"pattern": "[-+%=]=?|!=|\\*\\*?=?|\\/\\/?=?|<[<=>]?|>[=>]?|[&|^~]",
|
||||
"color": "#0366d6",
|
||||
"key": "keyword"
|
||||
}
|
||||
],
|
||||
"other": [
|
||||
{
|
||||
"pattern": "([a-zA-Z_{1}][a-zA-Z0-9_]+)(?=\\()",
|
||||
"color": "#6f42c1",
|
||||
"key": "function"
|
||||
},
|
||||
{
|
||||
"pattern": "(^|[^\\\\])#.*",
|
||||
"color": "#6a737d",
|
||||
"key": "comment"
|
||||
},
|
||||
{
|
||||
"pattern": "(?:\\b(?=\\d)|\\B(?=\\.))(?:0[bo])?(?:(?:\\d|0x[\\da-f])[\\da-f]*\\.?\\d*|\\.\\d+)(?:e[+-]?\\d+)?j?\\b",
|
||||
"color": "#0366d6",
|
||||
"key": "number"
|
||||
},
|
||||
{
|
||||
"pattern": "(?:[rub]|rb|br)?(\"|')(?:\\\\.|(?!\\1)[^\\\\\\r\\n])*\\1",
|
||||
"color": "#05264c",
|
||||
"key": "string"
|
||||
}
|
||||
]
|
||||
}
|
23
app/src/main/assets/syntax/scheme.json
Normal file
23
app/src/main/assets/syntax/scheme.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"keywords": [
|
||||
{
|
||||
"pattern": "",
|
||||
"color": "",
|
||||
"key": ""
|
||||
}
|
||||
],
|
||||
"operators": [
|
||||
{
|
||||
"pattern": "",
|
||||
"color": "",
|
||||
"key": ""
|
||||
}
|
||||
],
|
||||
"other": [
|
||||
{
|
||||
"pattern": "",
|
||||
"color": "",
|
||||
"key": ""
|
||||
}
|
||||
]
|
||||
}
|
53
app/src/main/assets/syntax/typescript.json
Normal file
53
app/src/main/assets/syntax/typescript.json
Normal file
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"keywords": [
|
||||
{
|
||||
"pattern": "\\b(?:abstract|as|asserts|async|await|break|case|catch|class|const|constructor|continue|debugger|declare|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|is|keyof|let|module|namespace|new|null|of|package|private|protected|public|readonly|return|require|set|static|super|switch|this|throw|try|type|typeof|undefined|var|void|while|with|yield)\\b",
|
||||
"color": "#bd2c00",
|
||||
"key": "keyword"
|
||||
},
|
||||
{
|
||||
"pattern": "\\b(?:string|Function|any|number|boolean|Array|symbol|console|Promise|unknown|never)\\b",
|
||||
"color": "#bd2c00",
|
||||
"key": "keyword"
|
||||
}
|
||||
],
|
||||
"operators": [
|
||||
{
|
||||
"pattern": "--|\\+\\+|\\*\\*=?|=>|&&=?|\\|\\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\\.{3}|\\?\\?=?|\\?\\.?|[~:]",
|
||||
"color": "#bd2c00",
|
||||
"key": "keyword"
|
||||
}
|
||||
],
|
||||
"other": [
|
||||
{
|
||||
"pattern": "#?[_$a-zA-Z\\xA0-\\uFFFF][$\\w\\xA0-\\uFFFF]*(?=\\s*(?:\\.\\s*(?:apply|bind|call)\\s*)?\\()",
|
||||
"color": "#6e5494",
|
||||
"key": "function"
|
||||
},
|
||||
{
|
||||
"pattern": "\\(|\\)|\\[|\\]|:|;|\\.|\\||;|\\&|\\{|\\}",
|
||||
"color": "#b71c1c",
|
||||
"key": ""
|
||||
},
|
||||
{
|
||||
"pattern": "@\\w+",
|
||||
"color": "#b87333",
|
||||
"key": "annotation"
|
||||
},
|
||||
{
|
||||
"pattern": "\\b(?:(?:0[xX](?:[\\dA-Fa-f](?:_[\\dA-Fa-f])?)+|0[bB](?:[01](?:_[01])?)+|0[oO](?:[0-7](?:_[0-7])?)+)n?|(?:\\d(?:_\\d)?)+n|NaN|Infinity)\\b|(?:\\b(?:\\d(?:_\\d)?)+\\.?(?:\\d(?:_\\d)?)*|\\B\\.(?:\\d(?:_\\d)?)+)(?:[Ee][+-]?(?:\\d(?:_\\d)?)+)?",
|
||||
"color": "#f4511e",
|
||||
"key": "number"
|
||||
},
|
||||
{
|
||||
"pattern": "(\\\"(.*)\\\"|\\\"(.*)\\\")",
|
||||
"color": "#4078c0",
|
||||
"key": ""
|
||||
},
|
||||
{
|
||||
"pattern": "(\\b(?:class|extends|implements|instanceof|interface|new|type)\\s+)(?!keyof\\b)[_$a-zA-Z\\xA0-\\uFFFF][$\\w\\xA0-\\uFFFF]*(?:\\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>)?",
|
||||
"color": "#6e5494",
|
||||
"key": "class"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -32,6 +32,7 @@ buildscript {
|
|||
classpath "com.google.protobuf:protobuf-gradle-plugin:0.8.12"
|
||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.3.0'
|
||||
classpath "org.jetbrains.dokka:dokka-gradle-plugin:${dokka_version}"
|
||||
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ dependencies {
|
|||
|
||||
implementation project(':core-utils')
|
||||
implementation project(':library-emojifier')
|
||||
implementation project(':library-syntax-highlighter')
|
||||
|
||||
implementation applicationDependencies.appcompat
|
||||
implementation applicationDependencies.kotlin
|
||||
|
|
|
@ -1,14 +1,21 @@
|
|||
package com.anytypeio.anytype.core_ui.widgets.text
|
||||
|
||||
import android.content.Context
|
||||
import android.text.Editable
|
||||
import android.text.InputType.*
|
||||
import android.text.TextWatcher
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.widget.AppCompatEditText
|
||||
import com.anytypeio.anytype.core_ui.tools.DefaultTextWatcher
|
||||
import com.anytypeio.anytype.library_syntax_highlighter.*
|
||||
import timber.log.Timber
|
||||
|
||||
class CodeTextInputWidget : AppCompatEditText {
|
||||
class CodeTextInputWidget : AppCompatEditText, SyntaxHighlighter {
|
||||
|
||||
override val rules: MutableList<Syntax> = mutableListOf()
|
||||
override val source: Editable get() = editableText
|
||||
|
||||
private val syntaxTextWatcher = SyntaxTextWatcher { highlight() }
|
||||
|
||||
private val watchers: MutableList<TextWatcher> = mutableListOf()
|
||||
|
||||
|
@ -17,6 +24,7 @@ class CodeTextInputWidget : AppCompatEditText {
|
|||
constructor(context: Context) : super(context)
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
|
||||
setup()
|
||||
setupSyntaxHighlighter()
|
||||
}
|
||||
|
||||
constructor(
|
||||
|
@ -25,12 +33,19 @@ class CodeTextInputWidget : AppCompatEditText {
|
|||
defStyle: Int
|
||||
) : super(context, attrs, defStyle) {
|
||||
setup()
|
||||
setupSyntaxHighlighter()
|
||||
}
|
||||
|
||||
private fun setup() {
|
||||
enableEditMode()
|
||||
}
|
||||
|
||||
private fun setupSyntaxHighlighter() {
|
||||
addRules(context.obtainSyntaxRules(Syntaxes.KOTLIN))
|
||||
highlight()
|
||||
addTextChangedListener(syntaxTextWatcher)
|
||||
}
|
||||
|
||||
fun enableEditMode() {
|
||||
setRawInputType(TYPE_CLASS_TEXT or TYPE_TEXT_FLAG_MULTI_LINE or TYPE_TEXT_FLAG_NO_SUGGESTIONS)
|
||||
setHorizontallyScrolling(false)
|
||||
|
@ -46,7 +61,7 @@ class CodeTextInputWidget : AppCompatEditText {
|
|||
}
|
||||
|
||||
override fun addTextChangedListener(watcher: TextWatcher) {
|
||||
watchers.add(watcher)
|
||||
if (watcher !is SyntaxTextWatcher) watchers.add(watcher)
|
||||
super.addTextChangedListener(watcher)
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ allprojects {
|
|||
ext {
|
||||
// Kotlin
|
||||
kotlin_coroutines_version = '1.3.9'
|
||||
kotlinx_serialization_json_version = '1.0.0'
|
||||
|
||||
// AndroidX
|
||||
androidx_core_version = '1.5.0-alpha02'
|
||||
|
@ -130,7 +131,7 @@ ext {
|
|||
permissionDispCompiler: "org.permissionsdispatcher:permissionsdispatcher-processor:$permission_disp_version",
|
||||
pickT: "com.github.HBiSoft:PickiT:$pickt_version",
|
||||
urlcleaner: "com.shekhargulati.urlcleaner:urlcleaner:$urlcleaner_version",
|
||||
|
||||
kotlinxSerializationJson: "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinx_serialization_json_version",
|
||||
crashlytics: "com.google.firebase:firebase-crashlytics:$crashlytics_version",
|
||||
firebaseCore: "com.google.firebase:firebase-core:$firebase_core_version"
|
||||
]
|
||||
|
|
1
library-syntax-highlighter/.gitignore
vendored
Normal file
1
library-syntax-highlighter/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/build
|
58
library-syntax-highlighter/build.gradle
Normal file
58
library-syntax-highlighter/build.gradle
Normal file
|
@ -0,0 +1,58 @@
|
|||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
|
||||
android {
|
||||
def config = rootProject.extensions.getByName("ext")
|
||||
|
||||
compileSdkVersion compile_sdk
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion config["min_sdk"]
|
||||
targetSdkVersion config["target_sdk"]
|
||||
versionCode config["version_code"]
|
||||
versionName config["version_name"]
|
||||
|
||||
testInstrumentationRunner config["test_runner"]
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests {
|
||||
includeAndroidResources = true
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
def applicationDependencies = rootProject.ext.mainApplication
|
||||
def unitTestDependencies = rootProject.ext.unitTesting
|
||||
|
||||
implementation applicationDependencies.appcompat
|
||||
implementation applicationDependencies.kotlin
|
||||
implementation applicationDependencies.coroutines
|
||||
implementation applicationDependencies.androidxCore
|
||||
implementation applicationDependencies.timber
|
||||
implementation applicationDependencies.kotlinxSerializationJson
|
||||
|
||||
testImplementation unitTestDependencies.junit
|
||||
testImplementation unitTestDependencies.kotlinTest
|
||||
testImplementation unitTestDependencies.robolectric
|
||||
testImplementation unitTestDependencies.androidXTestCore
|
||||
}
|
0
library-syntax-highlighter/consumer-rules.pro
Normal file
0
library-syntax-highlighter/consumer-rules.pro
Normal file
21
library-syntax-highlighter/proguard-rules.pro
vendored
Normal file
21
library-syntax-highlighter/proguard-rules.pro
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
1
library-syntax-highlighter/src/main/AndroidManifest.xml
Normal file
1
library-syntax-highlighter/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1 @@
|
|||
<manifest package="com.anytypeio.anytype.library_syntax_highlighter" />
|
|
@ -0,0 +1,9 @@
|
|||
package com.anytypeio.anytype.library_syntax_highlighter
|
||||
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
|
||||
class Syntax(val color: Int, val regex: String) {
|
||||
private val pattern = Pattern.compile(regex)
|
||||
fun matcher(txt: String): Matcher = pattern.matcher(txt)
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package com.anytypeio.anytype.library_syntax_highlighter
|
||||
|
||||
import android.text.style.ForegroundColorSpan
|
||||
|
||||
class SyntaxColorSpan(color: Int) : ForegroundColorSpan(color)
|
|
@ -0,0 +1,13 @@
|
|||
package com.anytypeio.anytype.library_syntax_highlighter
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class SyntaxEntity(val pattern: String, val color: String, val key: String)
|
||||
|
||||
@Serializable
|
||||
data class SyntaxDescriptor(
|
||||
val keywords: List<SyntaxEntity>,
|
||||
val operators: List<SyntaxEntity>,
|
||||
val other: List<SyntaxEntity>
|
||||
)
|
|
@ -0,0 +1,41 @@
|
|||
package com.anytypeio.anytype.library_syntax_highlighter
|
||||
|
||||
import android.text.Editable
|
||||
import android.text.Spannable
|
||||
|
||||
/**
|
||||
* @property [source] text containing source code, whose syntax we need to highlight
|
||||
* @property [rules] set of syntax rules for a programming language
|
||||
*/
|
||||
interface SyntaxHighlighter {
|
||||
|
||||
val source: Editable
|
||||
val rules: MutableList<Syntax>
|
||||
|
||||
fun addRules(new: List<Syntax>) {
|
||||
rules.apply {
|
||||
clear()
|
||||
addAll(new)
|
||||
}
|
||||
}
|
||||
|
||||
fun highlight() {
|
||||
clear()
|
||||
rules.forEach { syntax ->
|
||||
val matcher = syntax.matcher(source.toString())
|
||||
while (matcher.find()) {
|
||||
source.setSpan(
|
||||
SyntaxColorSpan(syntax.color),
|
||||
matcher.start(),
|
||||
matcher.end(),
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
val current = source.getSpans(0, source.length, SyntaxColorSpan::class.java)
|
||||
current.forEach { span -> source.removeSpan(span) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package com.anytypeio.anytype.library_syntax_highlighter
|
||||
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
|
||||
class SyntaxTextWatcher(private val onAfterTextChanged: () -> Unit) : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
onAfterTextChanged()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package com.anytypeio.anytype.library_syntax_highlighter
|
||||
|
||||
object Syntaxes {
|
||||
const val KOTLIN = "kotlin"
|
||||
const val PYTHON = "python"
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package com.anytypeio.anytype.library_syntax_highlighter
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.io.IOException
|
||||
|
||||
fun Context.obtainSyntaxRules(language: String): List<Syntax> {
|
||||
val path = "syntax/${language}.json"
|
||||
val json = obtainJsonDataFromAsset(path)
|
||||
checkNotNull(json) { "Could not deserialize syntax rules from path: $path" }
|
||||
val descriptor = Json.decodeFromString<SyntaxDescriptor>(json)
|
||||
val rules = descriptor.let { it.keywords + it.operators + it.other }
|
||||
return rules.map { s ->
|
||||
Syntax(
|
||||
regex = s.pattern,
|
||||
color = Color.parseColor(s.color)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.obtainJsonDataFromAsset(path: String): String? = try {
|
||||
assets.open(path).bufferedReader().use { stream -> stream.readText() }
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
|
@ -12,6 +12,7 @@ include ':app',
|
|||
':library-kanban-widget',
|
||||
':library-page-icon-picker-widget',
|
||||
':library-emojifier',
|
||||
':library-syntax-highlighter',
|
||||
':sample',
|
||||
':clipboard',
|
||||
':analytics'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue