mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
fix: include language-treesitter from sora-editor locally
This commit is contained in:
Generated
+5
-2
@@ -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>
|
||||
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -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
|
||||
```
|
||||
@@ -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)
|
||||
}
|
||||
Vendored
+21
@@ -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>
|
||||
+238
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
+139
@@ -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")
|
||||
}
|
||||
}
|
||||
+37
@@ -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
|
||||
|
||||
}
|
||||
+28
@@ -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 {
|
||||
}
|
||||
+56
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
+46
@@ -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
|
||||
}
|
||||
+94
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
+32
@@ -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
|
||||
)
|
||||
+48
@@ -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
|
||||
|
||||
}
|
||||
+33
@@ -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
|
||||
)
|
||||
+57
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
+72
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+82
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
+66
@@ -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)
|
||||
}
|
||||
}
|
||||
+43
@@ -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() {
|
||||
}
|
||||
}
|
||||
+46
@@ -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>
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+1
-1
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
+2
-2
@@ -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()
|
||||
|
||||
@@ -49,6 +49,7 @@ include(
|
||||
":common",
|
||||
":editor",
|
||||
":editor-api",
|
||||
":editor-treesitter",
|
||||
":emulatorview",
|
||||
":eventbus",
|
||||
":eventbus-android",
|
||||
|
||||
Reference in New Issue
Block a user