From 67b1388779ce24d251d4e03de3fd95a54ba1aa35 Mon Sep 17 00:00:00 2001 From: Akash Yadav Date: Wed, 15 Nov 2023 09:48:12 +0530 Subject: [PATCH] fix(editor): migrate IDELanguage to Kotlin --- .../language/CommonCompletionProvider.kt | 25 ++-- .../editor/language/IDELanguage.java | 134 ------------------ .../androidide/editor/language/IDELanguage.kt | 124 ++++++++++++++++ .../language/treesitter/JavaLanguage.kt | 6 +- .../language/treesitter/JsonLanguage.kt | 6 +- .../language/treesitter/KotlinLanguage.kt | 3 + .../language/treesitter/TSLanguageRegistry.kt | 1 + .../language/treesitter/TreeSitterLanguage.kt | 5 +- .../treesitter/TreeSitterLanguageSpec.kt | 5 +- .../editor/language/treesitter/XMLLanguage.kt | 8 +- .../internal/TSLanguageRegistryImpl.kt | 2 +- .../treesitter/predicates/AnyOfPredicate.kt | 16 +-- .../treesitter/predicates/EqualPredicate.kt | 14 +- .../predicates/InvertingPredicate.kt | 3 +- .../predicates/TreeSitterPredicate.kt | 4 +- .../editor/language/utils/CompletionHelper.kt | 51 +++++++ 16 files changed, 232 insertions(+), 175 deletions(-) delete mode 100644 editor/src/main/java/com/itsaky/androidide/editor/language/IDELanguage.java create mode 100644 editor/src/main/java/com/itsaky/androidide/editor/language/IDELanguage.kt create mode 100644 editor/src/main/java/com/itsaky/androidide/editor/language/utils/CompletionHelper.kt diff --git a/editor/src/main/java/com/itsaky/androidide/editor/language/CommonCompletionProvider.kt b/editor/src/main/java/com/itsaky/androidide/editor/language/CommonCompletionProvider.kt index f326a592e..00678787f 100644 --- a/editor/src/main/java/com/itsaky/androidide/editor/language/CommonCompletionProvider.kt +++ b/editor/src/main/java/com/itsaky/androidide/editor/language/CommonCompletionProvider.kt @@ -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 + prefixMatcher: (Char) -> Boolean ): List { 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) diff --git a/editor/src/main/java/com/itsaky/androidide/editor/language/IDELanguage.java b/editor/src/main/java/com/itsaky/androidide/editor/language/IDELanguage.java deleted file mode 100644 index 47e273d4f..000000000 --- a/editor/src/main/java/com/itsaky/androidide/editor/language/IDELanguage.java +++ /dev/null @@ -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 . - */ -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 true if the character is completion char, false 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; - } -} diff --git a/editor/src/main/java/com/itsaky/androidide/editor/language/IDELanguage.kt b/editor/src/main/java/com/itsaky/androidide/editor/language/IDELanguage.kt new file mode 100644 index 000000000..34f99d3c2 --- /dev/null +++ b/editor/src/main/java/com/itsaky/androidide/editor/language/IDELanguage.kt @@ -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 . + */ +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") + } +} \ No newline at end of file diff --git a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/JavaLanguage.kt b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/JavaLanguage.kt index 235d4e569..834c44957 100644 --- a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/JavaLanguage.kt +++ b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/JavaLanguage.kt @@ -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 == '.' diff --git a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/JsonLanguage.kt b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/JsonLanguage.kt index 2b718580c..6ab361fb5 100644 --- a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/JsonLanguage.kt +++ b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/JsonLanguage.kt @@ -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 { diff --git a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/KotlinLanguage.kt b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/KotlinLanguage.kt index 76d003e1f..4bb0fe547 100644 --- a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/KotlinLanguage.kt +++ b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/KotlinLanguage.kt @@ -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" diff --git a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/TSLanguageRegistry.kt b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/TSLanguageRegistry.kt index 9d556d16d..e8472603a 100644 --- a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/TSLanguageRegistry.kt +++ b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/TSLanguageRegistry.kt @@ -27,6 +27,7 @@ import com.itsaky.androidide.editor.language.treesitter.internal.TSLanguageRegis interface TSLanguageRegistry { companion object { + @JvmStatic val instance by lazy { TSLanguageRegistryImpl() } } diff --git a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/TreeSitterLanguage.kt b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/TreeSitterLanguage.kt index 23897b9ec..1ccde2454 100644 --- a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/TreeSitterLanguage.kt +++ b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/TreeSitterLanguage.kt @@ -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()) ) } diff --git a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/TreeSitterLanguageSpec.kt b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/TreeSitterLanguageSpec.kt index 3a3c83ee5..c8d8a5c4a 100644 --- a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/TreeSitterLanguageSpec.kt +++ b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/TreeSitterLanguageSpec.kt @@ -30,7 +30,7 @@ import java.io.Closeable */ class TreeSitterLanguageSpec @JvmOverloads constructor(val spec: TsLanguageSpec, - indentsQueryScm: String = "" + indentsQueryScm: String = "" ) : Closeable { // @@ -38,7 +38,8 @@ class TreeSitterLanguageSpec get() = spec.language // - 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 { diff --git a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/XMLLanguage.kt b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/XMLLanguage.kt index 2cde352c0..570e798d5 100644 --- a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/XMLLanguage.kt +++ b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/XMLLanguage.kt @@ -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 == '/' } diff --git a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/internal/TSLanguageRegistryImpl.kt b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/internal/TSLanguageRegistryImpl.kt index 842c73986..5d408373d 100644 --- a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/internal/TSLanguageRegistryImpl.kt +++ b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/internal/TSLanguageRegistryImpl.kt @@ -47,7 +47,7 @@ class TSLanguageRegistryImpl : TSLanguageRegistry { fileType: String ): TreeSitterLanguage.Factory { return (registry[fileType] ?: throw TSLanguageRegistry.NotRegisteredException(fileType)) - as TreeSitterLanguage.Factory + as TreeSitterLanguage.Factory } override fun destroy() { diff --git a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/predicates/AnyOfPredicate.kt b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/predicates/AnyOfPredicate.kt index 8541fd55e..cead7bdbb 100644 --- a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/predicates/AnyOfPredicate.kt +++ b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/predicates/AnyOfPredicate.kt @@ -42,14 +42,14 @@ object AnyOfPredicate : TreeSitterPredicate() { override fun canHandle(steps: List): 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( diff --git a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/predicates/EqualPredicate.kt b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/predicates/EqualPredicate.kt index 6aeb3a074..51d0bb840 100644 --- a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/predicates/EqualPredicate.kt +++ b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/predicates/EqualPredicate.kt @@ -43,12 +43,12 @@ object EqualPredicate : TreeSitterPredicate() { override fun canHandle(steps: List): 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" } diff --git a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/predicates/InvertingPredicate.kt b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/predicates/InvertingPredicate.kt index 825b8c1bd..685edb916 100644 --- a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/predicates/InvertingPredicate.kt +++ b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/predicates/InvertingPredicate.kt @@ -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): Boolean { diff --git a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/predicates/TreeSitterPredicate.kt b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/predicates/TreeSitterPredicate.kt index 6a95e9ff6..24d35696a 100644 --- a/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/predicates/TreeSitterPredicate.kt +++ b/editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/predicates/TreeSitterPredicate.kt @@ -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 } diff --git a/editor/src/main/java/com/itsaky/androidide/editor/language/utils/CompletionHelper.kt b/editor/src/main/java/com/itsaky/androidide/editor/language/utils/CompletionHelper.kt new file mode 100644 index 000000000..36dbc9287 --- /dev/null +++ b/editor/src/main/java/com/itsaky/androidide/editor/language/utils/CompletionHelper.kt @@ -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 . + */ + +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) + } +} \ No newline at end of file