diff --git a/build-tools/tooling-api-impl/src/test/test-home/.androidide/init/model.jar b/build-tools/tooling-api-impl/src/test/test-home/.androidide/init/model.jar index 7b2a07034..3cac0d415 100644 Binary files a/build-tools/tooling-api-impl/src/test/test-home/.androidide/init/model.jar and b/build-tools/tooling-api-impl/src/test/test-home/.androidide/init/model.jar differ diff --git a/lsp/api/build.gradle b/lsp/api/build.gradle index 907ae89a8..729c83b99 100644 --- a/lsp/api/build.gradle +++ b/lsp/api/build.gradle @@ -50,6 +50,7 @@ dependencies { implementation androidx.appcompat implementation androidx.ktx implementation common.kotlin + implementation common.utilcode implementation google.material api project (':build-tools:tooling-api-model') diff --git a/lsp/api/src/main/java/com/itsaky/lsp/models/Completions.kt b/lsp/api/src/main/java/com/itsaky/lsp/models/Completions.kt index 0af0d536b..1d9123b74 100644 --- a/lsp/api/src/main/java/com/itsaky/lsp/models/Completions.kt +++ b/lsp/api/src/main/java/com/itsaky/lsp/models/Completions.kt @@ -17,6 +17,8 @@ package com.itsaky.lsp.models +import android.os.Looper +import com.blankj.utilcode.util.ThreadUtils import com.itsaky.androidide.fuzzysearch.FuzzySearch import com.itsaky.androidide.tooling.api.model.IdeGradleProject import com.itsaky.androidide.utils.ILogger @@ -31,6 +33,9 @@ import com.itsaky.lsp.models.MatchLevel.PARTIAL_MATCH import io.github.rosemoe.sora.text.Content import io.github.rosemoe.sora.widget.CodeEditor import java.nio.file.Path +import java.util.concurrent.* +import java.util.concurrent.TimeUnit.* +import java.util.function.* data class CompletionParams(var position: Position, var file: Path) { var content: CharSequence? = null @@ -57,13 +62,12 @@ open class CompletionResult(items: List) { val items: List = run { var temp = items.toMutableList() temp.sort() - if (TRIM_TO_MAX && temp.size > MAX_ITEMS) { temp = temp.subList(0, MAX_ITEMS) } return@run temp } - + var isIncomplete = this.items.size < items.size var isCached = false @@ -88,7 +92,7 @@ open class CompletionResult(items: List) { // Max limit has been reached return } - + if (items is MutableList) { this.items.add(item) } @@ -118,6 +122,8 @@ open class CompletionItem( ) : io.github.rosemoe.sora.lang.completion.CompletionItem(label, detail), Comparable { + private val log = ILogger.newInstance(javaClass.simpleName) + var sortText: String? = sortText get() { if (field == null) { @@ -138,6 +144,8 @@ open class CompletionItem( var insertTextFormat: InsertTextFormat = insertTextFormat ?: PLAIN_TEXT + var postComputeAdditionalEdits: Supplier>? = null + constructor() : this( "", // label @@ -191,6 +199,11 @@ open class CompletionItem( fun getLabel(): String = this.label as String override fun performCompletion(editor: CodeEditor, text: Content, line: Int, column: Int) { + if (Looper.myLooper() != Looper.getMainLooper()) { + ThreadUtils.runOnUiThread { performCompletion(editor, text, line, column) } + return + } + val start = getIdentifierStart(text.getLine(line), column) val shift = insertText.contains("$0") @@ -204,7 +217,6 @@ open class CompletionItem( if (i != 0) { commit = "\n" + commit } - editor.commitText(commit) i++ } @@ -223,6 +235,17 @@ open class CompletionItem( } } + if (postComputeAdditionalEdits != null) { + try { + log.debug("Computing text edits for selected completion item...") + this.additionalTextEdits = + CompletableFuture.supplyAsync(postComputeAdditionalEdits).get(200, MILLISECONDS) + } catch (e: Throwable) { + log.error("Unable to compute additional edits for completion item", e) + this.additionalTextEdits = null + } + } + if (additionalTextEdits != null && additionalTextEdits!!.isNotEmpty()) { additionalTextEdits!!.forEach { val s = it.range.start @@ -250,17 +273,14 @@ open class CompletionItem( } private fun getIdentifierStart(text: CharSequence, end: Int): Int { - var start = end while (start > 0) { if (Character.isJavaIdentifierPart(text[start - 1])) { start-- continue } - break } - return start } diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/CompletionProvider.java b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/CompletionProvider.java index 07888d663..277bab27d 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/CompletionProvider.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/CompletionProvider.java @@ -127,7 +127,7 @@ public class CompletionProvider extends AbstractServiceProvider implements IComp result.markCached(); - if (!result.isIncomplete() && result.getItems().size() > 10) { + if (!result.isIncomplete() && !result.getItems().isEmpty()) { LOG.info("...using cached completion"); logCompletionDuration(started, result); return result; diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/completion/IJavaCompletionProvider.kt b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/completion/IJavaCompletionProvider.kt index 879ccb477..e0a966b1d 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/completion/IJavaCompletionProvider.kt +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/completion/IJavaCompletionProvider.kt @@ -39,6 +39,7 @@ import com.itsaky.lsp.models.MatchLevel import com.sun.source.tree.Tree import com.sun.source.util.TreePath import java.nio.file.Path +import java.util.function.* import javax.lang.model.element.Element import javax.lang.model.element.ElementKind.ANNOTATION_TYPE import javax.lang.model.element.ElementKind.CLASS @@ -75,7 +76,7 @@ abstract class IJavaCompletionProvider( protected val log: ILogger = ILogger.newInstance(javaClass.name) protected lateinit var filePackage: String protected lateinit var fileImports: Set - + open fun complete( task: CompileTask, path: TreePath, @@ -85,7 +86,7 @@ abstract class IJavaCompletionProvider( val root = task.root(completingFile) filePackage = root.`package`.packageName.toString() fileImports = root.imports.map { it.qualifiedIdentifier.toString() }.toSet() - return doComplete(task, path, partial, endsWithParen) + return doComplete(task, path, partial, endsWithParen); } /** @@ -201,7 +202,9 @@ abstract class IJavaCompletionProvider( val data = CompletionData() data.className = className item.data = data - item.additionalTextEdits = EditHelper.addImportIfNeeded(compiler, file, imports, className) + item.postComputeAdditionalEdits = Supplier { + EditHelper.addImportIfNeeded(compiler, file, imports, className) + } return item } diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/completion/ScopeCompletionProvider.kt b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/completion/ScopeCompletionProvider.kt index 7ffa81242..cda5b3ed1 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/providers/completion/ScopeCompletionProvider.kt +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/providers/completion/ScopeCompletionProvider.kt @@ -179,7 +179,7 @@ class ScopeCompletionProvider( if (item.additionalTextEdits == null) { item.additionalTextEdits = mutableListOf() } - + imports.removeIf { "java.lang." == it || fileImports.contains(it) || filePackage == it } if (imports.isNotEmpty()) { for (className in imports) { diff --git a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/AddImport.java b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/AddImport.java index 7f2e57388..a4e7a0003 100644 --- a/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/AddImport.java +++ b/lsp/java/src/main/java/com/itsaky/lsp/java/rewrite/AddImport.java @@ -56,6 +56,7 @@ public class AddImport extends Rewrite { file, new TextEdit[] {new TextEdit(new Range(point, point), text)}); } + @SuppressWarnings("Since15") private Position insertPosition(@NonNull ParseTask task) { List imports = task.root.getImports(); for (ImportTree i : imports) {