mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
fix(editor): support static span colors for hex colors
This commit is contained in:
@@ -26,7 +26,8 @@
|
||||
},
|
||||
"string": {
|
||||
"fg": "@string",
|
||||
"completion": false
|
||||
"completion": false,
|
||||
"maybeHexColor": true
|
||||
},
|
||||
"variable.builtin": {
|
||||
"fg": "@keyword",
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
"types": ["json"],
|
||||
"styles": {
|
||||
"string.special.key": "@json.key",
|
||||
"string" : "@string",
|
||||
"string" : {
|
||||
"fg": "@string",
|
||||
"maybeHexColor": true
|
||||
},
|
||||
"number" : "@number",
|
||||
"constant.builtin": "@keyword",
|
||||
"escape": "@json.escape",
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
},
|
||||
"string": {
|
||||
"fg": "@string",
|
||||
"completion": false
|
||||
"completion": false,
|
||||
"maybeHexColor": true
|
||||
},
|
||||
"string.regex": {
|
||||
"fg": "@string",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
scheme.name=AndroidIDE Default - Dark
|
||||
|
||||
# The version code of the color scheme
|
||||
scheme.version=12
|
||||
scheme.version=13
|
||||
|
||||
# Whether the scheme is dark or light
|
||||
scheme.isDark=true
|
||||
|
||||
@@ -28,6 +28,13 @@
|
||||
"xmlns.prefix": "@ns_prefix",
|
||||
"attr.prefix" : "@ns_prefix",
|
||||
"attr.name": "@onSurface",
|
||||
"attr.value": "@string"
|
||||
"attr.value": {
|
||||
"fg": "@string",
|
||||
"maybeHexColor": true
|
||||
},
|
||||
"text": {
|
||||
"fg": "@onSurface",
|
||||
"maybeHexColor": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,8 @@
|
||||
},
|
||||
"string": {
|
||||
"fg": "@string",
|
||||
"completion": false
|
||||
"completion": false,
|
||||
"maybeHexColor": true
|
||||
},
|
||||
"variable.builtin": {
|
||||
"fg": "@keyword",
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
"types": ["json"],
|
||||
"styles": {
|
||||
"string.special.key": "@json.key",
|
||||
"string" : "@string",
|
||||
"string" : {
|
||||
"fg": "@string",
|
||||
"maybeHexColor": true
|
||||
},
|
||||
"number" : "@number",
|
||||
"constant.builtin": "@keyword",
|
||||
"escape": "@json.escape",
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
},
|
||||
"string": {
|
||||
"fg": "@string",
|
||||
"completion": false
|
||||
"completion": false,
|
||||
"maybeHexColor": true
|
||||
},
|
||||
"string.regex": {
|
||||
"fg": "@string",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
scheme.name=AndroidIDE Default
|
||||
|
||||
# The version code of the color scheme
|
||||
scheme.version=12
|
||||
scheme.version=13
|
||||
|
||||
# Whether the scheme is dark or light
|
||||
scheme.isDark=false
|
||||
|
||||
@@ -28,6 +28,13 @@
|
||||
"xmlns.prefix": "@ns_prefix",
|
||||
"attr.prefix" : "@ns_prefix",
|
||||
"attr.name": "@onSurface",
|
||||
"attr.value": "@string"
|
||||
"attr.value": {
|
||||
"fg": "@string",
|
||||
"maybeHexColor": true
|
||||
},
|
||||
"text": {
|
||||
"fg": "@onSurface",
|
||||
"maybeHexColor": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,8 @@
|
||||
(cdata_end) @cdata.end
|
||||
(cdata) @cdata.data
|
||||
|
||||
(char_data) @text
|
||||
|
||||
(eq) @operator
|
||||
|
||||
["<" "/" ">" "<?" "?>"] @operator
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.editor.language.treesitter
|
||||
|
||||
import com.itsaky.androidide.editor.schemes.LanguageScheme
|
||||
import io.github.rosemoe.sora.editor.ts.TsAnalyzeManager
|
||||
import io.github.rosemoe.sora.editor.ts.TsLanguageSpec
|
||||
import io.github.rosemoe.sora.editor.ts.TsTheme
|
||||
import io.github.rosemoe.sora.lang.styling.Styles
|
||||
|
||||
/**
|
||||
* [TsAnalyzeManager] implementation for tree sitter languages.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class TreeSitterAnalyzeManager(
|
||||
languageSpec: TsLanguageSpec,
|
||||
theme: TsTheme
|
||||
) : TsAnalyzeManager(languageSpec, theme) {
|
||||
|
||||
override var styles: Styles = Styles()
|
||||
set(value) {
|
||||
field = value
|
||||
resetSpanFactory(value, langScheme)
|
||||
}
|
||||
|
||||
internal var langScheme: LanguageScheme? = null
|
||||
set(value) {
|
||||
field = value
|
||||
resetSpanFactory(styles, value)
|
||||
}
|
||||
|
||||
private fun resetSpanFactory(styles: Styles, langScheme: LanguageScheme?) {
|
||||
spanFactory = TreeSitterSpanFactory(reference, languageSpec.tsQuery, styles, langScheme)
|
||||
}
|
||||
}
|
||||
+12
-5
@@ -23,6 +23,7 @@ import com.itsaky.androidide.editor.language.newline.TSBracketsHandler
|
||||
import com.itsaky.androidide.editor.language.utils.CommonSymbolPairs
|
||||
import com.itsaky.androidide.editor.schemes.IDEColorScheme
|
||||
import com.itsaky.androidide.editor.schemes.IDEColorSchemeProvider
|
||||
import com.itsaky.androidide.editor.schemes.LanguageScheme
|
||||
import com.itsaky.androidide.editor.schemes.LanguageSpecProvider.getLanguageSpec
|
||||
import com.itsaky.androidide.editor.schemes.LocalCaptureSpecProvider.newLocalCaptureSpec
|
||||
import com.itsaky.androidide.treesitter.TSLanguage
|
||||
@@ -31,7 +32,6 @@ import com.itsaky.androidide.treesitter.TSQueryCapture
|
||||
import com.itsaky.androidide.treesitter.TSQueryCursor
|
||||
import com.itsaky.androidide.treesitter.TSQueryMatch
|
||||
import com.itsaky.androidide.utils.ILogger
|
||||
import io.github.rosemoe.sora.editor.ts.TsAnalyzeManager
|
||||
import io.github.rosemoe.sora.editor.ts.TsTheme
|
||||
import io.github.rosemoe.sora.lang.analysis.AnalyzeManager
|
||||
import io.github.rosemoe.sora.text.ContentReference
|
||||
@@ -48,10 +48,11 @@ abstract class TreeSitterLanguage(context: Context, lang: TSLanguage, type: Stri
|
||||
|
||||
private lateinit var tsTheme: TsTheme
|
||||
private lateinit var languageSpec: TreeSitterLanguageSpec
|
||||
private val analyzer by lazy { TsAnalyzeManager(languageSpec.spec, tsTheme) }
|
||||
|
||||
private val analyzer by lazy { TreeSitterAnalyzeManager(languageSpec.spec, tsTheme) }
|
||||
private val newlineHandlersLazy by lazy { createNewlineHandlers() }
|
||||
|
||||
private var languageScheme: LanguageScheme? = null
|
||||
|
||||
private val log = ILogger.newInstance("TreeSitterLanguage")
|
||||
|
||||
init {
|
||||
@@ -69,6 +70,7 @@ abstract class TreeSitterLanguage(context: Context, lang: TSLanguage, type: Stri
|
||||
}
|
||||
|
||||
val langScheme = scheme.languages[type] ?: return@readScheme
|
||||
this.languageScheme = langScheme
|
||||
langScheme.styles.forEach { tsTheme.putStyleRule(it.key, it.value.makeStyle()) }
|
||||
}
|
||||
}
|
||||
@@ -78,7 +80,11 @@ abstract class TreeSitterLanguage(context: Context, lang: TSLanguage, type: Stri
|
||||
}
|
||||
|
||||
override fun getAnalyzeManager(): AnalyzeManager {
|
||||
return this.analyzer
|
||||
return this.analyzer.also {
|
||||
if (it.langScheme == null) {
|
||||
it.langScheme = this.languageScheme
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSymbolPairs(): SymbolPairMatch {
|
||||
@@ -152,7 +158,8 @@ abstract class TreeSitterLanguage(context: Context, lang: TSLanguage, type: Stri
|
||||
}
|
||||
|
||||
override fun destroy() {
|
||||
languageSpec.close()
|
||||
this.languageSpec.close()
|
||||
this.languageScheme = null
|
||||
}
|
||||
|
||||
/** A [Factory] creates instance of a specific [TreeSitterLanguage] implementation. */
|
||||
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.editor.language.treesitter
|
||||
|
||||
import android.graphics.Color
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import com.itsaky.androidide.editor.schemes.LanguageScheme
|
||||
import com.itsaky.androidide.treesitter.TSQuery
|
||||
import com.itsaky.androidide.treesitter.TSQueryCapture
|
||||
import com.itsaky.androidide.utils.ILogger
|
||||
import io.github.rosemoe.sora.editor.ts.spans.DefaultSpanFactory
|
||||
import io.github.rosemoe.sora.editor.ts.spans.TsSpanFactory
|
||||
import io.github.rosemoe.sora.lang.styling.Span
|
||||
import io.github.rosemoe.sora.lang.styling.StaticColorSpan
|
||||
import io.github.rosemoe.sora.lang.styling.Styles
|
||||
import io.github.rosemoe.sora.text.ContentReference
|
||||
|
||||
/**
|
||||
* [TsSpanFactory] for tree sitter languages.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class TreeSitterSpanFactory(
|
||||
private var content: ContentReference?,
|
||||
private var query: TSQuery?,
|
||||
private var styles: Styles?,
|
||||
private var langScheme: LanguageScheme?
|
||||
) : DefaultSpanFactory() {
|
||||
|
||||
companion object {
|
||||
|
||||
private val log = ILogger.newInstance("TreeSitterSpanFactory")
|
||||
|
||||
@JvmStatic
|
||||
private val HEX_REGEX = "#\\b([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})\\b".toRegex()
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
content = null
|
||||
query = null
|
||||
styles = null
|
||||
langScheme = null
|
||||
}
|
||||
|
||||
override fun createSpan(capture: TSQueryCapture, column: Int, spanStyle: Long): Span {
|
||||
val content = this.content?.reference ?: return super.createSpan(capture, column, spanStyle)
|
||||
val query = this.query ?: return super.createSpan(capture, column, spanStyle)
|
||||
val langScheme = this.langScheme ?: return super.createSpan(capture, column, spanStyle)
|
||||
|
||||
val captureName = query.getCaptureNameForId(capture.index)
|
||||
val styleDef = langScheme.getStyles()[captureName]
|
||||
if (styleDef?.maybeHexColor != true) {
|
||||
return super.createSpan(capture, column, spanStyle)
|
||||
}
|
||||
|
||||
val (start, end) = content.indexer.run {
|
||||
getCharPosition(capture.node.startByte / 2) to getCharPosition(capture.node.endByte / 2)
|
||||
}
|
||||
|
||||
if (start.line != end.line || start.column != column) {
|
||||
// A HEX color can only defined on a single line
|
||||
return super.createSpan(capture, column, spanStyle)
|
||||
}
|
||||
|
||||
var text: CharSequence = content.subContent(start.line, start.column + 1, end.line, end.column)
|
||||
|
||||
// TODO(itsaky): update the API in sora-editor such that we can add multiple spans for this capture
|
||||
// This method should probably return a list of spans
|
||||
val result = HEX_REGEX.find(text) ?: run {
|
||||
// Does not contain a HEX color string
|
||||
return super.createSpan(capture, column, spanStyle)
|
||||
}
|
||||
|
||||
val color = try {
|
||||
text = result.groupValues[1]
|
||||
if (text.length == 3){
|
||||
// HEX color is in the form of #FFF
|
||||
// convert it to #FFFFFF format (6 character long)
|
||||
val r = text[0]
|
||||
val g = text[1]
|
||||
val b = text[2]
|
||||
text = "$r$r$g$g$b$b"
|
||||
}
|
||||
|
||||
if (text.length == 6) {
|
||||
// Prepend alpha value
|
||||
text = "FF${text}"
|
||||
}
|
||||
|
||||
java.lang.Long.parseLong(text, 16)
|
||||
} catch (e: Exception) {
|
||||
log.error("Failed to parse hex color. color=$text", e)
|
||||
return super.createSpan(capture, column, spanStyle)
|
||||
}.toInt()
|
||||
|
||||
val textColor = if (ColorUtils.calculateLuminance(color) > 0.5f) {
|
||||
Color.BLACK
|
||||
} else {
|
||||
Color.WHITE
|
||||
}
|
||||
|
||||
return StaticColorSpan.obtain(color, textColor, column, styleDef.makeStaticStyle())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.editor.language.treesitter
|
||||
|
||||
import com.itsaky.androidide.editor.schemes.LanguageScheme
|
||||
|
||||
typealias LangScheme = Pair<String, LanguageScheme>
|
||||
@@ -20,6 +20,7 @@ package com.itsaky.androidide.editor.schemes
|
||||
import com.itsaky.androidide.editor.schemes.internal.parser.SchemeParser
|
||||
import com.itsaky.androidide.syntax.colorschemes.DynamicColorScheme
|
||||
import io.github.rosemoe.sora.lang.styling.TextStyle
|
||||
import io.github.rosemoe.sora.widget.schemes.EditorColorScheme
|
||||
import java.io.File
|
||||
import java.util.TreeSet
|
||||
|
||||
@@ -31,19 +32,19 @@ class IDEColorScheme(internal val file: File, val key: String) : DynamicColorSch
|
||||
|
||||
var name: String = ""
|
||||
internal set
|
||||
|
||||
|
||||
var version: Int = 0
|
||||
internal set
|
||||
|
||||
var isDarkScheme: Boolean = false
|
||||
internal set
|
||||
|
||||
|
||||
var darkVariant: IDEColorScheme? = null
|
||||
internal set
|
||||
|
||||
var definitions: Map<String, Int> = emptyMap()
|
||||
internal set
|
||||
|
||||
|
||||
private var colorId = endColorId
|
||||
|
||||
internal fun load() {
|
||||
@@ -65,7 +66,7 @@ class IDEColorScheme(internal val file: File, val key: String) : DynamicColorSch
|
||||
// in this case, the below properties will be null
|
||||
return editorScheme?.get(type) ?: colorIds?.get(type) ?: super.getColor(type)
|
||||
}
|
||||
|
||||
|
||||
override fun isDark(): Boolean {
|
||||
return this.isDarkScheme
|
||||
}
|
||||
@@ -94,8 +95,8 @@ class LanguageScheme {
|
||||
fun isLocalScope(capture: String): Boolean {
|
||||
return localScopes.contains(capture)
|
||||
}
|
||||
|
||||
fun isMembersScope(capture: String) : Boolean {
|
||||
|
||||
fun isMembersScope(capture: String): Boolean {
|
||||
return localMembersScopes.contains(capture)
|
||||
}
|
||||
|
||||
@@ -106,6 +107,7 @@ class LanguageScheme {
|
||||
fun isLocalDefVal(capture: String): Boolean {
|
||||
return localDefVals.contains(capture)
|
||||
}
|
||||
|
||||
fun isLocalRef(capture: String): Boolean {
|
||||
return localRefs.contains(capture)
|
||||
}
|
||||
@@ -120,17 +122,43 @@ class LanguageScheme {
|
||||
* @property italic Whether the highlighted region should have italic text.
|
||||
* @property strikeThrough Whether the highlighted region should have strikethrough text.
|
||||
* @property completion Whether code completions can be performed in the highlighted region.
|
||||
* @property maybeHexColor Whether the node represented by this style can contain HEX color strings.
|
||||
* If this value is `true`, the node's text will be parsed to check if it represents a valid HEX color.
|
||||
* If it does, that color will be used as the node's background color. The foreground color of the node
|
||||
* will be automatically selected based on the HEX color's brightness.
|
||||
*/
|
||||
data class StyleDef(
|
||||
var fg: Int,
|
||||
var fg: Int = EditorColorScheme.TEXT_NORMAL,
|
||||
var bg: Int = 0,
|
||||
var bold: Boolean = false,
|
||||
var italic: Boolean = false,
|
||||
var strikeThrough: Boolean = false,
|
||||
var completion: Boolean = true
|
||||
var completion: Boolean = true,
|
||||
var maybeHexColor: Boolean = false
|
||||
) {
|
||||
|
||||
/**
|
||||
* Make the style for the style definition.
|
||||
*
|
||||
* @see TextStyle.makeStyle
|
||||
*/
|
||||
fun makeStyle(): Long {
|
||||
return TextStyle.makeStyle(fg, bg, bold, italic, strikeThrough, !completion)
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the static style for this style definition. The background color ID in the returned style is
|
||||
* always [EditorColorScheme.STATIC_SPAN_BACKGROUND] and the foreground color ID is always
|
||||
* [EditorColorScheme.STATIC_SPAN_FOREGROUND].
|
||||
*/
|
||||
fun makeStaticStyle(): Long {
|
||||
return TextStyle.makeStyle(
|
||||
EditorColorScheme.STATIC_SPAN_FOREGROUND,
|
||||
EditorColorScheme.STATIC_SPAN_BACKGROUND,
|
||||
bold,
|
||||
italic,
|
||||
strikeThrough,
|
||||
!completion
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import com.google.gson.stream.JsonToken.STRING
|
||||
import com.itsaky.androidide.editor.schemes.IDEColorScheme
|
||||
import com.itsaky.androidide.editor.schemes.LanguageScheme
|
||||
import com.itsaky.androidide.editor.schemes.StyleDef
|
||||
import com.itsaky.androidide.editor.schemes.internal.parser.SchemeParser.EditorColors
|
||||
import java.io.File
|
||||
|
||||
/** @author Akash Yadav */
|
||||
@@ -126,6 +125,7 @@ fun IDEColorScheme.parseStyleDef(reader: JsonReader): StyleDef {
|
||||
"italic" -> def.italic = reader.nextBoolean()
|
||||
"strikethrough" -> def.strikeThrough = reader.nextBoolean()
|
||||
"completion" -> def.completion = reader.nextBoolean()
|
||||
"maybeHexColor" -> def.maybeHexColor = reader.nextBoolean()
|
||||
}
|
||||
}
|
||||
if (def.fg == 0) {
|
||||
|
||||
Reference in New Issue
Block a user