mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
feat: add indentation support in TreeSitterLanguage
This commit is contained in:
@@ -53,7 +53,7 @@ import com.blankj.utilcode.util.FileUtils;
|
||||
import com.blankj.utilcode.util.SizeUtils;
|
||||
import com.itsaky.androidide.app.BaseApplication;
|
||||
import com.itsaky.androidide.editor.databinding.LayoutCodeEditorBinding;
|
||||
import com.itsaky.androidide.editor.language.TreeSitterLanguage;
|
||||
import com.itsaky.androidide.editor.language.treesitter.TreeSitterLanguage;
|
||||
import com.itsaky.androidide.editor.language.cpp.CppLanguage;
|
||||
import com.itsaky.androidide.editor.language.groovy.GroovyLanguage;
|
||||
import com.itsaky.androidide.editor.language.java.JavaLanguage;
|
||||
@@ -74,7 +74,6 @@ import com.itsaky.androidide.syntax.colorschemes.DynamicColorScheme;
|
||||
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
|
||||
import com.itsaky.androidide.utils.FileUtil;
|
||||
import com.itsaky.androidide.utils.ILogger;
|
||||
import com.itsaky.androidide.utils.LSPUtils;
|
||||
import com.itsaky.androidide.utils.TypefaceUtilsKt;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
; Query for computing indents in tree-sitter language
|
||||
; Capture names can be 'indent' or 'outdent'. Any other capture name will be ignored
|
||||
; indent - increments indentation
|
||||
; outdent - decrements indentation
|
||||
|
||||
[
|
||||
"("
|
||||
"["
|
||||
"{"
|
||||
] @indent
|
||||
|
||||
[
|
||||
(class_body)
|
||||
(enum_body)
|
||||
(interface_body)
|
||||
(constructor_declaration)
|
||||
(constructor_body)
|
||||
(block)
|
||||
(switch_block)
|
||||
(array_initializer)
|
||||
(argument_list)
|
||||
(formal_parameters)
|
||||
] @indent
|
||||
|
||||
(expression_statement (method_invocation) @indent)
|
||||
|
||||
[
|
||||
")"
|
||||
"]"
|
||||
"}"
|
||||
] @outdent
|
||||
@@ -122,7 +122,12 @@ public abstract class IDELanguage implements Language {
|
||||
public int getTabSize() {
|
||||
return EditorPreferencesKt.getTabSize();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getIndentAdvance(@NonNull final ContentReference content, final int line, final int column) {
|
||||
return getIndentAdvance(content.getLine(line).substring(0, column));
|
||||
}
|
||||
|
||||
public int getIndentAdvance(@NonNull String line) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ package com.itsaky.androidide.editor.language.java
|
||||
import android.content.Context
|
||||
import com.itsaky.androidide.editor.language.BraceHandler
|
||||
import com.itsaky.androidide.editor.language.CommonSymbolPairs
|
||||
import com.itsaky.androidide.editor.language.TreeSitterLanguage
|
||||
import com.itsaky.androidide.editor.language.treesitter.TreeSitterLanguage
|
||||
import com.itsaky.androidide.lsp.api.ILanguageServer
|
||||
import com.itsaky.androidide.lsp.api.ILanguageServerRegistry
|
||||
import com.itsaky.androidide.lsp.java.JavaLanguageServer
|
||||
@@ -54,11 +54,6 @@ class JavaLanguage(context: Context) :
|
||||
return INTERRUPTION_LEVEL_SLIGHT
|
||||
}
|
||||
|
||||
override fun getIndentAdvance(content: ContentReference, line: Int, column: Int): Int {
|
||||
// TODO Implement this with tree-sitter
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun getSymbolPairs(): SymbolPairMatch {
|
||||
return JavaSymbolPairs()
|
||||
}
|
||||
|
||||
+50
-9
@@ -15,20 +15,25 @@
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.editor.language
|
||||
package com.itsaky.androidide.editor.language.treesitter
|
||||
|
||||
import android.content.Context
|
||||
import com.itsaky.androidide.editor.language.CommonSymbolPairs
|
||||
import com.itsaky.androidide.editor.language.IDELanguage
|
||||
import com.itsaky.androidide.editor.schemes.IDEColorScheme
|
||||
import com.itsaky.androidide.editor.schemes.IDEColorSchemeProvider
|
||||
import com.itsaky.androidide.editor.schemes.LanguageSpecProvider.getLanguageSpec
|
||||
import com.itsaky.androidide.editor.schemes.LocalCaptureSpecProvider.newLocalCaptureSpec
|
||||
import com.itsaky.androidide.treesitter.TSLanguage
|
||||
import com.itsaky.androidide.treesitter.TSParser
|
||||
import com.itsaky.androidide.treesitter.TSQueryCursor
|
||||
import com.itsaky.androidide.treesitter.TSQueryMatch
|
||||
import com.itsaky.androidide.utils.ILogger
|
||||
import io.github.rosemoe.sora.editor.ts.TsAnalyzeManager
|
||||
import io.github.rosemoe.sora.editor.ts.TsLanguageSpec
|
||||
import io.github.rosemoe.sora.editor.ts.TsTheme
|
||||
import io.github.rosemoe.sora.lang.analysis.AnalyzeManager
|
||||
import io.github.rosemoe.sora.widget.SymbolPairMatch
|
||||
import kotlin.math.max
|
||||
|
||||
/**
|
||||
* Tree Sitter language implementation.
|
||||
@@ -39,38 +44,74 @@ abstract class TreeSitterLanguage(context: Context, lang: TSLanguage, type: Stri
|
||||
IDELanguage() {
|
||||
|
||||
private lateinit var tsTheme: TsTheme
|
||||
private lateinit var languageSpec: TsLanguageSpec
|
||||
private val analyzer by lazy { TsAnalyzeManager(languageSpec, tsTheme) }
|
||||
private lateinit var languageSpec: TreeSitterLanguageSpec
|
||||
private val analyzer by lazy { TsAnalyzeManager(languageSpec.spec, tsTheme) }
|
||||
|
||||
private val log = ILogger.newInstance("TreeSitterLanguage")
|
||||
|
||||
|
||||
init {
|
||||
this.languageSpec = getLanguageSpec(context, type, lang, newLocalCaptureSpec(type))
|
||||
this.tsTheme = TsTheme(languageSpec.tsQuery)
|
||||
this.tsTheme = TsTheme(languageSpec.spec.tsQuery)
|
||||
IDEColorSchemeProvider.readScheme(context) { scheme ->
|
||||
if (scheme == null) {
|
||||
log.error("Failed to read color scheme")
|
||||
return@readScheme
|
||||
}
|
||||
|
||||
|
||||
if (scheme !is IDEColorScheme) {
|
||||
log.error("Invalid color scheme returned by color scheme provider", scheme)
|
||||
return@readScheme
|
||||
}
|
||||
|
||||
|
||||
val langScheme =
|
||||
checkNotNull(scheme.languages[type]) { "No color scheme found for file type '$type'" }
|
||||
langScheme.styles.forEach { tsTheme.putStyleRule(it.key, it.value.makeStyle()) }
|
||||
}
|
||||
}
|
||||
|
||||
fun finalizeIndent(indent: Int) : Int {
|
||||
return max(0, indent) * tabSize
|
||||
}
|
||||
|
||||
override fun getAnalyzeManager(): AnalyzeManager {
|
||||
return this.analyzer
|
||||
}
|
||||
|
||||
|
||||
override fun getSymbolPairs(): SymbolPairMatch {
|
||||
return CommonSymbolPairs()
|
||||
}
|
||||
|
||||
override fun getIndentAdvance(line: String): Int {
|
||||
return TSParser().use { parser ->
|
||||
parser.language = languageSpec.language
|
||||
val tree = parser.parseString(line)
|
||||
|
||||
return@use TSQueryCursor().use { cursor ->
|
||||
cursor.exec(languageSpec.indentsQuery, tree.rootNode)
|
||||
|
||||
var indent = 0
|
||||
var match: TSQueryMatch? = cursor.nextMatch()
|
||||
while (match != null) {
|
||||
for (capture in match.captures) {
|
||||
val captureName =
|
||||
languageSpec.indentsQuery.getCaptureNameForId(capture.index)
|
||||
if (captureName == "indent") {
|
||||
++indent
|
||||
} else if (captureName == "outdent") {
|
||||
--indent
|
||||
}
|
||||
}
|
||||
match = cursor.nextMatch()
|
||||
}
|
||||
|
||||
if (indent > 1) {
|
||||
indent = 1
|
||||
}
|
||||
|
||||
finalizeIndent(indent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun destroy() {
|
||||
languageSpec.close()
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.editor.language.treesitter
|
||||
|
||||
import com.itsaky.androidide.treesitter.TSLanguage
|
||||
import com.itsaky.androidide.treesitter.TSQuery
|
||||
import com.itsaky.androidide.treesitter.TSQueryError
|
||||
import io.github.rosemoe.sora.editor.ts.TsLanguageSpec
|
||||
import java.io.Closeable
|
||||
|
||||
/**
|
||||
* Extension of [TsLanguageSpec] for AndroidIDE.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class TreeSitterLanguageSpec
|
||||
@JvmOverloads
|
||||
constructor(val spec: TsLanguageSpec, indentsQueryScm: String = "") : Closeable {
|
||||
|
||||
// <editor-fold desc="">
|
||||
val language: TSLanguage
|
||||
get() = spec.language
|
||||
// </editor-fold>
|
||||
|
||||
val indentsQuery = TSQuery(language, indentsQueryScm)
|
||||
|
||||
init {
|
||||
indentsQuery.validateOrThrow(name = "indents")
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
indentsQuery.close()
|
||||
spec.closed
|
||||
}
|
||||
}
|
||||
|
||||
private fun TSQuery.validateOrThrow(name: String) {
|
||||
if (errorType != TSQueryError.None) {
|
||||
throw IllegalArgumentException(
|
||||
"query(name:$name) parsing failed: ${errorType.name} at text offset $errorOffset"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
package com.itsaky.androidide.editor.language.xml
|
||||
|
||||
import android.content.Context
|
||||
import com.itsaky.androidide.editor.language.TreeSitterLanguage
|
||||
import com.itsaky.androidide.editor.language.treesitter.TreeSitterLanguage
|
||||
import com.itsaky.androidide.lsp.api.ILanguageServer
|
||||
import com.itsaky.androidide.lsp.api.ILanguageServerRegistry
|
||||
import com.itsaky.androidide.lsp.xml.XMLLanguageServer
|
||||
@@ -51,10 +51,6 @@ class XMLLanguage(context: Context) :
|
||||
return INTERRUPTION_LEVEL_STRONG
|
||||
}
|
||||
|
||||
override fun getIndentAdvance(content: ContentReference, line: Int, column: Int): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun getNewlineHandlers(): Array<NewlineHandler> {
|
||||
return emptyArray()
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package com.itsaky.androidide.editor.schemes
|
||||
|
||||
import android.content.Context
|
||||
import com.itsaky.androidide.editor.language.treesitter.TreeSitterLanguageSpec
|
||||
import com.itsaky.androidide.treesitter.TSLanguage
|
||||
import com.itsaky.androidide.utils.ILogger
|
||||
import io.github.rosemoe.sora.editor.ts.LocalsCaptureSpec
|
||||
@@ -40,8 +41,8 @@ object LanguageSpecProvider {
|
||||
type: String,
|
||||
lang: TSLanguage,
|
||||
localsCaptureSpec: LocalsCaptureSpec = LocalsCaptureSpec.DEFAULT
|
||||
): TsLanguageSpec {
|
||||
return TsLanguageSpec(
|
||||
): TreeSitterLanguageSpec {
|
||||
val editorLangSpec = TsLanguageSpec(
|
||||
language = lang,
|
||||
highlightScmSource = readScheme(context, type, "highlights"),
|
||||
localsScmSource = readScheme(context, type, "locals"),
|
||||
@@ -49,6 +50,7 @@ object LanguageSpecProvider {
|
||||
bracketsScmSource = readScheme(context, type, "brackets"),
|
||||
localsCaptureSpec = localsCaptureSpec
|
||||
)
|
||||
return TreeSitterLanguageSpec(spec = editorLangSpec, indentsQueryScm = readScheme(context, type, "indents"))
|
||||
}
|
||||
|
||||
private fun readScheme(context: Context, type: String, name: String): String {
|
||||
|
||||
Reference in New Issue
Block a user