fix: include language-treesitter from sora-editor locally

This commit is contained in:
Akash Yadav
2023-10-13 23:03:57 +05:30
parent 0b22af8993
commit 971fc605ff
38 changed files with 2210 additions and 13 deletions
+5 -2
View File
@@ -18,14 +18,14 @@
<entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.18.0/89b684257096f548fa39a7df9fdaa409d4d4df91/error_prone_annotations-2.18.0.jar" />
<entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/2.8/c85270e307e7b822f1086b93689124b89768e273/j2objc-annotations-2.8.jar" />
</processorPath>
<module name="com.itsaky.androidide.annotations.AndroidIDE.annotation-processors.main" />
<module name="ide.AndroidIDE.annotation-processors.main" />
</profile>
<profile name="Gradle Imported" enabled="true">
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="false">
<entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.auto.value/auto-value/1.10.4/90f9629eaa123f88551cc26a64bc386967ee24cc/auto-value-1.10.4.jar" />
</processorPath>
<module name="com.itsaky.androidide.AndroidIDE.subprojects.google-java-format.main" />
<module name="google-java-format.AndroidIDE.subprojects.google-java-format.main" />
</profile>
</annotationProcessing>
<bytecodeTargetLevel target="1.8">
@@ -147,6 +147,9 @@
<module name="AndroidIDE.testing.tooling-api.test" target="11" />
<module name="AndroidIDE.testing.unit.test" target="11" />
<module name="AndroidIDE.treesitter.test" target="1.7" />
<module name="composite-builds.plugins" target="17" />
<module name="composite-builds.plugins.main" target="17" />
<module name="composite-builds.plugins.test" target="17" />
</bytecodeTargetLevel>
</component>
</project>
+1
View File
@@ -0,0 +1 @@
/build
+30
View File
@@ -0,0 +1,30 @@
# Tree Sitter support for AndroidIDE
This module has been adapted from the [sora-editor](https://github.com/Rosemoe/sora-editor)'
s `language-treesitter` module.
## License
```
sora-editor - the awesome code editor for Android
https://github.com/Rosemoe/sora-editor
Copyright (C) 2020-2023 Rosemoe
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.
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.
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
```
+37
View File
@@ -0,0 +1,37 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
plugins {
id("com.android.library")
id("kotlin-android")
}
android {
namespace = "io.github.rosemoe.sora.ts"
}
dependencies {
api(libs.common.editor)
api(libs.androidide.ts)
}
+21
View 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
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ sora-editor - the awesome code editor for Android
~ https://github.com/Rosemoe/sora-editor
~ Copyright (C) 2020-2023 Rosemoe
~
~ 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.
~
~ 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.
~
~ 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
@@ -0,0 +1,238 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts
import com.itsaky.androidide.treesitter.TSQueryCapture
import com.itsaky.androidide.treesitter.TSQueryCursor
import com.itsaky.androidide.treesitter.TSTree
import io.github.rosemoe.sora.editor.ts.spans.TsSpanFactory
import io.github.rosemoe.sora.lang.styling.Span
import io.github.rosemoe.sora.lang.styling.Spans
import io.github.rosemoe.sora.lang.styling.TextStyle
import io.github.rosemoe.sora.text.CharPosition
import io.github.rosemoe.sora.text.Content
import io.github.rosemoe.sora.widget.schemes.EditorColorScheme
/**
* Spans generator for tree-sitter. Results are cached.
*
* Note that this implementation does not support external modifications.
*
* @author Rosemoe
*/
class LineSpansGenerator(
internal var tree: TSTree, internal var lineCount: Int,
private val content: Content, internal var theme: TsTheme,
private val languageSpec: TsLanguageSpec, var scopedVariables: TsScopedVariables,
private val spanFactory: TsSpanFactory
) : Spans {
companion object {
const val CACHE_THRESHOLD = 60
}
private val caches = mutableListOf<SpanCache>()
fun queryCache(line: Int): MutableList<Span>? {
for (i in 0 until caches.size) {
val cache = caches[i]
if (cache.line == line) {
caches.removeAt(i)
caches.add(0, cache)
return cache.spans
}
}
return null
}
fun pushCache(line: Int, spans: MutableList<Span>) {
while (caches.size >= CACHE_THRESHOLD && caches.size > 0) {
caches.removeAt(caches.size - 1)
}
caches.add(0, SpanCache(spans, line))
}
fun captureRegion(startIndex: Int, endIndex: Int): MutableList<Span> {
val list = mutableListOf<Span>()
val captures = mutableListOf<TSQueryCapture>()
TSQueryCursor.create().use { cursor ->
cursor.setByteRange(startIndex * 2, endIndex * 2)
cursor.exec(languageSpec.tsQuery, tree.rootNode)
var match = cursor.nextMatch()
while (match != null) {
if (languageSpec.queryPredicator.doPredicate(
languageSpec.predicates,
content,
match
)
) {
captures.addAll(match.captures)
}
match = cursor.nextMatch()
}
captures.sortBy { it.node.startByte }
var lastIndex = 0
captures.forEach { capture ->
val startByte = capture.node.startByte
val endByte = capture.node.endByte
val start = (startByte / 2 - startIndex).coerceAtLeast(0)
val pattern = capture.index
// Do not add span for overlapping regions and out-of-bounds regions
if (start >= lastIndex && endByte / 2 >= startIndex && startByte / 2 < endIndex
&& (pattern !in languageSpec.localsScopeIndices && pattern !in languageSpec.localsDefinitionIndices
&& pattern !in languageSpec.localsDefinitionValueIndices && pattern !in languageSpec.localsMembersScopeIndices)
) {
if (start != lastIndex) {
list.addAll(
createSpans(
capture,
lastIndex,
start - 1,
theme.normalTextStyle
)
)
}
var style = 0L
if (capture.index in languageSpec.localsReferenceIndices) {
val def = scopedVariables.findDefinition(
startByte / 2,
endByte / 2,
content.substring(startByte / 2, endByte / 2)
)
if (def != null && def.matchedHighlightPattern != -1) {
style = theme.resolveStyleForPattern(def.matchedHighlightPattern)
}
// This reference can not be resolved to its definition
// but it can have its own fallback color by other captures
// so continue to next capture
if (style == 0L) {
return@forEach
}
}
if (style == 0L) {
style = theme.resolveStyleForPattern(capture.index)
}
if (style == 0L) {
style = theme.normalTextStyle
}
val end = (endByte / 2 - startIndex).coerceAtMost(endIndex)
list.addAll(createSpans(capture, start, end, style))
lastIndex = end
}
}
if (lastIndex != endIndex) {
list.add(emptySpan(lastIndex))
}
}
if (list.isEmpty()) {
list.add(emptySpan(0))
}
return list
}
private fun createSpans(
capture: TSQueryCapture,
startColumn: Int,
endColumn: Int,
style: Long
): List<Span> {
val spans = spanFactory.createSpans(capture, startColumn, style)
if (spans.size > 1) {
var prevCol = spans[0].column
if (prevCol > endColumn) {
throw IndexOutOfBoundsException("Span's column is out of bounds! column=$prevCol, endColumn=$endColumn")
}
for (i in 1..spans.lastIndex) {
val col = spans[i].column
if (col <= prevCol) {
throw IllegalStateException("Spans must not overlap! prevCol=$prevCol, col=$col")
}
if (col > endColumn) {
throw IndexOutOfBoundsException("Span's column is out of bounds! column=$col, endColumn=$endColumn")
}
prevCol = col
}
}
return spans
}
private fun emptySpan(column: Int): Span {
return Span.obtain(column, TextStyle.makeStyle(EditorColorScheme.TEXT_NORMAL))
}
override fun adjustOnInsert(start: CharPosition, end: CharPosition) {
}
override fun adjustOnDelete(start: CharPosition, end: CharPosition) {
}
override fun read() = object : Spans.Reader {
private var spans = mutableListOf<Span>()
override fun moveToLine(line: Int) {
if (line < 0 || line >= lineCount) {
spans = mutableListOf()
return
}
val cached = queryCache(line)
if (cached != null) {
spans = cached
return
}
val start = content.indexer.getCharPosition(line, 0).index
val end = start + content.getColumnCount(line)
spans = captureRegion(start, end)
pushCache(line, spans)
}
override fun getSpanCount() = spans.size
override fun getSpanAt(index: Int) = spans[index]
override fun getSpansOnLine(line: Int): MutableList<Span> {
val cached = queryCache(line)
if (cached != null) {
return ArrayList(cached)
}
val start = content.indexer.getCharPosition(line, 0).index
val end = start + content.getColumnCount(line)
return captureRegion(start, end)
}
}
override fun supportsModify() = false
override fun modify(): Spans.Modifier {
throw UnsupportedOperationException()
}
override fun getLineCount() = lineCount
}
data class SpanCache(val spans: MutableList<Span>, val line: Int)
@@ -0,0 +1,54 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts
/**
* Defines what type of locals pattern of a capture
*
* @author Rosemoe
*/
open class LocalsCaptureSpec {
companion object {
val DEFAULT = LocalsCaptureSpec()
}
open fun isDefinitionValueCapture(captureName: String) = captureName == "local.definition-value"
open fun isDefinitionCapture(captureName: String) = captureName == "local.definition"
open fun isReferenceCapture(captureName: String) = captureName == "local.reference"
open fun isScopeCapture(captureName: String) = captureName == "local.scope"
/**
* Usually, variables in a scope take effect after their declarations. This special scope
* indicates that, all variables in this scope (but not in its sub-scope), take effect in this
* scope, no matter where they are.
* For example, class member fields.
*/
open fun isMembersScopeCapture(captureName: String) = captureName == "local.scope.members"
}
@@ -0,0 +1,356 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
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.string.UTF16String
import com.itsaky.androidide.treesitter.string.UTF16StringFactory
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
open class TsAnalyzeManager(val languageSpec: TsLanguageSpec, var theme: TsTheme) :
AnalyzeManager {
var currentReceiver: StyleReceiver? = null
var reference: ContentReference? = null
var extraArguments: Bundle? = null
var thread: TsLooperThread? = null
var spanFactory : TsSpanFactory = DefaultSpanFactory()
open var styles = Styles()
fun updateTheme(theme: TsTheme) {
this.theme = theme
(styles.spans as LineSpansGenerator?)?.let {
it.theme = theme
}
}
override fun setReceiver(receiver: StyleReceiver?) {
currentReceiver = receiver
}
override fun reset(content: ContentReference, extraArguments: Bundle) {
reference = content
this.extraArguments = extraArguments
rerun()
}
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()
)
)
(styles.spans as LineSpansGenerator?)?.apply {
lineCount = reference!!.lineCount
tree.edit(
TSInputEdit.create(
start.index * 2,
start.index * 2,
end.index * 2,
start.toTSPoint(),
start.toTSPoint(),
end.toTSPoint()
)
)
}
}
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
)
)
(styles.spans as LineSpansGenerator?)?.apply {
lineCount = reference!!.lineCount
tree.edit(
TSInputEdit.create(
start.index * 2,
end.index * 2,
start.index * 2,
start.toTSPoint(),
end.toTSPoint(),
start.toTSPoint()
)
)
}
}
override fun rerun() {
thread?.let {
if (it.isAlive) {
it.interrupt()
it.abort = true
}
}
(styles.spans as LineSpansGenerator?)?.tree?.close()
styles.spans = null
val initText = reference?.reference?.toString() ?: ""
thread = TsLooperThread().also {
it.name = "TsDaemon-${nextThreadId()}"
styles = Styles()
it.offerMessage(MSG_INIT, initText)
it.start()
}
}
override fun destroy() {
thread?.let {
if (it.isAlive) {
it.interrupt()
it.abort = true
}
}
(styles.spans as LineSpansGenerator?)?.tree?.close()
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
}
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() {
val scopedVariables = TsScopedVariables(tree!!, localText, languageSpec)
if (thread == this && messageQueue.isEmpty()) {
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()) {
return
}
val blocks = mutableListOf<CodeBlock>()
TSQueryCursor.create().use {
it.exec(languageSpec.blocksQuery, tree!!.rootNode)
var match = it.nextMatch()
while (match != null) {
if (languageSpec.blocksPredicator.doPredicate(
languageSpec.predicates,
localText,
match
)
) {
match.captures.forEach {
val block = ObjectAllocator.obtainBlockLine().also { block ->
var node = it.node
val start = node.startPoint
block.startLine = start.row
block.startColumn = start.column / 2
val end = if (languageSpec.blocksQuery.getCaptureNameForId(it.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)
}
}
}
match = it.nextMatch()
}
}
val distinct = blocks.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) {
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)
}
}
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?
)
}
@@ -0,0 +1,94 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts
import com.itsaky.androidide.treesitter.TSNode
import com.itsaky.androidide.treesitter.TSQueryCursor
import com.itsaky.androidide.treesitter.TSTree
import io.github.rosemoe.sora.lang.brackets.BracketsProvider
import io.github.rosemoe.sora.lang.brackets.PairedBracket
import io.github.rosemoe.sora.text.Content
import java.lang.Math.max
class TsBracketPairs(
private val tree: TSTree,
private val languageSpec: TsLanguageSpec
) : BracketsProvider {
companion object {
val OPEN_NAME = "editor.brackets.open"
val CLOSE_NAME = "editor.brackets.close"
}
override fun getPairedBracketAt(text: Content, index: Int): PairedBracket? {
if (languageSpec.bracketsQuery.patternCount > 0 && languageSpec.bracketsQuery.canAccess() && tree.canAccess()) {
TSQueryCursor.create().use { cursor ->
cursor.setByteRange(max(0, index - 1) * 2, index * 2 + 1)
cursor.exec(languageSpec.bracketsQuery, tree.rootNode)
var match = cursor.nextMatch()
var matched = false
while (match != null && !matched) {
if (languageSpec.bracketsPredicator.doPredicate(
languageSpec.predicates,
text,
match
)
) {
var startNode: TSNode? = null
var endNode: TSNode? = null
for (capture in match.captures) {
val captureName =
languageSpec.bracketsQuery.getCaptureNameForId(capture.index)
if (captureName == OPEN_NAME || captureName == CLOSE_NAME) {
val node = capture.node
if (index >= node.startByte / 2 && index <= node.endByte / 2) {
matched = true
}
if (captureName == OPEN_NAME) {
startNode = node
} else {
endNode = node
}
}
}
if (matched && startNode != null && endNode != null) {
return PairedBracket(
startNode.startByte / 2,
(startNode.endByte - startNode.startByte) / 2,
endNode.startByte / 2,
(endNode.endByte - endNode.startByte) / 2
)
}
}
match = cursor.nextMatch()
}
}
}
return null
}
}
@@ -0,0 +1,112 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts
import android.os.Bundle
import io.github.rosemoe.sora.lang.EmptyLanguage
import io.github.rosemoe.sora.lang.Language
import io.github.rosemoe.sora.lang.completion.CompletionPublisher
import io.github.rosemoe.sora.lang.format.Formatter
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
/**
* Tree-sitter based language.
*
* @param languageSpec The language specification for parsing and highlighting
* @param themeDescription Theme for colorizing nodes
* @param tab whether tab should be used
*
* @see TsTheme
* @see TsLanguageSpec
*
* @author Rosemoe
*/
open class TsLanguage(
val languageSpec: TsLanguageSpec,
val tab: Boolean = false,
themeDescription: TsThemeBuilder.() -> Unit
) : Language {
init {
if (languageSpec.closed) {
throw IllegalStateException("spec is closed")
}
}
protected var tsTheme = TsThemeBuilder(languageSpec.tsQuery).apply { themeDescription() }.theme
open val analyzer by lazy {
TsAnalyzeManager(languageSpec, tsTheme)
}
/**
* Update tree-sitter colorizing theme with the given description
*/
fun updateTheme(themeDescription: TsThemeBuilder.() -> Unit) = languageSpec.let {
if (it.closed) {
throw IllegalStateException("spec is closed")
}
updateTheme(TsThemeBuilder(languageSpec.tsQuery).apply { themeDescription() }.theme)
}
/**
* Update tree-sitter colorizing theme
*/
fun updateTheme(theme: TsTheme) {
this.tsTheme = theme
analyzer.updateTheme(theme)
}
override fun getAnalyzeManager() = analyzer
override fun getInterruptionLevel() = Language.INTERRUPTION_LEVEL_STRONG
override fun requireAutoComplete(
content: ContentReference,
position: CharPosition,
publisher: CompletionPublisher,
extraArguments: Bundle
) {
// Nothing
}
override fun getIndentAdvance(content: ContentReference, line: Int, column: Int) = 0
override fun useTab() = tab
override fun getFormatter(): Formatter = EmptyLanguage.EmptyFormatter.INSTANCE
override fun getSymbolPairs(): SymbolPairMatch = EmptyLanguage.EMPTY_SYMBOL_PAIRS
override fun getNewlineHandlers() = emptyArray<NewlineHandler>()
override fun destroy() {
languageSpec.close()
}
}
@@ -0,0 +1,189 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts
import com.itsaky.androidide.treesitter.TSLanguage
import com.itsaky.androidide.treesitter.TSQuery
import com.itsaky.androidide.treesitter.TSQueryError
import io.github.rosemoe.sora.editor.ts.predicate.Predicator
import io.github.rosemoe.sora.editor.ts.predicate.TsPredicate
import io.github.rosemoe.sora.editor.ts.predicate.builtin.MatchPredicate
import java.io.Closeable
/**
* Language specification for tree-sitter highlighter. This specification covers language code
* parsing, highlighting captures and local variable tracking descriptions.
*
* Note that you must use ASCII characters in your scm sources. Otherwise, an [IllegalArgumentException] is
* thrown.
* Be careful that this should be closed to avoid native memory leaks.
*
* @author Rosemoe
* @param language The tree-sitter language instance to be used for parsing
* @param highlightScmSource The scm source code for highlighting tree nodes
* @param codeBlocksScmSource The scm source for capturing code blocks.
* All captured nodes are considered to be a code block.
* Capture named with '.marked' suffix will have its last terminal node's start position as its scope end
* @param bracketsScmSource The scm source for capturing brackets. Capture named 'editor.brackets.open' and 'editor.brackets.close' are used to compute bracket pairs
* @param localsScmSource The scm source code for tracking local variables
* @param localsCaptureSpec Custom specification for locals scm file
* @param predicates Client custom predicate implementations
*/
open class TsLanguageSpec(
val language: TSLanguage,
highlightScmSource: String,
codeBlocksScmSource: String = "",
bracketsScmSource: String = "",
localsScmSource: String = "",
localsCaptureSpec: LocalsCaptureSpec = LocalsCaptureSpec.DEFAULT,
val predicates: List<TsPredicate> = listOf(MatchPredicate)
) : Closeable {
/**
* The generated scm source code for querying
*/
val querySource = localsScmSource + "\n" + highlightScmSource
/**
* Offset of highlighting scm source code in [querySource]
*/
val highlightScmOffset = localsScmSource.encodeToByteArray().size + 1
/**
* The actual [TSQuery] object
*/
val tsQuery = TSQuery.create(language, querySource)
/**
* The first index of highlighting pattern
*/
val highlightPatternOffset: Int
/**
* Indices of variable definition patterns
*/
val localsDefinitionIndices = mutableListOf<Int>()
/**
* Indices of variable reference patterns
*/
val localsReferenceIndices = mutableListOf<Int>()
/**
* Indices of variable scope patterns
*/
val localsScopeIndices = mutableListOf<Int>()
/**
* Indices of weak variable scope patterns
* @see [LocalsCaptureSpec.isMembersScopeCapture] for more information
*/
val localsMembersScopeIndices = mutableListOf<Int>()
/**
* Indices of variable definition-value patterns. Currently unused in analysis.
*/
val localsDefinitionValueIndices = mutableListOf<Int>()
val blocksQuery = if (codeBlocksScmSource.isBlank()) {
TSQuery.EMPTY
} else TSQuery.create(language, codeBlocksScmSource)
val bracketsQuery = if (bracketsScmSource.isBlank()) {
TSQuery.EMPTY
} else TSQuery.create(language, bracketsScmSource)
init {
// Check the queries before access
try {
blocksQuery.validateOrThrow("code-blocks")
bracketsQuery.validateOrThrow("brackets")
querySource.forEach {
if (it > 0xFF.toChar()) {
throw IllegalArgumentException("use non-ASCII characters in scm source is unexpected")
}
}
if (!tsQuery.canAccess()) {
throw IllegalArgumentException("Syntax highlights query is invalid")
}
if (tsQuery.errorType != TSQueryError.None) {
val region = if (tsQuery.errorOffset < highlightScmOffset) "locals" else "highlight"
val offset =
if (tsQuery.errorOffset < highlightScmOffset) tsQuery.errorOffset else tsQuery.errorOffset - highlightScmOffset
throw IllegalArgumentException("bad scm sources: error ${tsQuery.errorType.name} occurs in $region range at offset $offset")
}
} catch (e: IllegalArgumentException) {
tsQuery.close()
blocksQuery.close()
bracketsQuery.close()
throw e
}
}
val queryPredicator = Predicator(tsQuery)
val blocksPredicator = Predicator(blocksQuery)
val bracketsPredicator = Predicator(bracketsQuery)
/**
* Close flag
*/
var closed = false
private set
init {
var highlightOffset = 0
for (i in 0 until tsQuery.captureCount) {
// Only locals in localsScm are taken down
val name = tsQuery.getCaptureNameForId(i)
if (localsCaptureSpec.isDefinitionCapture(name)) {
localsDefinitionIndices.add(i)
} else if (localsCaptureSpec.isReferenceCapture(name)) {
localsReferenceIndices.add(i)
} else if (localsCaptureSpec.isScopeCapture(name)) {
localsScopeIndices.add(i)
} else if (localsCaptureSpec.isDefinitionValueCapture(name)) {
localsDefinitionValueIndices.add(i)
} else if (localsCaptureSpec.isMembersScopeCapture(name)) {
localsMembersScopeIndices.add(i)
}
}
for (i in 0 until tsQuery.patternCount) {
if (tsQuery.getStartByteForPattern(i) < highlightScmOffset) {
highlightOffset++
}
}
highlightPatternOffset = highlightOffset
}
override fun close() {
tsQuery.close()
blocksQuery.close()
bracketsQuery.close()
closed = true
}
}
@@ -0,0 +1,139 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts
import com.itsaky.androidide.treesitter.TSNode
import com.itsaky.androidide.treesitter.TSQueryCapture
import com.itsaky.androidide.treesitter.TSQueryCursor
import com.itsaky.androidide.treesitter.TSTree
import com.itsaky.androidide.treesitter.string.UTF16String
import java.util.Stack
/**
* Class for storing tree-sitter variables. This class tracks the positions and scopes
* of variables and find definitions.
*
* @author Rosemoe
* @param tree The parsed tree
* @param text The current text for tree
* @param spec Language specification, which should the same as highlighter's
*/
class TsScopedVariables(tree: TSTree, text: UTF16String, val spec: TsLanguageSpec) {
private val rootScope: Scope
init {
rootScope = Scope(0, tree.rootNode.endByte / 2)
if (spec.localsDefinitionIndices.isNotEmpty()) {
TSQueryCursor.create().use { cursor ->
cursor.exec(spec.tsQuery, tree.rootNode)
var match = cursor.nextMatch()
val captures = mutableListOf<TSQueryCapture>()
while (match != null) {
if (spec.queryPredicator.doPredicate(spec.predicates, text, match)) {
captures.addAll(match.captures)
}
match = cursor.nextMatch()
}
captures.sortBy { it.node.startByte }
val scopeStack = Stack<Scope>()
var lastAddedVariableNode: TSNode? = null
scopeStack.push(rootScope)
for (capture in captures) {
val startIndex = capture.node.startByte / 2
val endIndex = capture.node.endByte / 2
while (startIndex >= scopeStack.peek().endIndex) {
scopeStack.pop()
}
val pattern = capture.index
if (pattern in spec.localsScopeIndices) {
val newScope = Scope(startIndex, endIndex)
scopeStack.peek().childScopes.add(newScope)
scopeStack.push(newScope)
} else if (pattern in spec.localsMembersScopeIndices) {
val newScope = Scope(startIndex, endIndex, true)
scopeStack.peek().childScopes.add(newScope)
scopeStack.push(newScope)
} else if (pattern in spec.localsDefinitionIndices) {
val scope = scopeStack.peek()
val utf16Name = text.subseqChars(startIndex, endIndex)
val name = utf16Name.toString()
utf16Name.close()
val scopedVar = ScopedVariable(
name,
if (scope.forMembers) scope.startIndex else startIndex,
scope.endIndex
)
scope.variables.add(scopedVar)
lastAddedVariableNode = capture.node
} else if (pattern !in spec.localsDefinitionValueIndices && pattern !in spec.localsReferenceIndices && lastAddedVariableNode != null) {
val topVariables = scopeStack.peek().variables
if (topVariables.isNotEmpty()) {
val topVariable = topVariables.last()
if (lastAddedVariableNode.startByte / 2 == startIndex && lastAddedVariableNode.endByte / 2 == endIndex && topVariable.matchedHighlightPattern == -1) {
topVariable.matchedHighlightPattern = pattern
}
}
}
}
}
}
}
data class Scope(
val startIndex: Int,
val endIndex: Int,
val forMembers: Boolean = false,
val variables: MutableList<ScopedVariable> = mutableListOf(),
val childScopes: MutableList<Scope> = mutableListOf()
)
data class ScopedVariable(
var name: String,
var scopeStartIndex: Int,
var scopeEndIndex: Int,
var matchedHighlightPattern: Int = -1
)
fun findDefinition(startIndex: Int, endIndex: Int, name: String): ScopedVariable? {
var definition: ScopedVariable? = null
var currentScope: Scope? = rootScope
while (currentScope != null) {
for (variable in currentScope.variables) {
if (variable.scopeStartIndex > startIndex) {
break
}
if (variable.scopeStartIndex <= startIndex && variable.scopeEndIndex >= endIndex && variable.name == name) {
definition = variable
// Do not break here: name can be shadowed in some languages
}
}
currentScope =
currentScope.childScopes.firstOrNull { scope -> scope.startIndex <= startIndex && scope.endIndex >= endIndex }
}
return definition
}
}
@@ -0,0 +1,114 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts
import android.util.SparseLongArray
import com.itsaky.androidide.treesitter.TSQuery
import io.github.rosemoe.sora.lang.styling.TextStyle
import io.github.rosemoe.sora.widget.schemes.EditorColorScheme
/**
* Theme for tree-sitter. This is different from [io.github.rosemoe.sora.widget.schemes.EditorColorScheme].
* It is only used for colorizing spans in tree-sitter module. The real colors are still stored in editor
* color schemes.
* As what tree-sitter do, we try to match the longest scope.
* For example, if 'variable' and 'variable.builtin' rule are both defined, Query 'variable.builtin'
* and 'variable.builtin.this' will get 'variable.builtin' rule.
* The theme also provide a fallback. You may call [TsTheme.putStyleRule] with a rule of length 0 to
* set fallback color scheme.
* Note that colors of 'locals.definition', 'locals.reference', etc. can not be set by this theme object.
*
* @author Rosemoe
*/
class TsTheme(private val tsQuery: TSQuery) {
private val styles = mutableMapOf<String, Long>()
private val mapping = SparseLongArray()
/**
* The text style for normal texts
*/
var normalTextStyle = TextStyle.makeStyle(EditorColorScheme.TEXT_NORMAL)
/**
* Set text style for the given rule string.
*
* @param rule The rule for locating nodes
* @param style The style value for those nodes
* @see io.github.rosemoe.sora.lang.styling.TextStyle
*/
fun putStyleRule(rule: String, style: Long) {
styles[rule] = style
mapping.clear()
}
/**
* Remove rule
* @param rule The rule for locating nodes
*/
fun eraseStyleRule(rule: String) = putStyleRule(rule, 0L)
fun resolveStyleForPattern(pattern: Int): Long {
val index = mapping.indexOfKey(pattern)
return if (index >= 0) {
mapping.valueAt(index)
} else {
var mappedName = tsQuery.getCaptureNameForId(pattern)
var style = styles[mappedName] ?: 0L
while (style == 0L && mappedName.isNotEmpty()) {
mappedName = mappedName.substringBeforeLast('.', "")
style = styles[mappedName] ?: 0L
}
mapping.put(pattern, style)
style
}
}
}
/**
* Builder class for tree-sitter themes
*/
class TsThemeBuilder(tsQuery: TSQuery) {
internal val theme = TsTheme(tsQuery)
infix fun Long.applyTo(targetRule: String) {
theme.putStyleRule(targetRule, this)
}
infix fun Long.applyTo(targetRules: Array<String>) {
targetRules.forEach {
applyTo(it)
}
}
}
/**
* Build tree-sitter theme
*/
fun tsTheme(tsQuery: TSQuery, description: TsThemeBuilder.() -> Unit) =
TsThemeBuilder(tsQuery).also { it.description() }.theme
@@ -0,0 +1,41 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts
import com.itsaky.androidide.treesitter.TSPoint
import com.itsaky.androidide.treesitter.TSQuery
import com.itsaky.androidide.treesitter.TSQueryError
import io.github.rosemoe.sora.text.CharPosition
/**
* Convert a [CharPosition] object to a [TSPoint] object
*/
fun CharPosition.toTSPoint() = TSPoint.create(line, column * 2)
fun TSQuery.validateOrThrow(name: String = "unknown") {
if (errorType != TSQueryError.None) {
throw IllegalArgumentException("query(name:$name) parsing failed: ${errorType.name} at text offset $errorOffset")
}
}
@@ -0,0 +1,37 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts.multilang
interface LanguagePriorityCheck {
companion object {
val PRIORITY_NEVER = 0
val PRIORITY_AS_FALLBACK = 1
val PRIORITY_ALWAYS = 1000
}
fun getPriorityByName(name: String) : Int
}
@@ -0,0 +1,28 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts.multilang
interface TsIndentHelper {
}
@@ -0,0 +1,56 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts.multilang
import com.itsaky.androidide.treesitter.TSLanguage
import io.github.rosemoe.sora.editor.ts.LocalsCaptureSpec
import io.github.rosemoe.sora.editor.ts.TsLanguageSpec
import io.github.rosemoe.sora.editor.ts.TsThemeBuilder
import io.github.rosemoe.sora.editor.ts.predicate.TsPredicate
import io.github.rosemoe.sora.editor.ts.predicate.builtin.MatchPredicate
class TsInjectableLanguageSpec(
language: TSLanguage,
highlightScmSource: String,
themeDescription: TsThemeBuilder.() -> Unit,
val languageName: LanguagePriorityCheck,
val indentHelper: TsIndentHelper? = null,
codeBlocksScmSource: String = "",
bracketsScmSource: String = "",
localsScmSource: String = "",
localsCaptureSpec: LocalsCaptureSpec = LocalsCaptureSpec.DEFAULT,
predicates: List<TsPredicate> = listOf(MatchPredicate)
) : TsLanguageSpec(language, highlightScmSource, codeBlocksScmSource, bracketsScmSource, localsScmSource, localsCaptureSpec, predicates) {
var theme = TsThemeBuilder(tsQuery).apply { themeDescription() }.theme
fun updateTheme(themeDescription: TsThemeBuilder.() -> Unit) = run {
if (closed) {
throw IllegalStateException("spec is closed")
}
theme = TsThemeBuilder(tsQuery).apply { themeDescription() }.theme
}
}
@@ -0,0 +1,46 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts.predicate
/**
* Predicate result for [TsPredicate]
*/
enum class PredicateResult {
/**
* The given predicate is not handled by this [TsPredicate].
* The [com.itsaky.androidide.treesitter.TSQueryMatch] object will be passed to other [TsPredicate].
*/
UNHANDLED,
/**
* The given predicate is accepted by the [TsPredicate]
*/
ACCEPT,
/**
* The given predicate is not accepted by the [TsPredicate]
*/
REJECT
}
@@ -0,0 +1,94 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts.predicate
import com.itsaky.androidide.treesitter.TSQuery
import com.itsaky.androidide.treesitter.TSQueryMatch
import com.itsaky.androidide.treesitter.TSQueryPredicateStep
/**
* Predicate runner for tree-sitter
*
* @author Rosemoe
*/
class Predicator(private val query: TSQuery) {
/**
* Predicates for patterns
*/
private val patternPredicates = mutableListOf<List<TsClientPredicateStep>>()
init {
for (i in 0 until query.patternCount) {
patternPredicates.add(query.getPredicatesForPattern(i).map {
when (it.type) {
TSQueryPredicateStep.Type.String -> TsClientPredicateStep(
it.type,
query.getStringValueForId(it.valueId)
)
TSQueryPredicateStep.Type.Capture -> TsClientPredicateStep(
it.type,
query.getCaptureNameForId(it.valueId)
)
else -> TsClientPredicateStep(it.type, "")
}
})
}
}
fun doPredicate(
predicates: List<TsPredicate>,
text: CharSequence,
match: TSQueryMatch,
syntheticCaptureContainer: TsSyntheticCaptureContainer = TsSyntheticCaptureContainer.EMPTY_IMMUTABLE_CONTAINER
): Boolean {
val description = patternPredicates[match.patternIndex]
var tail = 0
for (i in description.indices) {
if (description[i].predicateType == TSQueryPredicateStep.Type.Done) {
// Avoid allocating sublist if possible
val subPredicateStep =
if (tail == 0 && i + 1 == description.size) description else description.subList(
tail,
i + 1
)
for (j in predicates.indices) {
val predicate = predicates[j]
when (predicate.doPredicate(query, text, match, subPredicateStep, syntheticCaptureContainer)) {
PredicateResult.ACCEPT -> break
PredicateResult.REJECT -> return false
else -> {}
}
}
tail = i + 1
}
}
return true
}
}
@@ -0,0 +1,32 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts.predicate
import com.itsaky.androidide.treesitter.TSQueryPredicateStep
data class TsClientPredicateStep(
val predicateType: TSQueryPredicateStep.Type,
val content: String
)
@@ -0,0 +1,48 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts.predicate
import com.itsaky.androidide.treesitter.TSQuery
import com.itsaky.androidide.treesitter.TSQueryMatch
/**
* Predicate client-side implementation
*/
interface TsPredicate {
/**
* Run the predicate on the given [TSQueryMatch]
* @see TSQueryMatch
* @see PredicateResult
*/
fun doPredicate(
tsQuery: TSQuery,
text: CharSequence,
match: TSQueryMatch,
predicateSteps: List<TsClientPredicateStep>,
syntheticCaptures: TsSyntheticCaptureContainer
): PredicateResult
}
@@ -0,0 +1,33 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts.predicate
import com.itsaky.androidide.treesitter.TSNode
data class TsSyntheticCapture(
val captureName: String,
val captureText: String? = null,
val captureNode: TSNode? = null
)
@@ -0,0 +1,57 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts.predicate
class TsSyntheticCaptureContainer(private val noAddOperation: Boolean = false) {
companion object {
val EMPTY_IMMUTABLE_CONTAINER = TsSyntheticCaptureContainer(true)
}
private val syntheticCaptures = mutableListOf<TsSyntheticCapture>()
val indices
get() = syntheticCaptures.indices
val size
get() = syntheticCaptures.size
operator fun get(index: Int) = syntheticCaptures[index]
fun addSyntheticCapture(syntheticCapture: TsSyntheticCapture) {
if (syntheticCapture.captureNode == null && syntheticCapture.captureText == null) {
throw IllegalArgumentException("at least one field between 'captureText' and 'captureNode' should be non-null")
}
if (noAddOperation) {
return
}
syntheticCaptures.add(syntheticCapture)
}
fun clear() {
syntheticCaptures.clear()
}
}
@@ -0,0 +1,72 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts.predicate.builtin
import com.itsaky.androidide.treesitter.TSQuery
import com.itsaky.androidide.treesitter.TSQueryMatch
import com.itsaky.androidide.treesitter.TSQueryPredicateStep.Type
import io.github.rosemoe.sora.editor.ts.predicate.PredicateResult
import io.github.rosemoe.sora.editor.ts.predicate.TsClientPredicateStep
import io.github.rosemoe.sora.editor.ts.predicate.TsPredicate
import io.github.rosemoe.sora.editor.ts.predicate.TsSyntheticCaptureContainer
import java.util.concurrent.ConcurrentHashMap
import java.util.regex.PatternSyntaxException
object MatchPredicate : TsPredicate {
private val PARAMETERS = arrayOf(Type.String, Type.Capture, Type.String, Type.Done)
private val cache = ConcurrentHashMap<String, Regex>()
override fun doPredicate(
tsQuery: TSQuery,
text: CharSequence,
match: TSQueryMatch,
predicateSteps: List<TsClientPredicateStep>,
syntheticCaptures: TsSyntheticCaptureContainer
): PredicateResult {
if (!parametersMatch(predicateSteps, PARAMETERS) || predicateSteps[0].content != "match?") {
return PredicateResult.UNHANDLED
}
val captured = getCaptureContent(tsQuery, match, predicateSteps[1].content, text)
try {
var regex = cache[predicateSteps[2].content]
if (regex == null) {
regex = Regex(predicateSteps[2].content)
cache[predicateSteps[2].content] = regex
}
for (str in captured) {
if (regex.find(str) == null) {
return PredicateResult.REJECT
}
}
return PredicateResult.ACCEPT
} catch (e: PatternSyntaxException) {
e.printStackTrace()
return PredicateResult.UNHANDLED
}
}
}
@@ -0,0 +1,82 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts.predicate.builtin
import com.itsaky.androidide.treesitter.TSQuery
import com.itsaky.androidide.treesitter.TSQueryMatch
import com.itsaky.androidide.treesitter.TSQueryPredicateStep
import io.github.rosemoe.sora.editor.ts.predicate.PredicateResult
import io.github.rosemoe.sora.editor.ts.predicate.TsClientPredicateStep
import io.github.rosemoe.sora.editor.ts.predicate.TsPredicate
import io.github.rosemoe.sora.editor.ts.predicate.TsSyntheticCapture
import io.github.rosemoe.sora.editor.ts.predicate.TsSyntheticCaptureContainer
object SetCapturePredicate : TsPredicate {
private val PARAMETERS_1 = arrayOf(
TSQueryPredicateStep.Type.String,
TSQueryPredicateStep.Type.Capture,
TSQueryPredicateStep.Type.String,
TSQueryPredicateStep.Type.Done
)
private val PARAMETERS_2 = arrayOf(
TSQueryPredicateStep.Type.String,
TSQueryPredicateStep.Type.Capture,
TSQueryPredicateStep.Type.Capture,
TSQueryPredicateStep.Type.Done
)
override fun doPredicate(
tsQuery: TSQuery,
text: CharSequence,
match: TSQueryMatch,
predicateSteps: List<TsClientPredicateStep>,
syntheticCaptures: TsSyntheticCaptureContainer
): PredicateResult {
if (predicateSteps[0].content == "set!") {
if (parametersMatch(predicateSteps, PARAMETERS_1)) {
syntheticCaptures.addSyntheticCapture(
TsSyntheticCapture(
predicateSteps[1].content,
predicateSteps[2].content
)
)
} else if (parametersMatch(predicateSteps, PARAMETERS_2)) {
val captureTexts = getCaptureContent(tsQuery, match, predicateSteps[2].content, text)
if (captureTexts.size == 1) {
syntheticCaptures.addSyntheticCapture(
TsSyntheticCapture(
predicateSteps[1].content,
captureTexts[0]
)
)
}
}
}
// As this does not affect whether the match is actually valid, we always return UNHANDLED
return PredicateResult.UNHANDLED
}
}
@@ -0,0 +1,66 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts.predicate.builtin
import com.itsaky.androidide.treesitter.TSQuery
import com.itsaky.androidide.treesitter.TSQueryMatch
import com.itsaky.androidide.treesitter.TSQueryPredicateStep.Type
import com.itsaky.androidide.treesitter.string.UTF16String
import io.github.rosemoe.sora.editor.ts.predicate.TsClientPredicateStep
import io.github.rosemoe.sora.text.Content
fun parametersMatch(
predicate: List<TsClientPredicateStep>,
types: Array<Type>
): Boolean {
if (predicate.size == types.size) {
for (i in types.indices) {
if (predicate[i].predicateType != types[i]) {
return false
}
}
return true
}
return false
}
fun getCaptureContent(
tsQuery: TSQuery,
match: TSQueryMatch,
captureName: String,
text: CharSequence
) = match.captures.filter { tsQuery.getCaptureNameForId(it.index) == captureName }
.map {
if (text is UTF16String) {
val utf16Name = text.subseqChars(it.node.startByte / 2, it.node.endByte / 2)
val name = utf16Name.toString()
utf16Name.close()
name
} else if(text is Content) {
text.substring(it.node.startByte / 2, it.node.endByte / 2)
} else {
text.substring(it.node.startByte / 2, it.node.endByte / 2)
}
}
@@ -0,0 +1,43 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts.spans
import com.itsaky.androidide.treesitter.TSQueryCapture
import io.github.rosemoe.sora.lang.styling.Span
/**
* Default implementation of the [TsSpanFactory].
*
* @author Akash Yadav
*/
open class DefaultSpanFactory : TsSpanFactory {
override fun createSpans(capture: TSQueryCapture, column: Int, spanStyle: Long): List<Span> {
return listOf(Span.obtain(column, spanStyle))
}
override fun close() {
}
}
@@ -0,0 +1,46 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2023 Rosemoe
*
* 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.
*
* 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.
*
* 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
******************************************************************************/
package io.github.rosemoe.sora.editor.ts.spans
import com.itsaky.androidide.treesitter.TSQueryCapture
import io.github.rosemoe.sora.lang.styling.Span
/**
* Factory for creating spans for the tree sitter analyze manager.
*
* @author Akash Yadav
*/
interface TsSpanFactory : AutoCloseable {
/**
* Creates the spans using the provided data.
*
* @param capture The query capture. More information about the node can be found using the [TSQueryCapture.node] object.
* @param column The start column index for the span.
* @param spanStyle The style for the spans.
* @return The [Span] objects.
*/
fun createSpans(capture: TSQueryCapture, column: Int, spanStyle: Long): List<Span>
}
+2 -2
View File
@@ -26,10 +26,10 @@ dependencies {
api(libs.androidide.ts.log)
api(libs.androidide.ts.xml)
api(libs.common.editor)
api(libs.common.editor.ts)
api(projects.editorApi)
api(projects.editorTreesitter)
implementation(libs.androidx.annotation)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.constraintlayout)
@@ -118,11 +118,11 @@ abstract class TreeSitterLanguage(context: Context, lang: TSLanguage, type: Stri
decrementBy: Int = 0
): Int {
val indentsQuery = languageSpec.indentsQuery ?: return 0
return TSParser().use { parser ->
return TSParser.create().use { parser ->
parser.language = languageSpec.language
val tree = parser.parseString(content)
return@use TSQueryCursor().use { cursor ->
return@use TSQueryCursor.create().use { cursor ->
cursor.exec(indentsQuery, tree.rootNode)
var indent = 0
@@ -38,7 +38,7 @@ class TreeSitterLanguageSpec
get() = spec.language
// </editor-fold>
val indentsQuery: TSQuery? = TSQuery.create(language, indentsQueryScm)
val indentsQuery: TSQuery? = if (indentsQueryScm.isBlank()) TSQuery.EMPTY else TSQuery.create(language, indentsQueryScm)
.let { if (it.canAccess()) it else null }
init {
+1 -2
View File
@@ -5,7 +5,7 @@ gradle-tooling = "8.3"
junit-jupiter = "5.10.0"
kotlin = "1.9.10"
kotlin-coroutines = "1.7.3"
tree-sitter = "3.4.0"
tree-sitter = "4.0.0-internal.1+branch.dev.commit.170fe25.timestamp.20231011182804-SNAPSHOT"
editor = "0.22.1-f7ee449-SNAPSHOT"
glide = "4.16.0"
androidx-vectordrawable = "1.1.0"
@@ -29,7 +29,6 @@ androidide-ts-xml = { module = "com.itsaky.androidide:tree-sitter-xml", version.
# Common
common-editor = { module = "io.github.Rosemoe.sora-editor:editor", version.ref = "editor" }
common-editor-ts = { module = "io.github.Rosemoe.sora-editor:language-treesitter", version.ref = "editor" }
common-utilcode = { module = "com.blankj:utilcodex", version = "1.31.1" }
common-glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" }
common-glide_ap = { module = "com.github.bumptech.glide:compiler", version.ref = "glide" }
@@ -40,7 +40,7 @@ object TSJavaParser : IJavaParser<TSParseResult> {
private val cache = TSParseCache(15) // cache 15 results at max
private var isClosed = false
private val parser = TSParser().also { it.language = TSLanguageJava.getInstance() }
private val parser = TSParser.create().also { it.language = TSLanguageJava.getInstance() }
get() {
check(!isClosed) { "${javaClass.simpleName} instance has been closed" }
return field
@@ -36,7 +36,7 @@ object TSMethodPruner {
val root = tree.rootNode
TSQuery.create(TSLanguageJava.getInstance(), METHOD_BODIES_QUERY).use { query ->
check(query.canAccess()) { "Invalid method bodies query" }
TSQueryCursor().use { queryCursor ->
TSQueryCursor.create().use { queryCursor ->
queryCursor.exec(query, root)
var match: TSQueryMatch? = queryCursor.nextMatch()
@@ -153,7 +153,7 @@ object AdvancedEditProvider {
}
private fun parseXml(content: String, action: (TSTree) -> Unit) {
TSParser().use { parser ->
TSParser.create().use { parser ->
parser.language = TSLanguageXml.getInstance()
parser.parseString(content).use(action)
}
@@ -169,7 +169,7 @@ object AdvancedEditProvider {
throw RuntimeException("Invalid query. Please open an issue on GitHub.")
}
TSQueryCursor().use { cursor ->
TSQueryCursor.create().use { cursor ->
cursor.exec(query, tree.rootNode)
val matches = mutableListOf<TSQueryMatch>()
var match: TSQueryMatch? = cursor.nextMatch()
+1
View File
@@ -49,6 +49,7 @@ include(
":common",
":editor",
":editor-api",
":editor-treesitter",
":emulatorview",
":eventbus",
":eventbus-android",