mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
fix(editor): migrate IDELanguage to Kotlin
This commit is contained in:
+15
-10
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package com.itsaky.androidide.editor.language
|
||||
|
||||
import com.itsaky.androidide.editor.language.utils.CompletionHelper
|
||||
import com.itsaky.androidide.lsp.api.ILanguageServer
|
||||
import com.itsaky.androidide.lsp.models.CompletionItem
|
||||
import com.itsaky.androidide.lsp.models.CompletionParams
|
||||
@@ -27,11 +28,9 @@ import com.itsaky.androidide.models.Position
|
||||
import com.itsaky.androidide.progress.ProcessCancelledException
|
||||
import com.itsaky.androidide.utils.ILogger
|
||||
import io.github.rosemoe.sora.lang.completion.CompletionCancelledException
|
||||
import io.github.rosemoe.sora.lang.completion.CompletionHelper
|
||||
import io.github.rosemoe.sora.text.CharPosition
|
||||
import io.github.rosemoe.sora.text.ContentReference
|
||||
import java.nio.file.Path
|
||||
import java.util.function.Predicate
|
||||
|
||||
/**
|
||||
* Common implementation of completion provider which requests completions to provided language
|
||||
@@ -39,10 +38,15 @@ import java.util.function.Predicate
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class CommonCompletionProvider(private val server: ILanguageServer,
|
||||
private val cancelChecker: CompletionCancelChecker) {
|
||||
internal class CommonCompletionProvider(
|
||||
private val server: ILanguageServer,
|
||||
private val cancelChecker: CompletionCancelChecker
|
||||
) {
|
||||
|
||||
private val log = ILogger.newInstance(javaClass.simpleName)
|
||||
companion object {
|
||||
|
||||
private val log = ILogger.newInstance("CommonCompletionProvider")
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes completion items using the provided language server instance.
|
||||
@@ -53,18 +57,19 @@ class CommonCompletionProvider(private val server: ILanguageServer,
|
||||
* @return The computed completion items. May return an empty list if the there was an error
|
||||
* computing the completion items.
|
||||
*/
|
||||
fun complete(
|
||||
content: ContentReference?,
|
||||
inline fun complete(
|
||||
content: ContentReference,
|
||||
file: Path,
|
||||
position: CharPosition,
|
||||
prefixMatcher: Predicate<Char?>
|
||||
prefixMatcher: (Char) -> Boolean
|
||||
): List<CompletionItem> {
|
||||
val completionResult =
|
||||
try {
|
||||
setupLookupForCompletion(file)
|
||||
val prefix = CompletionHelper.computePrefix(content, position) { prefixMatcher.test(it) }
|
||||
val prefix = CompletionHelper.computePrefix(content, position, prefixMatcher)
|
||||
val params =
|
||||
CompletionParams(Position(position.line, position.column, position.index), file, cancelChecker)
|
||||
CompletionParams(Position(position.line, position.column, position.index), file,
|
||||
cancelChecker)
|
||||
params.content = content
|
||||
params.prefix = prefix
|
||||
server.complete(params)
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.itsaky.androidide.editor.language;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import com.itsaky.androidide.editor.api.IEditor;
|
||||
import com.itsaky.androidide.editor.ui.IDECompletionPublisher;
|
||||
import com.itsaky.androidide.lookup.Lookup;
|
||||
import com.itsaky.androidide.lsp.api.ILanguageServer;
|
||||
import com.itsaky.androidide.preferences.internal.EditorPreferencesKt;
|
||||
import com.itsaky.androidide.progress.ICancelChecker;
|
||||
import com.itsaky.androidide.utils.ILogger;
|
||||
import io.github.rosemoe.sora.lang.Language;
|
||||
import io.github.rosemoe.sora.lang.completion.CompletionCancelledException;
|
||||
import io.github.rosemoe.sora.lang.completion.CompletionPublisher;
|
||||
import io.github.rosemoe.sora.lang.format.Formatter;
|
||||
import io.github.rosemoe.sora.text.CharPosition;
|
||||
import io.github.rosemoe.sora.text.ContentReference;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Base class for language implementations in the IDE.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public abstract class IDELanguage implements Language {
|
||||
|
||||
private static final ILogger LOG = ILogger.newInstance("IDELanguage");
|
||||
private Formatter formatter;
|
||||
|
||||
@Override
|
||||
public void requireAutoComplete(
|
||||
@NonNull final ContentReference content,
|
||||
@NonNull final CharPosition position,
|
||||
@NonNull final CompletionPublisher publisher,
|
||||
@NonNull final Bundle extraArguments)
|
||||
throws CompletionCancelledException {
|
||||
try {
|
||||
final var cancelChecker = new CompletionCancelChecker(publisher);
|
||||
Lookup.getDefault().register(ICancelChecker.class, cancelChecker);
|
||||
doComplete(content, position, publisher, cancelChecker, extraArguments);
|
||||
} finally {
|
||||
Lookup.getDefault().unregister(ICancelChecker.class);
|
||||
}
|
||||
}
|
||||
|
||||
private void doComplete(
|
||||
final @NonNull ContentReference content,
|
||||
final @NonNull CharPosition position,
|
||||
final @NonNull CompletionPublisher publisher,
|
||||
final @NonNull CompletionCancelChecker cancelChecker,
|
||||
final @NonNull Bundle extraArguments) {
|
||||
final var server = getLanguageServer();
|
||||
if (server == null) {
|
||||
LOG.warn("Cannot provide completions. No language server available.");
|
||||
return;
|
||||
}
|
||||
|
||||
final var path = extraArguments.getString(IEditor.KEY_FILE, null);
|
||||
if (path == null) {
|
||||
LOG.warn("Cannot provide completions. No file provided.");
|
||||
return;
|
||||
}
|
||||
|
||||
final var completionProvider = new CommonCompletionProvider(server, cancelChecker);
|
||||
final var file = Paths.get(path);
|
||||
|
||||
final var completionItems =
|
||||
completionProvider.complete(content, file, position, this::checkIsCompletionChar);
|
||||
|
||||
publisher.setUpdateThreshold(1);
|
||||
|
||||
((IDECompletionPublisher) publisher).addLSPItems(completionItems);
|
||||
}
|
||||
|
||||
protected ILanguageServer getLanguageServer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given character is a completion character.
|
||||
*
|
||||
* @param c The character to check.
|
||||
* @return <code>true</code> if the character is completion char, <code>false</code> otherwise.
|
||||
*/
|
||||
protected boolean checkIsCompletionChar(char c) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useTab() {
|
||||
return !EditorPreferencesKt.getUseSoftTab();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Formatter getFormatter() {
|
||||
if (formatter != null) {
|
||||
return formatter;
|
||||
}
|
||||
|
||||
return formatter = new LSPFormatter(getLanguageServer());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import android.os.Bundle
|
||||
import com.itsaky.androidide.editor.api.IEditor
|
||||
import com.itsaky.androidide.editor.ui.IDECompletionPublisher
|
||||
import com.itsaky.androidide.lookup.Lookup
|
||||
import com.itsaky.androidide.lsp.api.ILanguageServer
|
||||
import com.itsaky.androidide.preferences.internal.tabSize
|
||||
import com.itsaky.androidide.preferences.internal.useSoftTab
|
||||
import com.itsaky.androidide.progress.ICancelChecker
|
||||
import com.itsaky.androidide.utils.ILogger
|
||||
import io.github.rosemoe.sora.lang.Language
|
||||
import io.github.rosemoe.sora.lang.completion.CompletionCancelledException
|
||||
import io.github.rosemoe.sora.lang.completion.CompletionPublisher
|
||||
import io.github.rosemoe.sora.lang.format.Formatter
|
||||
import io.github.rosemoe.sora.text.CharPosition
|
||||
import io.github.rosemoe.sora.text.ContentReference
|
||||
import java.nio.file.Paths
|
||||
|
||||
/**
|
||||
* Base class for language implementations in the IDE.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
abstract class IDELanguage : Language {
|
||||
|
||||
private var formatter: Formatter? = null
|
||||
|
||||
protected open val languageServer: ILanguageServer?
|
||||
get() = null
|
||||
|
||||
open fun getTabSize(): Int {
|
||||
return tabSize
|
||||
}
|
||||
|
||||
@Throws(CompletionCancelledException::class)
|
||||
override fun requireAutoComplete(
|
||||
content: ContentReference,
|
||||
position: CharPosition,
|
||||
publisher: CompletionPublisher,
|
||||
extraArguments: Bundle
|
||||
) {
|
||||
try {
|
||||
val cancelChecker = CompletionCancelChecker(publisher)
|
||||
Lookup.getDefault().register(ICancelChecker::class.java, cancelChecker)
|
||||
doComplete(content, position, publisher, cancelChecker, extraArguments)
|
||||
} finally {
|
||||
Lookup.getDefault().unregister(
|
||||
ICancelChecker::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
private fun doComplete(
|
||||
content: ContentReference,
|
||||
position: CharPosition,
|
||||
publisher: CompletionPublisher,
|
||||
cancelChecker: CompletionCancelChecker,
|
||||
extraArguments: Bundle
|
||||
) {
|
||||
val server = languageServer
|
||||
if (server == null) {
|
||||
LOG.warn("Cannot provide completions. No language server available.")
|
||||
return
|
||||
}
|
||||
val path = extraArguments.getString(IEditor.KEY_FILE, null)
|
||||
if (path == null) {
|
||||
LOG.warn("Cannot provide completions. No file provided.")
|
||||
return
|
||||
}
|
||||
val completionProvider = CommonCompletionProvider(server, cancelChecker)
|
||||
val file = Paths.get(path)
|
||||
val completionItems = completionProvider.complete(content, file, position) { checkIsCompletionChar(it) }
|
||||
publisher.setUpdateThreshold(1)
|
||||
(publisher as IDECompletionPublisher).addLSPItems(completionItems)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given character is a completion character.
|
||||
*
|
||||
* @param c The character to check.
|
||||
* @return `true` if the character is completion char, `false` otherwise.
|
||||
*/
|
||||
protected open fun checkIsCompletionChar(c: Char): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun useTab(): Boolean {
|
||||
return !useSoftTab
|
||||
}
|
||||
|
||||
override fun getFormatter(): Formatter {
|
||||
return formatter ?: LSPFormatter(languageServer).also { formatter = it }
|
||||
}
|
||||
|
||||
override fun getIndentAdvance(content: ContentReference, line: Int,
|
||||
column: Int): Int {
|
||||
return getIndentAdvance(content.getLine(line).substring(0, column))
|
||||
}
|
||||
|
||||
open fun getIndentAdvance(line: String): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val LOG = ILogger.newInstance("IDELanguage")
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -26,6 +26,7 @@ import com.itsaky.androidide.lsp.api.ILanguageServer
|
||||
import com.itsaky.androidide.lsp.api.ILanguageServerRegistry
|
||||
import com.itsaky.androidide.lsp.java.JavaLanguageServer
|
||||
import com.itsaky.androidide.treesitter.java.TSLanguageJava
|
||||
import io.github.rosemoe.sora.lang.Language.INTERRUPTION_LEVEL_SLIGHT
|
||||
import io.github.rosemoe.sora.util.MyCharacter
|
||||
import io.github.rosemoe.sora.widget.SymbolPairMatch
|
||||
|
||||
@@ -45,9 +46,8 @@ class JavaLanguage(context: Context) :
|
||||
val FACTORY = Factory { JavaLanguage(it) }
|
||||
}
|
||||
|
||||
override fun getLanguageServer(): ILanguageServer? {
|
||||
return ILanguageServerRegistry.getDefault().getServer(JavaLanguageServer.SERVER_ID)
|
||||
}
|
||||
override val languageServer: ILanguageServer?
|
||||
get() = ILanguageServerRegistry.getDefault().getServer(JavaLanguageServer.SERVER_ID)
|
||||
|
||||
override fun checkIsCompletionChar(c: Char): Boolean {
|
||||
return MyCharacter.isJavaIdentifierPart(c) || c == '.'
|
||||
|
||||
+5
-1
@@ -20,6 +20,7 @@ package com.itsaky.androidide.editor.language.treesitter
|
||||
import android.content.Context
|
||||
import com.itsaky.androidide.editor.language.treesitter.TreeSitterLanguage.Factory
|
||||
import com.itsaky.androidide.treesitter.json.TSLanguageJson
|
||||
import io.github.rosemoe.sora.lang.Language.INTERRUPTION_LEVEL_STRONG
|
||||
|
||||
/**
|
||||
* [TreeSitterLanguage] implementation for JSON files.
|
||||
@@ -28,10 +29,13 @@ import com.itsaky.androidide.treesitter.json.TSLanguageJson
|
||||
*/
|
||||
class JsonLanguage(context: Context) :
|
||||
TreeSitterLanguage(context, TSLanguageJson.getInstance(), TS_TYPE) {
|
||||
|
||||
companion object {
|
||||
|
||||
const val TS_TYPE = "json"
|
||||
|
||||
@JvmField val FACTORY = Factory { JsonLanguage(it) }
|
||||
@JvmField
|
||||
val FACTORY = Factory { JsonLanguage(it) }
|
||||
}
|
||||
|
||||
override fun getInterruptionLevel(): Int {
|
||||
|
||||
+3
@@ -18,7 +18,9 @@
|
||||
package com.itsaky.androidide.editor.language.treesitter
|
||||
|
||||
import android.content.Context
|
||||
import com.itsaky.androidide.editor.language.treesitter.TreeSitterLanguage.Factory
|
||||
import com.itsaky.androidide.treesitter.kotlin.TSLanguageKotlin
|
||||
import io.github.rosemoe.sora.lang.Language.INTERRUPTION_LEVEL_STRONG
|
||||
|
||||
/**
|
||||
* [TreeSitterLanguage] implementation for Kotlin.
|
||||
@@ -29,6 +31,7 @@ open class KotlinLanguage(context: Context) :
|
||||
TreeSitterLanguage(context, TSLanguageKotlin.getInstance(), TS_TYPE_KT) {
|
||||
|
||||
companion object {
|
||||
|
||||
val FACTORY = Factory { KotlinLanguage(it) }
|
||||
const val TS_TYPE_KT = "kt"
|
||||
const val TS_TYPE_KTS = "kts"
|
||||
|
||||
+1
@@ -27,6 +27,7 @@ import com.itsaky.androidide.editor.language.treesitter.internal.TSLanguageRegis
|
||||
interface TSLanguageRegistry {
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
val instance by lazy { TSLanguageRegistryImpl() }
|
||||
}
|
||||
|
||||
+3
-2
@@ -33,6 +33,7 @@ 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.TsTheme
|
||||
import io.github.rosemoe.sora.lang.Language.INTERRUPTION_LEVEL_STRONG
|
||||
import io.github.rosemoe.sora.lang.analysis.AnalyzeManager
|
||||
import io.github.rosemoe.sora.text.ContentReference
|
||||
import io.github.rosemoe.sora.text.TextUtils
|
||||
@@ -78,7 +79,7 @@ abstract class TreeSitterLanguage(context: Context, lang: TSLanguage, type: Stri
|
||||
}
|
||||
|
||||
open fun finalizeIndent(indent: Int): Int {
|
||||
return indent * tabSize
|
||||
return indent * getTabSize()
|
||||
}
|
||||
|
||||
override fun getAnalyzeManager(): AnalyzeManager {
|
||||
@@ -106,7 +107,7 @@ abstract class TreeSitterLanguage(context: Context, lang: TSLanguage, type: Stri
|
||||
content.toString(),
|
||||
line,
|
||||
column,
|
||||
decrementBy = TextUtils.countLeadingSpaceCount(content.getLine(line), tabSize)
|
||||
decrementBy = TextUtils.countLeadingSpaceCount(content.getLine(line), getTabSize())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -30,7 +30,7 @@ import java.io.Closeable
|
||||
*/
|
||||
class TreeSitterLanguageSpec
|
||||
@JvmOverloads constructor(val spec: TsLanguageSpec,
|
||||
indentsQueryScm: String = ""
|
||||
indentsQueryScm: String = ""
|
||||
) : Closeable {
|
||||
|
||||
// <editor-fold desc="Proxy properties">
|
||||
@@ -38,7 +38,8 @@ class TreeSitterLanguageSpec
|
||||
get() = spec.language
|
||||
// </editor-fold>
|
||||
|
||||
val indentsQuery: TSQuery? = if (indentsQueryScm.isBlank()) TSQuery.EMPTY else 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 {
|
||||
|
||||
+4
-4
@@ -23,6 +23,7 @@ import com.itsaky.androidide.lsp.api.ILanguageServer
|
||||
import com.itsaky.androidide.lsp.api.ILanguageServerRegistry
|
||||
import com.itsaky.androidide.lsp.xml.XMLLanguageServer
|
||||
import com.itsaky.androidide.treesitter.xml.TSLanguageXml
|
||||
import io.github.rosemoe.sora.lang.Language.INTERRUPTION_LEVEL_STRONG
|
||||
import io.github.rosemoe.sora.util.MyCharacter
|
||||
|
||||
/**
|
||||
@@ -33,6 +34,9 @@ import io.github.rosemoe.sora.util.MyCharacter
|
||||
class XMLLanguage(context: Context) :
|
||||
TreeSitterLanguage(context, lang = TSLanguageXml.getInstance(), type = TS_TYPE) {
|
||||
|
||||
override val languageServer: ILanguageServer?
|
||||
get() = ILanguageServerRegistry.getDefault().getServer(XMLLanguageServer.SERVER_ID)
|
||||
|
||||
companion object {
|
||||
|
||||
const val TS_TYPE = "xml"
|
||||
@@ -41,10 +45,6 @@ class XMLLanguage(context: Context) :
|
||||
val FACTORY = Factory { XMLLanguage(it) }
|
||||
}
|
||||
|
||||
override fun getLanguageServer(): ILanguageServer? {
|
||||
return ILanguageServerRegistry.getDefault().getServer(XMLLanguageServer.SERVER_ID)
|
||||
}
|
||||
|
||||
override fun checkIsCompletionChar(c: Char): Boolean {
|
||||
return MyCharacter.isJavaIdentifierPart(c) || c == '<' || c == '/'
|
||||
}
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ class TSLanguageRegistryImpl : TSLanguageRegistry {
|
||||
fileType: String
|
||||
): TreeSitterLanguage.Factory<T> {
|
||||
return (registry[fileType] ?: throw TSLanguageRegistry.NotRegisteredException(fileType))
|
||||
as TreeSitterLanguage.Factory<T>
|
||||
as TreeSitterLanguage.Factory<T>
|
||||
}
|
||||
|
||||
override fun destroy() {
|
||||
|
||||
+8
-8
@@ -42,14 +42,14 @@ object AnyOfPredicate : TreeSitterPredicate() {
|
||||
|
||||
override fun canHandle(steps: List<TsClientPredicateStep>): Boolean {
|
||||
return steps.size > 4 &&
|
||||
steps.let {
|
||||
it[0].predicateType == TSQueryPredicateStep.Type.String &&
|
||||
it[1].predicateType == TSQueryPredicateStep.Type.Capture &&
|
||||
it[it.lastIndex].predicateType == TSQueryPredicateStep.Type.Done &&
|
||||
it.subList(2, it.lastIndex - 1).all { step ->
|
||||
step.predicateType == TSQueryPredicateStep.Type.String
|
||||
}
|
||||
}
|
||||
steps.let {
|
||||
it[0].predicateType == TSQueryPredicateStep.Type.String &&
|
||||
it[1].predicateType == TSQueryPredicateStep.Type.Capture &&
|
||||
it[it.lastIndex].predicateType == TSQueryPredicateStep.Type.Done &&
|
||||
it.subList(2, it.lastIndex - 1).all { step ->
|
||||
step.predicateType == TSQueryPredicateStep.Type.String
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun doPredicateInternal(
|
||||
|
||||
+7
-7
@@ -43,12 +43,12 @@ object EqualPredicate : TreeSitterPredicate() {
|
||||
|
||||
override fun canHandle(steps: List<TsClientPredicateStep>): Boolean {
|
||||
return steps.size == 4 &&
|
||||
steps[0].predicateType == TSQueryPredicateStep.Type.String &&
|
||||
steps[1].predicateType == TSQueryPredicateStep.Type.Capture &&
|
||||
steps[2].predicateType.let {
|
||||
it == TSQueryPredicateStep.Type.Capture || it == TSQueryPredicateStep.Type.String
|
||||
} &&
|
||||
steps[3].predicateType == TSQueryPredicateStep.Type.Done
|
||||
steps[0].predicateType == TSQueryPredicateStep.Type.String &&
|
||||
steps[1].predicateType == TSQueryPredicateStep.Type.Capture &&
|
||||
steps[2].predicateType.let {
|
||||
it == TSQueryPredicateStep.Type.Capture || it == TSQueryPredicateStep.Type.String
|
||||
} &&
|
||||
steps[3].predicateType == TSQueryPredicateStep.Type.Done
|
||||
}
|
||||
|
||||
override fun doPredicateInternal(
|
||||
@@ -63,7 +63,7 @@ object EqualPredicate : TreeSitterPredicate() {
|
||||
predicateSteps[2].let {
|
||||
check(
|
||||
it.predicateType == TSQueryPredicateStep.Type.String ||
|
||||
it.predicateType == TSQueryPredicateStep.Type.Capture
|
||||
it.predicateType == TSQueryPredicateStep.Type.Capture
|
||||
) {
|
||||
"Second predicate step of #eq? predicate must be a string or a capture"
|
||||
}
|
||||
|
||||
+2
-1
@@ -28,7 +28,8 @@ import io.github.rosemoe.sora.editor.ts.predicate.TsSyntheticCaptureContainer
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
open class InvertingPredicate(override val name: String, private val predicate: TreeSitterPredicate) :
|
||||
open class InvertingPredicate(override val name: String,
|
||||
private val predicate: TreeSitterPredicate) :
|
||||
TreeSitterPredicate() {
|
||||
|
||||
override fun canHandle(steps: List<TsClientPredicateStep>): Boolean {
|
||||
|
||||
+2
-2
@@ -70,8 +70,8 @@ abstract class TreeSitterPredicate : TsPredicate {
|
||||
|
||||
if (
|
||||
predicateSteps.isEmpty() ||
|
||||
predicateSteps[0].content != "${name}?" ||
|
||||
!canHandle(predicateSteps)
|
||||
predicateSteps[0].content != "${name}?" ||
|
||||
!canHandle(predicateSteps)
|
||||
) {
|
||||
return PredicateResult.UNHANDLED
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.utils
|
||||
|
||||
import io.github.rosemoe.sora.text.CharPosition
|
||||
import io.github.rosemoe.sora.text.ContentReference
|
||||
|
||||
/**
|
||||
* Utility functions for completion providers.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
object CompletionHelper {
|
||||
|
||||
/**
|
||||
* Searches backward on the line, with the given checker to check chars.
|
||||
* Returns the longest text that matches the requirement.
|
||||
*
|
||||
* This is a variant of [CompletionHelper.computePrefix][io.github.rosemoe.sora.lang.completion.CompletionHelper.computePrefix]
|
||||
* which inlines the predicate for better performance.
|
||||
*/
|
||||
inline fun computePrefix(
|
||||
ref: ContentReference, pos: CharPosition,
|
||||
checker: (Char) -> Boolean
|
||||
): String {
|
||||
var begin = pos.column
|
||||
val line = ref.getLine(pos.line)
|
||||
while (begin > 0) {
|
||||
if (!checker(line[begin - 1])) {
|
||||
break
|
||||
}
|
||||
begin--
|
||||
}
|
||||
return line.substring(begin, pos.column)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user