feat: use tree-sitter for log highlighting

This commit is contained in:
Akash Yadav
2023-04-22 19:26:53 +05:30
parent f89b832b77
commit e78b9068f4
17 changed files with 324 additions and 192 deletions
+1 -1
View File
@@ -221,4 +221,4 @@
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
</project>
@@ -34,6 +34,7 @@ import com.itsaky.androidide.actions.ActionsRegistry.Companion.getInstance
import com.itsaky.androidide.editor.language.java.JavaLanguage
import com.itsaky.androidide.editor.language.json.JsonLanguage
import com.itsaky.androidide.editor.language.kotlin.KotlinLanguage
import com.itsaky.androidide.editor.language.log.LogLanguage
import com.itsaky.androidide.editor.language.treesitter.TSLanguageRegistry
import com.itsaky.androidide.editor.language.xml.XMLLanguage
import com.itsaky.androidide.editor.schemes.IDEColorSchemeProvider
@@ -59,6 +60,8 @@ import java.io.File
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.set
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
/**
* Base class for EditorActivity. Handles logic for working with file editors.
@@ -124,6 +127,7 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
TSLanguageRegistry.instance.register(JavaLanguage.TS_TYPE, JavaLanguage.FACTORY)
TSLanguageRegistry.instance.register(KotlinLanguage.TS_TYPE_KT, KotlinLanguage.FACTORY)
TSLanguageRegistry.instance.register(KotlinLanguage.TS_TYPE_KTS, KotlinLanguage.FACTORY)
TSLanguageRegistry.instance.register(LogLanguage.TS_TYPE, LogLanguage.FACTORY)
TSLanguageRegistry.instance.register(JsonLanguage.TS_TYPE, JsonLanguage.FACTORY)
TSLanguageRegistry.instance.register(XMLLanguage.TS_TYPE, XMLLanguage.FACTORY)
IDEColorSchemeProvider.initIfNeeded()
@@ -54,6 +54,7 @@ class IDEApplication : BaseApplication() {
System.loadLibrary("tree-sitter-java")
System.loadLibrary("tree-sitter-json")
System.loadLibrary("tree-sitter-kotlin")
System.loadLibrary("tree-sitter-log")
System.loadLibrary("tree-sitter-xml")
}
}
@@ -27,8 +27,8 @@ import androidx.fragment.app.Fragment
import com.blankj.utilcode.util.ThreadUtils
import com.itsaky.androidide.databinding.FragmentLogBinding
import com.itsaky.androidide.editor.language.log.LogLanguage
import com.itsaky.androidide.editor.schemes.IDEColorSchemeProvider
import com.itsaky.androidide.models.LogLine
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.ILogger.Priority
import com.itsaky.androidide.utils.jetbrainsMono
@@ -82,7 +82,6 @@ abstract class LogViewFragment : Fragment(), ShareableOutputFragment {
object : Runnable {
override fun run() {
cacheLock.withLock {
if (cacheLineTrack.size == MAX_LINE_COUNT) {
cache.delete(0, cacheLineTrack.poll()!!)
}
@@ -194,8 +193,10 @@ abstract class LogViewFragment : Fragment(), ShareableOutputFragment {
editor.typefaceLineNumber = jetbrainsMono()
editor.setTextSize(12f)
editor.typefaceText = jetbrainsMono()
editor.colorScheme = SchemeAndroidIDE.newInstance(requireContext())
editor.setEditorLanguage(LogLanguage())
IDEColorSchemeProvider.readScheme(requireContext()) { scheme ->
editor.applyTreeSitterLang(LogLanguage(requireContext()), LogLanguage.TS_TYPE, scheme)
}
}
override fun onDestroyView() {
+1
View File
@@ -23,6 +23,7 @@ dependencies {
api(libs.androidide.ts.java)
api(libs.androidide.ts.json)
api(libs.androidide.ts.kotlin)
api(libs.androidide.ts.log)
api(libs.androidide.ts.xml)
api(libs.common.editor)
api(libs.common.editor.ts)
@@ -38,13 +38,34 @@
"ns_prefix": "#ce93d8",
"json.key": "#ba68c8",
"json.escape": "#2196f3"
"json.escape": "#2196f3",
"log.err.text": "#f44336",
"log.err.pr.fg" : "#000000",
"log.err.pr.bg" : "#f44336",
"log.warn.text": "#FFEB3B",
"log.warn.pr.fg" : "#000000",
"log.warn.pr.bg" : "#FFEB3B",
"log.info.text": "#4CAF50",
"log.info.pr.fg" : "#ffffff",
"log.info.pr.bg" : "#1f65c0",
"log.debug.text": "#f5f5f5",
"log.debug.pr.fg" : "#ffffff",
"log.debug.pr.bg" : "#9e9d24",
"log.verbose.text": "#4fc3f7",
"log.verbose.pr.fg" : "#000000",
"log.verbose.pr.bg" : "#ffffff"
},
"editor": "@editor.json",
"languages": [
"@java.json",
"@json.json",
"@kotlin.json",
"@xml.json"
"@xml.json",
"@log.json"
]
}
@@ -0,0 +1,63 @@
{
"types": ["log"],
"styles": {
"begin_header" : {
"fg": "@comment",
"italic": true
},
"err.date": "@log.err.text",
"err.time": "@log.err.text",
"err.pid": "@log.err.text",
"err.tid": "@log.err.text",
"err.tag" : "@log.err.text",
"err.message": "@log.err.text",
"err.priority": {
"fg": "@log.err.pr.fg",
"bg": "@log.err.pr.bg"
},
"warn.date": "@log.warn.text",
"warn.time": "@log.warn.text",
"warn.pid": "@log.warn.text",
"warn.tid": "@log.warn.text",
"warn.tag" : "@log.warn.text",
"warn.message": "@log.warn.text",
"warn.priority": {
"fg": "@log.warn.pr.fg",
"bg": "@log.warn.pr.bg"
},
"info.date": "@log.info.text",
"info.time": "@log.info.text",
"info.pid": "@log.info.text",
"info.tid": "@log.info.text",
"info.tag" : "@log.info.text",
"info.message": "@log.info.text",
"info.priority": {
"fg": "@log.info.pr.fg",
"bg": "@log.info.pr.bg"
},
"debug.date": "@log.debug.text",
"debug.time": "@log.debug.text",
"debug.pid": "@log.debug.text",
"debug.tid": "@log.debug.text",
"debug.tag" : "@log.debug.text",
"debug.message": "@log.debug.text",
"debug.priority": {
"fg": "@log.debug.pr.fg",
"bg": "@log.debug.pr.bg"
},
"verbose.date": "@log.verbose.text",
"verbose.time": "@log.verbose.text",
"verbose.pid": "@log.verbose.text",
"verbose.tid": "@log.verbose.text",
"verbose.tag" : "@log.verbose.text",
"verbose.message": "@log.verbose.text",
"verbose.priority": {
"fg": "@log.verbose.pr.fg",
"bg": "@log.verbose.pr.bg"
}
}
}
@@ -2,7 +2,7 @@
scheme.name=AndroidIDE Default - Dark
# The version code of the color scheme
scheme.version=9
scheme.version=10
# Whether the scheme is dark or light
scheme.isDark=true
@@ -39,13 +39,34 @@
"ns_prefix": "#9c27b0",
"json.key": "#1976d2",
"json.escape": "#2196f3"
"json.escape": "#2196f3",
"log.err.text": "#f44336",
"log.err.pr.fg" : "#000000",
"log.err.pr.bg" : "#f44336",
"log.warn.text": "#ffab00",
"log.warn.pr.fg" : "#000000",
"log.warn.pr.bg" : "#FFEB3B",
"log.info.text": "#4CAF50",
"log.info.pr.fg" : "#ffffff",
"log.info.pr.bg" : "#1f65c0",
"log.debug.text": "#212121",
"log.debug.pr.fg" : "#ffffff",
"log.debug.pr.bg" : "#9e9d24",
"log.verbose.text": "#0288d1",
"log.verbose.pr.fg" : "#000000",
"log.verbose.pr.bg" : "#ffffff"
},
"editor": "@editor.json",
"languages": [
"@java.json",
"@json.json",
"@kotlin.json",
"@xml.json"
"@xml.json",
"@log.json"
]
}
@@ -0,0 +1,63 @@
{
"types": ["log"],
"styles": {
"begin_header" : {
"fg": "@comment",
"italic": true
},
"err.date": "@log.err.text",
"err.time": "@log.err.text",
"err.pid": "@log.err.text",
"err.tid": "@log.err.text",
"err.tag" : "@log.err.text",
"err.msg": "@log.err.text",
"err.priority": {
"fg": "@log.err.pr.fg",
"bg": "@log.err.pr.bg"
},
"warn.date": "@log.warn.text",
"warn.time": "@log.warn.text",
"warn.pid": "@log.warn.text",
"warn.tid": "@log.warn.text",
"warn.tag" : "@log.warn.text",
"warn.msg": "@log.warn.text",
"warn.priority": {
"fg": "@log.warn.pr.fg",
"bg": "@log.warn.pr.bg"
},
"info.date": "@log.info.text",
"info.time": "@log.info.text",
"info.pid": "@log.info.text",
"info.tid": "@log.info.text",
"info.tag" : "@log.info.text",
"info.msg": "@log.info.text",
"info.priority": {
"fg": "@log.info.pr.fg",
"bg": "@log.info.pr.bg"
},
"debug.date": "@log.debug.text",
"debug.time": "@log.debug.text",
"debug.pid": "@log.debug.text",
"debug.tid": "@log.debug.text",
"debug.tag" : "@log.debug.text",
"debug.msg": "@log.debug.text",
"debug.priority": {
"fg": "@log.debug.pr.fg",
"bg": "@log.debug.pr.bg"
},
"verbose.date": "@log.verbose.text",
"verbose.time": "@log.verbose.text",
"verbose.pid": "@log.verbose.text",
"verbose.tid": "@log.verbose.text",
"verbose.tag" : "@log.verbose.text",
"verbose.msg": "@log.verbose.text",
"verbose.priority": {
"fg": "@log.verbose.pr.fg",
"bg": "@log.verbose.pr.bg"
}
}
}
@@ -2,7 +2,7 @@
scheme.name=AndroidIDE Default
# The version code of the color scheme
scheme.version=9
scheme.version=10
# Whether the scheme is dark or light
scheme.isDark=false
@@ -0,0 +1,91 @@
(begin_header) @header
(log_line
(date) @err.date
(time) @err.time
(pid) @err.pid
(tid) @err.tid
(priority) @err.priority
(tag) @err.tag
(message) @err.msg
(#match? @err.priority "E")
)
(log_line
(date) @warn.date
(time) @warn.time
(pid) @warn.pid
(tid) @warn.tid
(priority) @warn.priority
(tag) @warn.tag
(message) @warn.msg
(#match? @warn.priority "W")
)
(log_line
(date) @info.date
(time) @info.time
(pid) @info.pid
(tid) @info.tid
(priority) @info.priority
(tag) @info.tag
(message) @info.msg
(#match? @info.priority "I")
)
(log_line
(date) @debug.date
(time) @debug.time
(pid) @debug.pid
(tid) @debug.tid
(priority) @debug.priority
(tag) @debug.tag
(message) @debug.msg
(#match? @debug.priority "I")
)
(log_line
(date) @verbose.date
(time) @verbose.time
(pid) @verbose.pid
(tid) @verbose.tid
(priority) @verbose.priority
(tag) @verbose.tag
(message) @verbose.msg
(#match? @verbose.priority "V")
)
(ide_log_line
(ide_tag) @err.tag
(priority) @err.priority
(message) @err.msg
(#match? @err.priority "E")
)
(ide_log_line
(ide_tag) @warn.tag
(priority) @warn.priority
(message) @warn.msg
(#match? @warn.priority "W")
)
(ide_log_line
(ide_tag) @info.tag
(priority) @info.priority
(message) @info.msg
(#match? @info.priority "I")
)
(ide_log_line
(ide_tag) @debug.tag
(priority) @debug.priority
(message) @debug.msg
(#match? @debug.priority "D")
)
(ide_log_line
(ide_tag) @verbose.tag
(priority) @verbose.priority
(message) @verbose.msg
(#match? @verbose.priority "V")
)
@@ -17,31 +17,22 @@
package com.itsaky.androidide.editor.language.log
import android.os.Bundle
import com.itsaky.androidide.editor.language.IDELanguage
import io.github.rosemoe.sora.lang.completion.CompletionPublisher
import io.github.rosemoe.sora.lang.smartEnter.NewlineHandler
import io.github.rosemoe.sora.text.CharPosition
import io.github.rosemoe.sora.text.ContentReference
import io.github.rosemoe.sora.widget.SymbolPairMatch.DefaultSymbolPairs
import android.content.Context
import com.itsaky.androidide.editor.language.treesitter.TreeSitterLanguage
import com.itsaky.androidide.editor.language.treesitter.TreeSitterLanguage.Factory
import com.itsaky.androidide.treesitter.log.TSLanguageLog
/** @author Akash Yadav */
class LogLanguage @JvmOverloads constructor(val analyzer: LogLineAnalyzer = LogLineAnalyzer()) :
IDELanguage() {
/**
* Tree Sitter language implementation for logs.
*
* @author Akash Yadav
*/
class LogLanguage(context: Context) :
TreeSitterLanguage(context, TSLanguageLog.newInstance(), TS_TYPE) {
override fun getAnalyzeManager() = analyzer
companion object {
const val TS_TYPE = "log"
override fun requireAutoComplete(
content: ContentReference,
position: CharPosition,
publisher: CompletionPublisher,
extraArguments: Bundle
) {}
override fun destroy() {}
override fun getInterruptionLevel() = INTERRUPTION_LEVEL_NONE
override fun getIndentAdvance(content: ContentReference, line: Int, column: Int) = 0
override fun getSymbolPairs() = DefaultSymbolPairs()
override fun getNewlineHandlers() = emptyArray<NewlineHandler>()
@JvmField val FACTORY = Factory { LogLanguage(it) }
}
}
@@ -1,152 +0,0 @@
/*
* 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.log
import com.itsaky.androidide.editor.language.log.LogLineAnalyzer.LogLineState
import com.itsaky.androidide.lexers.log.LogLineTokenizer
import com.itsaky.androidide.lexers.log.LogToken
import com.itsaky.androidide.lexers.log.LogToken.PRIORITY
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.LOG_PRIORITY_BG_DEBUG
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.LOG_PRIORITY_BG_ERROR
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.LOG_PRIORITY_BG_INFO
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.LOG_PRIORITY_BG_VERBOSE
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.LOG_PRIORITY_BG_WARNING
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.LOG_PRIORITY_FG_DEBUG
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.LOG_PRIORITY_FG_ERROR
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.LOG_PRIORITY_FG_INFO
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.LOG_PRIORITY_FG_VERBOSE
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.LOG_PRIORITY_FG_WARNING
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.LOG_TEXT_DEBUG
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.LOG_TEXT_ERROR
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.LOG_TEXT_INFO
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.LOG_TEXT_VERBOSE
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.LOG_TEXT_WARNING
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.get
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.ILogger.Priority.DEBUG
import com.itsaky.androidide.utils.ILogger.Priority.ERROR
import com.itsaky.androidide.utils.ILogger.Priority.INFO
import com.itsaky.androidide.utils.ILogger.Priority.VERBOSE
import com.itsaky.androidide.utils.ILogger.Priority.WARNING
import io.github.rosemoe.sora.lang.analysis.AsyncIncrementalAnalyzeManager
import io.github.rosemoe.sora.lang.analysis.IncrementalAnalyzeManager.LineTokenizeResult
import io.github.rosemoe.sora.lang.styling.CodeBlock
import io.github.rosemoe.sora.lang.styling.Span
import io.github.rosemoe.sora.lang.styling.TextStyle
import io.github.rosemoe.sora.text.Content
/**
* Analyzer for log lines.
*
* @author Akash Yadav
*/
class LogLineAnalyzer : AsyncIncrementalAnalyzeManager<LogLineState, LogToken>() {
class LogLineState
override fun getInitialState() = LogLineState()
override fun stateEquals(state: LogLineState?, another: LogLineState?) =
state?.equals(another) ?: false
override fun computeBlocks(text: Content?, delegate: CodeBlockAnalyzeDelegate?) =
mutableListOf<CodeBlock>()
override fun tokenizeLine(
lineText: CharSequence,
state: LogLineState,
line: Int
): LineTokenizeResult<LogLineState, LogToken> {
val input = lineText.toArray()
val tokenizer = LogLineTokenizer(input)
val tokens = tokenizer.allTokens()
return LineTokenizeResult(LogLineState(), tokens)
}
override fun generateSpansForLine(
result: LineTokenizeResult<LogLineState, LogToken>
): MutableList<Span> {
val tokens = result.tokens
val spans = mutableListOf<Span>()
for (token in tokens) {
when (token.type) {
PRIORITY -> {
val priority = ILogger.priority(token.text[0].uppercaseChar())!!
val text: Int
val foreground: Int
val background: Int
when (priority) {
DEBUG -> {
text = LOG_TEXT_DEBUG
foreground = LOG_PRIORITY_FG_DEBUG
background = LOG_PRIORITY_BG_DEBUG
}
INFO -> {
text = LOG_TEXT_INFO
foreground = LOG_PRIORITY_FG_INFO
background = LOG_PRIORITY_BG_INFO
}
ERROR -> {
text = LOG_TEXT_ERROR
foreground = LOG_PRIORITY_FG_ERROR
background = LOG_PRIORITY_BG_ERROR
}
WARNING -> {
text = LOG_TEXT_WARNING
foreground = LOG_PRIORITY_FG_WARNING
background = LOG_PRIORITY_BG_WARNING
}
VERBOSE -> {
text = LOG_TEXT_VERBOSE
foreground = LOG_PRIORITY_FG_VERBOSE
background = LOG_PRIORITY_BG_VERBOSE
}
}
val span = Span.obtain(0, get(text))
spans.add(span)
val pSpan =
Span.obtain(
token.startIndex - 1,
TextStyle.makeStyle(
/* foregroundColorId = */ foreground,
/* backgroundColorId = */ background,
/* bold = */ false,
/* italic = */ false,
/* strikeThrough = */ false,
/* noCompletion = */ true
)
)
spans.add(pSpan)
// Add the normal color span after the priority tag
spans.add(span.copy().apply { column = token.endIndex + 2 })
}
}
}
if (spans.isEmpty()) {
spans.add(Span.obtain(0, get(LOG_TEXT_DEBUG)))
}
return spans
}
private fun CharSequence.toArray(): CharArray {
return CharArray(this.length) { this[it] }
}
}
@@ -93,6 +93,10 @@ abstract class TreeSitterLanguage(context: Context, lang: TSLanguage, type: Stri
return newlineHandlersLazy
}
override fun getInterruptionLevel(): Int {
return INTERRUPTION_LEVEL_STRONG
}
override fun getIndentAdvance(content: ContentReference, line: Int, column: Int): Int {
return computeIndent(
content.toString(),
@@ -56,7 +56,13 @@ object LanguageSpecProvider {
bracketsScmSource = readScheme(context, type, "brackets"),
localsCaptureSpec = localsCaptureSpec,
predicates =
listOf(MatchPredicate, NotMatchPredicate, EqualPredicate, NotEqualPredicate, AnyOfPredicate)
listOf(
MatchPredicate,
NotMatchPredicate,
EqualPredicate,
NotEqualPredicate,
AnyOfPredicate
)
)
return TreeSitterLanguageSpec(
spec = editorLangSpec,
@@ -73,5 +79,16 @@ object LanguageSpecProvider {
}
""
}
<<<<<<< HEAD
=======
if (assests.list("$BASE_SPEC_PATH/$type")?.contains("$name.scm") == false) {
if (type != "log" || name == "highlights") {
log.warn("Scheme file '$name' for type '$type' not found")
}
return ""
}
return assests.open("${BASE_SPEC_PATH}/${type}/${name}.scm").reader().readText()
>>>>>>> c75ab30e3 (feat: use tree-sitter for log highlighting)
}
}
@@ -955,8 +955,14 @@ public class IDEEditor extends CodeEditor implements IEditor, ILspEditor {
}
}
private void applyTreeSitterLang(final Language language, final String extension,
SchemeAndroidIDE scheme
public void applyTreeSitterLang(final TreeSitterLanguage language, final String type,
@Nullable SchemeAndroidIDE scheme
) {
applyTreeSitterLang(((Language) language), type, scheme);
}
private void applyTreeSitterLang(final Language language, final String type,
@Nullable SchemeAndroidIDE scheme
) {
if (scheme == null) {
LOG.error("Failed to read current color scheme");
@@ -964,8 +970,8 @@ public class IDEEditor extends CodeEditor implements IEditor, ILspEditor {
}
if (scheme instanceof IDEColorScheme &&
((IDEColorScheme) scheme).getLanguageScheme(extension) == null) {
LOG.warn("Color scheme does not support file type '" + extension + "'");
((IDEColorScheme) scheme).getLanguageScheme(type) == null) {
LOG.warn("Color scheme does not support file type '" + type + "'");
scheme = SchemeAndroidIDE.newInstance(getContext());
}