Skip to content
This repository has been archived by the owner on Oct 18, 2024. It is now read-only.

Commit

Permalink
fix(editor): support static span colors for hex colors
Browse files Browse the repository at this point in the history
  • Loading branch information
itsaky committed Sep 25, 2023
1 parent 30a03cb commit ce4295b
Show file tree
Hide file tree
Showing 17 changed files with 277 additions and 24 deletions.
3 changes: 2 additions & 1 deletion editor/src/main/assets/editor/schemes/default-dark/java.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
},
"string": {
"fg": "@string",
"completion": false
"completion": false,
"maybeHexColor": true
},
"variable.builtin": {
"fg": "@keyword",
Expand Down
5 changes: 4 additions & 1 deletion editor/src/main/assets/editor/schemes/default-dark/json.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
},
"string": {
"fg": "@string",
"completion": false
"completion": false,
"maybeHexColor": true
},
"string.regex": {
"fg": "@string",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 8 additions & 1 deletion editor/src/main/assets/editor/schemes/default-dark/xml.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
3 changes: 2 additions & 1 deletion editor/src/main/assets/editor/schemes/default/java.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
},
"string": {
"fg": "@string",
"completion": false
"completion": false,
"maybeHexColor": true
},
"variable.builtin": {
"fg": "@keyword",
Expand Down
5 changes: 4 additions & 1 deletion editor/src/main/assets/editor/schemes/default/json.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
3 changes: 2 additions & 1 deletion editor/src/main/assets/editor/schemes/default/kotlin.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
},
"string": {
"fg": "@string",
"completion": false
"completion": false,
"maybeHexColor": true
},
"string.regex": {
"fg": "@string",
Expand Down
2 changes: 1 addition & 1 deletion editor/src/main/assets/editor/schemes/default/scheme.prop
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 8 additions & 1 deletion editor/src/main/assets/editor/schemes/default/xml.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
2 changes: 2 additions & 0 deletions editor/src/main/assets/editor/treesitter/xml/highlights.scm
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
(cdata_end) @cdata.end
(cdata) @cdata.data

(char_data) @text

(eq) @operator

["<" "/" ">" "<?" "?>"] @operator
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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 {
Expand All @@ -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()) }
}
}
Expand All @@ -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 {
Expand Down Expand Up @@ -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. */
Expand Down
Original file line number Diff line number Diff line change
@@ -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())
}
}
Original file line number Diff line number Diff line change
@@ -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>
Loading

1 comment on commit ce4295b

@abmsharif1
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this color update available next version?

Please sign in to comment.