refactor: rename 'TSAnalyzeManager' to 'TsAnalyzeManager' for consistency

This commit is contained in:
Akash Yadav
2023-10-23 21:38:51 +05:30
parent 6d8cbf7a42
commit 42ae28a0b9
5 changed files with 54 additions and 443 deletions
@@ -1,112 +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 io.github.rosemoe.sora.editor.ts
import android.os.Bundle
import com.itsaky.androidide.treesitter.TSInputEdit
import io.github.rosemoe.sora.editor.ts.spans.DefaultSpanFactory
import io.github.rosemoe.sora.editor.ts.spans.TsSpanFactory
import io.github.rosemoe.sora.lang.analysis.AnalyzeManager
import io.github.rosemoe.sora.lang.analysis.StyleReceiver
import io.github.rosemoe.sora.lang.styling.Styles
import io.github.rosemoe.sora.text.CharPosition
import io.github.rosemoe.sora.text.ContentReference
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
/**
* @author Akash Yadav
*/
open class TSAnalyzeManager(val languageSpec: TsLanguageSpec, var theme: TsTheme) : AnalyzeManager {
var stylesReceiver: StyleReceiver? = null
var reference: ContentReference? = null
var spanFactory: TsSpanFactory = DefaultSpanFactory()
open var styles = Styles()
private var analyzeWorker: TsAnalyzeWorker? = null
protected val analyzerScope = CoroutineScope(
Dispatchers.Default + CoroutineName("TSAnalyzeManager"))
open fun updateTheme(theme: TsTheme) {
this.theme = theme
(styles.spans as LineSpansGenerator?)?.also {
it.theme = theme
}
}
override fun setReceiver(receiver: StyleReceiver?) {
stylesReceiver = receiver
analyzeWorker?.stylesReceiver = receiver
}
override fun reset(content: ContentReference, extraArguments: Bundle) {
reference = content
rerun()
}
override fun insert(start: CharPosition, end: CharPosition, insertedContent: CharSequence) {
val edit = TSInputEdit.create(start.index shl 1, start.index shl 1, end.index shl 1,
start.toTSPoint(), start.toTSPoint(), end.toTSPoint())
(styles.spans as LineSpansGenerator?)?.apply {
lineCount = reference!!.lineCount
edit(edit)
}
analyzeWorker?.onMod(Mod(TextMod(start.index, end.index, edit, insertedContent.toString())))
}
override fun delete(start: CharPosition, end: CharPosition, deletedContent: CharSequence) {
val edit = TSInputEdit.create(start.index shl 1, start.index shl 1, end.index shl 1,
start.toTSPoint(), start.toTSPoint(), end.toTSPoint())
(styles.spans as LineSpansGenerator?)?.apply {
lineCount = reference!!.lineCount
edit(edit)
}
analyzeWorker?.onMod(Mod(TextMod(start.index, end.index, edit, null)))
}
override fun rerun() {
analyzeWorker?.stop()
analyzeWorker = null
(styles.spans as LineSpansGenerator?)?.tree?.close()
styles.spans = null
styles = Styles()
val initText = reference?.reference?.toString() ?: ""
analyzeWorker = TsAnalyzeWorker(this, languageSpec, theme, styles, reference!!, spanFactory)
analyzeWorker!!.apply {
this.stylesReceiver = this@TSAnalyzeManager.stylesReceiver
init(Init(initText))
start(analyzerScope)
}
}
override fun destroy() {
analyzeWorker?.stop()
analyzeWorker = null
(styles.spans as LineSpansGenerator?)?.tree?.close()
styles.spans = null
spanFactory.close()
}
}
@@ -1,72 +1,60 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
/*
* This file is part of AndroidIDE.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* 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.
*
* This library 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
* Lesser General Public License for more details.
* 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 Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*
* Please contact Rosemoe by email 2073412493@qq.com if you need
* additional information or have any questions
******************************************************************************/
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.rosemoe.sora.editor.ts
import android.os.Bundle
import android.os.Message
import android.util.Log
import com.itsaky.androidide.treesitter.TSInputEdit
import com.itsaky.androidide.treesitter.TSParser
import com.itsaky.androidide.treesitter.TSQueryCursor
import com.itsaky.androidide.treesitter.TSTree
import com.itsaky.androidide.treesitter.api.TreeSitterInputEdit
import com.itsaky.androidide.treesitter.api.TreeSitterQueryCapture
import com.itsaky.androidide.treesitter.api.safeExecQueryCursor
import com.itsaky.androidide.treesitter.string.UTF16String
import com.itsaky.androidide.treesitter.string.UTF16StringFactory
import com.itsaky.androidide.utils.ILogger
import io.github.rosemoe.sora.data.ObjectAllocator
import io.github.rosemoe.sora.editor.ts.spans.DefaultSpanFactory
import io.github.rosemoe.sora.editor.ts.spans.TsSpanFactory
import io.github.rosemoe.sora.lang.analysis.AnalyzeManager
import io.github.rosemoe.sora.lang.analysis.StyleReceiver
import io.github.rosemoe.sora.lang.styling.CodeBlock
import io.github.rosemoe.sora.lang.styling.Styles
import io.github.rosemoe.sora.text.CharPosition
import io.github.rosemoe.sora.text.ContentReference
import java.util.concurrent.LinkedBlockingQueue
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
open class TsAnalyzeManager(val languageSpec: TsLanguageSpec, var theme: TsTheme) :
AnalyzeManager {
/**
* @author Akash Yadav
*/
open class TsAnalyzeManager(val languageSpec: TsLanguageSpec, var theme: TsTheme) : AnalyzeManager {
var currentReceiver: StyleReceiver? = null
var stylesReceiver: StyleReceiver? = null
var reference: ContentReference? = null
var thread: TsLooperThread? = null
var spanFactory: TsSpanFactory = DefaultSpanFactory()
open var styles = Styles()
fun updateTheme(theme: TsTheme) {
private var analyzeWorker: TsAnalyzeWorker? = null
protected val analyzerScope = CoroutineScope(
Dispatchers.Default + CoroutineName("TsAnalyzeManager"))
open fun updateTheme(theme: TsTheme) {
this.theme = theme
(styles.spans as LineSpansGenerator?)?.let {
(styles.spans as LineSpansGenerator?)?.also {
it.theme = theme
}
}
override fun setReceiver(receiver: StyleReceiver?) {
currentReceiver = receiver
stylesReceiver = receiver
analyzeWorker?.stylesReceiver = receiver
}
override fun reset(content: ContentReference, extraArguments: Bundle) {
@@ -75,313 +63,50 @@ open class TsAnalyzeManager(val languageSpec: TsLanguageSpec, var theme: TsTheme
}
override fun insert(start: CharPosition, end: CharPosition, insertedContent: CharSequence) {
thread?.offerMessage(
MSG_MOD,
TextModification(
start.index,
end.index,
TSInputEdit.create(
start.index * 2,
start.index * 2,
end.index * 2,
start.toTSPoint(),
start.toTSPoint(),
end.toTSPoint()
),
insertedContent.toString()
)
)
val edit = TSInputEdit.create(start.index shl 1, start.index shl 1, end.index shl 1,
start.toTSPoint(), start.toTSPoint(), end.toTSPoint())
(styles.spans as LineSpansGenerator?)?.apply {
lineCount = reference!!.lineCount
edit(TSInputEdit.create(
start.index * 2,
start.index * 2,
end.index * 2,
start.toTSPoint(),
start.toTSPoint(),
end.toTSPoint()
))
edit(edit)
}
analyzeWorker?.onMod(Mod(TextMod(start.index, end.index, edit, insertedContent.toString())))
}
override fun delete(start: CharPosition, end: CharPosition, deletedContent: CharSequence) {
thread?.offerMessage(
MSG_MOD,
TextModification(
start.index,
end.index,
TSInputEdit.create(
start.index * 2,
end.index * 2,
start.index * 2,
start.toTSPoint(),
end.toTSPoint(),
start.toTSPoint()
),
null
)
)
val edit = TSInputEdit.create(start.index shl 1, start.index shl 1, end.index shl 1,
start.toTSPoint(), start.toTSPoint(), end.toTSPoint())
(styles.spans as LineSpansGenerator?)?.apply {
lineCount = reference!!.lineCount
edit(TSInputEdit.create(
start.index * 2,
end.index * 2,
start.index * 2,
start.toTSPoint(),
end.toTSPoint(),
start.toTSPoint()
))
edit(edit)
}
analyzeWorker?.onMod(Mod(TextMod(start.index, end.index, edit, null)))
}
override fun rerun() {
thread?.let {
if (it.isAlive) {
it.interrupt()
it.abort = true
}
}
analyzeWorker?.stop()
analyzeWorker = null
(styles.spans as LineSpansGenerator?)?.tree?.close()
styles.spans = null
styles = Styles()
val initText = reference?.reference?.toString() ?: ""
thread = TsLooperThread().also {
it.name = "TsDaemon-${nextThreadId()}"
styles = Styles()
it.offerMessage(MSG_INIT, initText)
it.start()
analyzeWorker = TsAnalyzeWorker(this, languageSpec, theme, styles, reference!!, spanFactory)
analyzeWorker!!.apply {
this.stylesReceiver = this@TsAnalyzeManager.stylesReceiver
init(Init(initText))
start(analyzerScope)
}
}
override fun destroy() {
thread?.let {
if (it.isAlive) {
it.interrupt()
it.abort = true
}
}
analyzeWorker?.stop()
analyzeWorker = null
(styles.spans as LineSpansGenerator?)?.tree?.close()
styles.spans = null
spanFactory.close()
}
companion object {
private const val MSG_BASE = 11451400
private const val MSG_INIT = MSG_BASE + 1
private const val MSG_MOD = MSG_BASE + 2
@Volatile
private var threadId = 0
@Synchronized
fun nextThreadId() = ++threadId
private val log = ILogger.newInstance("TsAnalyzeManager")
}
inner class TsLooperThread : Thread() {
private val messageQueue = LinkedBlockingQueue<Message>()
@Volatile
var abort: Boolean = false
val localText: UTF16String = UTF16StringFactory.newString()
private val parser = TSParser.create().also {
it.language = languageSpec.language
}
var tree: TSTree? = null
fun offerMessage(what: Int, obj: Any?) {
val msg = Message.obtain()
msg.what = what
msg.obj = obj
offerMessage(msg)
}
fun offerMessage(msg: Message) {
// Result ignored: capacity is enough as it is INT_MAX
messageQueue.offer(msg)
}
fun updateStyles() {
if (thread != this || messageQueue.isNotEmpty()) {
return
}
val scopedVariables = TsScopedVariables(tree!!, localText, languageSpec)
val oldTree = (styles.spans as LineSpansGenerator?)?.tree
val copied = tree!!.copy()
styles.spans = LineSpansGenerator(
copied,
reference!!.lineCount,
reference!!.reference,
theme,
languageSpec,
scopedVariables,
spanFactory
)
val oldBlocks = styles.blocks
updateCodeBlocks()
if (oldBlocks != null) {
ObjectAllocator.recycleBlockLines(oldBlocks)
}
currentReceiver?.setStyles(this@TsAnalyzeManager, styles) {
oldTree?.close()
}
currentReceiver?.updateBracketProvider(
this@TsAnalyzeManager,
TsBracketPairs(copied, languageSpec)
)
}
fun updateCodeBlocks() {
if (languageSpec.blocksQuery.patternCount == 0
|| !languageSpec.blocksQuery.canAccess()
|| tree?.canAccess() != true
) {
return
}
val blocks = mutableListOf<CodeBlock>()
TSQueryCursor.create().use { cursor ->
cursor.safeExecQueryCursor(
query = languageSpec.blocksQuery,
tree = tree,
recycleNodeAfterUse = true,
matchCondition = null,
whileTrue = null,
onClosedOrEdited = { blocks.clear() },
debugName = "TsAnalyzeManager.updateCodeBlocks()"
) { match ->
if (!languageSpec.blocksPredicator.doPredicate(
languageSpec.predicates,
localText,
match
)
) {
return@safeExecQueryCursor
}
match.captures.forEach { capture ->
val block = ObjectAllocator.obtainBlockLine()
var node = capture.node
val start = node.startPoint
block.startLine = start.row
block.startColumn = start.column / 2
val end = if (languageSpec.blocksQuery.getCaptureNameForId(capture.index)
.endsWith(".marked")
) {
// Goto last terminal element
while (node.childCount > 0) {
node = node.getChild(node.childCount - 1)
}
node.startPoint
} else {
node.endPoint
}
block.endLine = end.row
block.endColumn = end.column / 2
if (block.endLine - block.startLine > 1) {
blocks.add(block)
}
(capture as? TreeSitterQueryCapture?)?.recycle()
}
}
}
val distinct = blocks.asSequence().distinct().toMutableList()
styles.blocks = distinct
styles.finishBuilding()
}
override fun run() {
try {
while (!abort && !isInterrupted) {
val msg = messageQueue.take()
if (!handleMessage(msg)) {
break
}
msg.recycle()
}
} catch (e: InterruptedException) {
// ignored
}
releaseThreadResources()
}
fun handleMessage(msg: Message): Boolean {
try {
when (msg.what) {
MSG_INIT -> {
localText.append(msg.obj!! as String)
if (!abort && !isInterrupted) {
if (parser.isParsing) {
parser.requestCancellation()
}
tree = parser.parseString(localText)
updateStyles()
}
}
MSG_MOD -> {
if (!abort && !isInterrupted) {
val modification = msg.obj!! as TextModification
val newText = modification.changedText
val t = tree!!
t.edit(modification.tsEdition)
if (newText == null) {
localText.delete(modification.start, modification.end)
} else {
if (modification.start == localText.length) {
localText.append(newText)
} else {
localText.insert(modification.start, newText)
}
}
(modification.tsEdition as? TreeSitterInputEdit?)?.recycle()
if (tree != null && parser.isParsing) {
parser.requestCancellation()
}
tree = parser.parseString(t, localText)
t.close()
updateStyles()
}
}
}
return true
} catch (e: Exception) {
Log.w(
"TsAnalyzeManager",
"Thread $name exited with an error",
e
)
}
return false
}
fun releaseThreadResources() {
parser.close()
tree?.close()
localText.close()
}
}
private data class TextModification(
val start: Int,
val end: Int,
val tsEdition: TSInputEdit,
/**
* null for deletion
*/
val changedText: String?
)
}
@@ -32,7 +32,6 @@ import io.github.rosemoe.sora.lang.analysis.StyleReceiver
import io.github.rosemoe.sora.lang.styling.CodeBlock
import io.github.rosemoe.sora.lang.styling.Styles
import io.github.rosemoe.sora.text.ContentReference
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -47,7 +46,7 @@ import java.util.concurrent.LinkedBlockingQueue
* @author Akash Yadav
*/
internal class TsAnalyzeWorker(
private val analyzer: TSAnalyzeManager,
private val analyzer: TsAnalyzeManager,
private val languageSpec: TsLanguageSpec,
private val theme: TsTheme,
private val styles: Styles,
@@ -61,7 +61,7 @@ open class TsLanguage(
protected var tsTheme = TsThemeBuilder(languageSpec.tsQuery).apply { themeDescription() }.theme
open val analyzer by lazy {
TSAnalyzeManager(languageSpec, tsTheme)
TsAnalyzeManager(languageSpec, tsTheme)
}
/**
@@ -18,7 +18,6 @@
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.TsAnalyzeManager
import io.github.rosemoe.sora.editor.ts.TsLanguageSpec
import io.github.rosemoe.sora.editor.ts.TsTheme
@@ -32,7 +31,7 @@ import io.github.rosemoe.sora.lang.styling.Styles
class TreeSitterAnalyzeManager(
languageSpec: TsLanguageSpec,
theme: TsTheme
) : TSAnalyzeManager(languageSpec, theme) {
) : TsAnalyzeManager(languageSpec, theme) {
override var styles: Styles = Styles()
set(value) {