refactor: rename CompletionItem.kind to avoid conflict with editor's CompletionItem.kind

This commit is contained in:
Akash Yadav
2023-03-31 13:31:01 +05:30
parent 43403304fa
commit e8e203f548
9 changed files with 82 additions and 204 deletions
@@ -24,10 +24,7 @@ import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams
import android.view.ViewTreeObserver
import android.widget.TextView
import androidx.core.view.updateLayoutParams
import com.itsaky.androidide.editor.R
import com.itsaky.androidide.editor.databinding.LayoutCompletionItemBinding
import com.itsaky.androidide.lookup.Lookup
@@ -59,7 +56,6 @@ import io.github.rosemoe.sora.lang.completion.CompletionItem
import io.github.rosemoe.sora.widget.component.EditorAutoCompletion
import io.github.rosemoe.sora.widget.component.EditorCompletionAdapter
import io.github.rosemoe.sora.widget.schemes.EditorColorScheme
import kotlin.math.max
class CompletionListAdapter : EditorCompletionAdapter() {
@@ -88,7 +84,7 @@ class CompletionListAdapter : EditorCompletionAdapter() {
val item = getItem(position) as LspCompletionItem
val label = item.getLabel()
val desc = item.detail
var type: String? = item.kind.toString()
var type: String? = item.completionKind.toString()
val header = if (type!!.isEmpty()) "O" else type[0].toString()
if (item.overrideTypeText != null) {
type = item.overrideTypeText
@@ -160,7 +156,7 @@ class CompletionListAdapter : EditorCompletionAdapter() {
is MemberCompletionData -> data.classInfo.className
else -> return@executeAsync null
}
val kind = item.kind
val kind = item.completionKind
val clazz = versions.getClass(className) ?: return@executeAsync null
var info: Info? = clazz
@@ -212,7 +208,7 @@ class CompletionListAdapter : EditorCompletionAdapter() {
if (item == null) {
return false
}
val type = item.kind
val type = item.completionKind
val data = item.data
return if ( // These represent a class type
(type === CLASS ||
@@ -17,7 +17,6 @@
package com.itsaky.androidide.editor.language.groovy;
import android.os.Bundle;
import android.text.TextUtils;
import com.blankj.utilcode.util.StringUtils;
import com.itsaky.androidide.lsp.models.CompletionItem;
import com.itsaky.androidide.lsp.models.CompletionItemKind;
@@ -389,7 +388,7 @@ public class GroovyAutoComplete {
item.setInsertText(itemLabel);
item.setIdeSortText("0" + itemLabel);
item.setInsertTextFormat(InsertTextFormat.PLAIN_TEXT);
item.setKind(CompletionItemKind.SNIPPET);
item.setCompletionKind(CompletionItemKind.NONE);
return item;
}
}
@@ -41,7 +41,6 @@ import com.itsaky.androidide.lsp.java.providers.completion.KeywordCompletionProv
import com.itsaky.androidide.lsp.java.providers.completion.MemberReferenceCompletionProvider;
import com.itsaky.androidide.lsp.java.providers.completion.MemberSelectCompletionProvider;
import com.itsaky.androidide.lsp.java.providers.completion.SwitchConstantCompletionProvider;
import com.itsaky.androidide.lsp.java.providers.completion.TopLevelSnippetsProvider;
import com.itsaky.androidide.lsp.java.utils.ASTFixer;
import com.itsaky.androidide.lsp.java.utils.CancelChecker;
import com.itsaky.androidide.lsp.java.visitors.FindCompletionsAt;
@@ -73,11 +72,10 @@ public class CompletionProvider extends AbstractServiceProvider implements IComp
super();
}
public synchronized CompletionProvider reset(
JavaCompilerService compiler,
IServerSettings settings,
CachedCompletion cache,
Consumer<CachedCompletion> nextCacheConsumer) {
public synchronized CompletionProvider reset(JavaCompilerService compiler,
IServerSettings settings, CachedCompletion cache,
Consumer<CachedCompletion> nextCacheConsumer
) {
this.compiler = compiler;
this.cache = cache;
this.nextCacheConsumer = nextCacheConsumer;
@@ -130,25 +128,22 @@ public class CompletionProvider extends AbstractServiceProvider implements IComp
if (this.cache != null && this.cache.canUseCache(params)) {
final String prefix = params.requirePrefix();
final String partial = partialIdentifier(prefix, prefix.length());
final CompletionResult result =
CompletionResult.mapAndFilter(
this.cache.getResult(),
partial,
item -> {
final var description = item.getSnippetDescription();
var deleteSelected = true;
var allowCommands = false;
CodeSnippet snippet = null;
final CompletionResult result = CompletionResult.mapAndFilter(this.cache.getResult(), partial,
item -> {
final var description = item.getSnippetDescription();
var deleteSelected = true;
var allowCommands = false;
CodeSnippet snippet = null;
if (description != null) {
deleteSelected = description.getDeleteSelected();
allowCommands = description.getAllowCommandExecution();
snippet = description.getSnippet();
}
if (description != null) {
deleteSelected = description.getDeleteSelected();
allowCommands = description.getAllowCommandExecution();
snippet = description.getSnippet();
}
item.setSnippetDescription(
describeSnippet(partial, deleteSelected, snippet, allowCommands));
});
item.setSnippetDescription(
describeSnippet(partial, deleteSelected, snippet, allowCommands));
});
result.markCached();
@@ -183,8 +178,8 @@ public class CompletionProvider extends AbstractServiceProvider implements IComp
}
final String contentString = contents.toString();
final PartialReparseRequest partialRequest =
new PartialReparseRequest(cursor - params.requirePrefix().length(), contentString);
final PartialReparseRequest partialRequest = new PartialReparseRequest(
cursor - params.requirePrefix().length(), contentString);
abortIfCancelled();
abortCompletionIfCancelled();
@@ -217,14 +212,9 @@ public class CompletionProvider extends AbstractServiceProvider implements IComp
private void logCompletionDuration(Instant started, @NonNull CompletionResult result) {
long elapsedMs = Duration.between(started, Instant.now()).toMillis();
LOG.info(
String.format(
Locale.US,
"Found %d items%s%sin %,d ms",
result.getItems().size(),
result.isIncomplete() ? " (incomplete) " : "",
result.isCached() ? " (cached) " : " ",
elapsedMs));
LOG.info(String.format(Locale.US, "Found %d items%s%sin %,d ms", result.getItems().size(),
result.isIncomplete() ? " (incomplete) " : "", result.isCached() ? " (cached) " : " ",
elapsedMs));
}
private int endOfLine(@NonNull CharSequence contents, int cursor) {
@@ -238,8 +228,9 @@ public class CompletionProvider extends AbstractServiceProvider implements IComp
return cursor;
}
private CompletionResult compileAndComplete(
String contents, CompletionParams params, PartialReparseRequest partialRequest) {
private CompletionResult compileAndComplete(String contents, CompletionParams params,
PartialReparseRequest partialRequest
) {
final long cursor = params.getPosition().requireIndex();
final var file = params.getFile();
final var started = Instant.now();
@@ -250,58 +241,51 @@ public class CompletionProvider extends AbstractServiceProvider implements IComp
abortIfCancelled();
abortCompletionIfCancelled();
final CompilationRequest request =
new CompilationRequest(Collections.singletonList(source), partialRequest);
final CompilationRequest request = new CompilationRequest(Collections.singletonList(source),
partialRequest);
request.configureContext = ctx -> {
final var config = JavaCompilerConfig.instance(ctx);
config.setCompletionInfo(new CompletionInfo(params.getPosition()));
};
SynchronizedTask synchronizedTask = compiler.compile(request);
return synchronizedTask.get(
task -> {
if (task == null || task.task == null || task.task.getContext() == null) {
LOG.warn("Compilation resulted in an invalid JavacTask");
return CompletionResult.EMPTY;
return synchronizedTask.get(task -> {
if (task == null || task.task == null || task.task.getContext() == null) {
LOG.warn("Compilation resulted in an invalid JavacTask");
return CompletionResult.EMPTY;
}
abortIfCancelled();
abortCompletionIfCancelled();
LOG.info("...compiled in " + Duration.between(started, Instant.now()).toMillis() + "ms");
TreePath path = new FindCompletionsAt(task.task).scan(task.root(), cursor);
abortIfCancelled();
abortCompletionIfCancelled();
String newPartial = partial;
if (path.getLeaf().getKind() == Tree.Kind.IMPORT) {
newPartial = qualifiedPartialIdentifier(contents, (int) cursor);
if (newPartial.endsWith(ASTFixer.IDENT)) {
newPartial = newPartial.substring(0, newPartial.length() - ASTFixer.IDENT.length());
}
abortIfCancelled();
abortCompletionIfCancelled();
LOG.info("...compiled in " + Duration.between(started, Instant.now()).toMillis() + "ms");
TreePath path = new FindCompletionsAt(task.task).scan(task.root(), cursor);
}
abortIfCancelled();
abortCompletionIfCancelled();
String newPartial = partial;
if (path.getLeaf().getKind() == Tree.Kind.IMPORT) {
newPartial = qualifiedPartialIdentifier(contents, (int) cursor);
if (newPartial.endsWith(ASTFixer.IDENT)) {
newPartial = newPartial.substring(0, newPartial.length() - ASTFixer.IDENT.length());
}
}
final var result = doComplete(file, contents, cursor, newPartial, endsWithParen, task, path);
final var result = doComplete(file, contents, cursor, newPartial, endsWithParen, task,
path);
new TopLevelSnippetsProvider().complete(partial, task.root(), result);
// IMPORTANT: Unregister the completion info from the compiler configuration
if (task.task.getContext() != null) {
final var compilerConfig = JavaCompilerConfig.instance(task.task.getContext());
compilerConfig.setCompletionInfo(null);
}
// IMPORTANT: Unregister the completion info from the compiler configuration
if (task.task.getContext() != null) {
final var compilerConfig = JavaCompilerConfig.instance(task.task.getContext());
compilerConfig.setCompletionInfo(null);
}
return result;
});
return result;
});
}
@NonNull
private CompletionResult doComplete(
final Path file,
final String contents,
final long cursor,
final String partial,
final boolean endsWithParen,
final CompileTask task,
final TreePath path) {
private CompletionResult doComplete(final Path file, final String contents, final long cursor,
final String partial, final boolean endsWithParen,
final CompileTask task, final TreePath path
) {
final Class<? extends IJavaCompletionProvider> klass;
abortIfCancelled();
abortCompletionIfCancelled();
@@ -326,12 +310,13 @@ public class CompletionProvider extends AbstractServiceProvider implements IComp
break;
}
final IJavaCompletionProvider provider =
ReflectUtils.reflect(klass).newInstance(file, cursor, compiler, getSettings()).get();
final IJavaCompletionProvider provider = ReflectUtils.reflect(klass)
.newInstance(file, cursor, compiler, getSettings())
.get();
if (provider instanceof ImportCompletionProvider) {
((ImportCompletionProvider) provider)
.setImportPath(qualifiedPartialIdentifier(contents, (int) cursor));
((ImportCompletionProvider) provider).setImportPath(
qualifiedPartialIdentifier(contents, (int) cursor));
}
abortIfCancelled();
@@ -150,7 +150,7 @@ abstract class IJavaCompletionProvider(
abortCompletionIfCancelled()
val item = JavaCompletionItem()
item.setLabel(keyword)
item.kind = KEYWORD
item.completionKind = KEYWORD
item.detail = "keyword"
item.ideSortText = keyword
item.matchLevel = matchLevel
@@ -169,7 +169,7 @@ abstract class IJavaCompletionProvider(
val first = overloads[0]
val item = JavaCompletionItem()
item.setLabel(first.simpleName.toString())
item.kind = CompletionItemKind.METHOD
item.completionKind = CompletionItemKind.METHOD
item.detail = printMethodDetail(first)
item.ideSortText = item.label.toString()
item.matchLevel = matchLevel
@@ -220,7 +220,7 @@ abstract class IJavaCompletionProvider(
abortCompletionIfCancelled()
val item = JavaCompletionItem()
item.setLabel(element.simpleName.toString())
item.kind = kind(element)
item.completionKind = kind(element)
item.detail = element.toString()
item.data = data(task, element, 1)
item.ideSortText = item.label.toString()
@@ -250,7 +250,7 @@ abstract class IJavaCompletionProvider(
abortCompletionIfCancelled()
val item = JavaCompletionItem()
item.setLabel(simpleName(className).toString())
item.kind = CompletionItemKind.CLASS
item.completionKind = CompletionItemKind.CLASS
item.detail = packageName(className).toString()
item.ideSortText = item.label.toString()
item.matchLevel = matchLevel
@@ -285,7 +285,7 @@ abstract class IJavaCompletionProvider(
setLabel(simpleName)
this.detail = packageName
this.insertText = simpleName
this.kind = MODULE
this.completionKind = MODULE
this.ideSortText = name
this.matchLevel = matchLevel
}
@@ -170,7 +170,7 @@ class ScopeCompletionProvider(
val item = JavaCompletionItem()
item.setLabel(methodSpec.name)
item.kind = com.itsaky.androidide.lsp.models.CompletionItemKind.METHOD
item.completionKind = com.itsaky.androidide.lsp.models.CompletionItemKind.METHOD
item.detail = method.returnType.toString() + " " + method
item.ideSortText = item.label.toString()
item.insertText = insertText
@@ -1,102 +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.lsp.java.providers.completion
import com.itsaky.androidide.lookup.Lookup
import com.itsaky.androidide.lsp.api.ICompletionCancelChecker
import com.itsaky.androidide.lsp.api.describeSnippet
import com.itsaky.androidide.lsp.java.models.JavaCompletionItem
import com.itsaky.androidide.lsp.models.CompletionItem
import com.itsaky.androidide.lsp.models.CompletionItemKind.SNIPPET
import com.itsaky.androidide.lsp.models.CompletionResult
import com.itsaky.androidide.lsp.models.InsertTextFormat
import com.itsaky.androidide.lsp.models.MatchLevel.CASE_INSENSITIVE_EQUAL
import com.itsaky.androidide.lsp.models.SnippetDescription
import com.itsaky.androidide.progress.ProgressManager.Companion.abortIfCancelled
import com.itsaky.androidide.projects.ProjectManager
import openjdk.source.tree.CompilationUnitTree
import openjdk.source.tree.Tree.Kind.ERRONEOUS
import java.nio.file.Path
import java.nio.file.Paths
/** @author Akash Yadav */
class TopLevelSnippetsProvider {
fun complete(prefix: String, root: CompilationUnitTree, result: CompletionResult) {
abortIfCancelled()
abortCompletionIfCancelled()
val file = Paths.get(root.sourceFile.toUri())
if (!hasTypeDeclaration(root)) {
result.add(classSnippet(prefix, file))
if (root.getPackage() == null) {
val packageSnippet = packageSnippet(prefix, file)
if (packageSnippet != null) {
result.add(packageSnippet)
}
}
}
}
private fun hasTypeDeclaration(root: CompilationUnitTree): Boolean {
for (tree in root.typeDecls) {
if (tree.kind != ERRONEOUS) {
return true
}
}
return false
}
private fun classSnippet(prefix: String, file: Path): CompletionItem {
abortIfCancelled()
abortCompletionIfCancelled()
var name = file.fileName.toString()
name = name.substring(0, name.length - ".java".length)
return snippetItem("class $name", "class $name {\n $0\n}", describeSnippet(prefix))
}
private fun packageSnippet(prefix: String, file: Path): CompletionItem? {
abortIfCancelled()
abortCompletionIfCancelled()
val module = ProjectManager.findModuleForFile(file) ?: return null
val name = module.suggestPackageName(file)
return snippetItem("package $name", "package $name;\n\n", describeSnippet(prefix))
}
private fun snippetItem(
label: String,
snippet: String,
description: SnippetDescription
): CompletionItem {
abortIfCancelled()
abortCompletionIfCancelled()
val item = JavaCompletionItem()
item.setLabel(label)
item.kind = SNIPPET
item.insertText = snippet
item.insertTextFormat = InsertTextFormat.SNIPPET
item.snippetDescription = description
item.ideSortText = label
item.matchLevel = CASE_INSENSITIVE_EQUAL
return item
}
/** Abort the completion if cancelled. */
fun abortCompletionIfCancelled() {
val checker = Lookup.DEFAULT.lookup(ICompletionCancelChecker::class.java)
checker?.abortIfCancelled()
}
}
@@ -121,7 +121,7 @@ open class CompletionItem(
insertTextFormat: InsertTextFormat?,
sortText: String?,
var command: Command?,
var kind: CompletionItemKind,
var completionKind: CompletionItemKind,
var matchLevel: MatchLevel,
var additionalTextEdits: List<TextEdit>?,
var data: ICompletionData?,
@@ -227,7 +227,7 @@ open class CompletionItem(
if (label != other.label) return false
if (detail != other.detail) return false
if (command != other.command) return false
if (kind != other.kind) return false
if (completionKind != other.completionKind) return false
if (matchLevel != other.matchLevel) return false
if (additionalTextEdits != other.additionalTextEdits) return false
if (data != other.data) return false
@@ -245,7 +245,7 @@ open class CompletionItem(
var result = label.hashCode()
result = 31 * result + detail.hashCode()
result = 31 * result + (command?.hashCode() ?: 0)
result = 31 * result + kind.hashCode()
result = 31 * result + completionKind.hashCode()
result = 31 * result + matchLevel.hashCode()
result = 31 * result + (additionalTextEdits?.hashCode() ?: 0)
result = 31 * result + (data?.hashCode() ?: 0)
@@ -259,7 +259,7 @@ open class CompletionItem(
}
override fun toString(): String {
return "CompletionItem(label='$label', detail='$detail', command=$command, kind=$kind, matchLevel=$matchLevel, additionalTextEdits=$additionalTextEdits, data=$data, sortText=$ideSortText, insertText='$insertText', insertTextFormat=$insertTextFormat, editHandler=$editHandler, additionalEditHandler=$additionalEditHandler, overrideTypeText=$overrideTypeText)"
return "CompletionItem(label='$label', detail='$detail', command=$command, kind=$completionKind, matchLevel=$matchLevel, additionalTextEdits=$additionalTextEdits, data=$data, sortText=$ideSortText, insertText='$insertText', insertTextFormat=$insertTextFormat, editHandler=$editHandler, additionalEditHandler=$additionalEditHandler, overrideTypeText=$overrideTypeText)"
}
}
@@ -149,7 +149,7 @@ abstract class IXmlCompletionProvider(private val provider: ICompletionProvider)
this.insertTextFormat = PLAIN_TEXT
this.editHandler = TagEditHandler()
this.matchLevel = matchLevel
this.kind = CLASS
this.completionKind = CLASS
this.data = ClassCompletionData(className = qualifiedName)
}
@@ -176,7 +176,7 @@ abstract class IXmlCompletionProvider(private val provider: ICompletionProvider)
val title = "$prefix${attr.name.entry!!}"
this.label = title
this.kind = FIELD
this.completionKind = FIELD
this.detail = "From package '$resPkg'"
this.insertText = "$title=\"$0\""
this.insertTextFormat = SNIPPET
@@ -214,7 +214,7 @@ abstract class IXmlCompletionProvider(private val provider: ICompletionProvider)
return CompletionItem().apply {
this.label = text
this.detail = "From package '$pck'"
this.kind = VALUE
this.completionKind = VALUE
this.overrideTypeText = type.uppercase()
this.ideSortText = if (pck == ResourceTableRegistry.PCK_ANDROID) "zzz$text" else text
this.insertText = text
@@ -239,7 +239,7 @@ abstract class IXmlCompletionProvider(private val provider: ICompletionProvider)
return CompletionItem().apply {
this.label = name
this.detail = if (pck.isBlank()) "" else "From package '$pck'"
this.kind = VALUE
this.completionKind = VALUE
this.ideSortText = "000$name"
this.insertText = name
this.editHandler = QualifiedValueEditHandler()
@@ -95,7 +95,7 @@ class ManifestAttrValueCompletionProvider(provider: ICompletionProvider) :
item.overrideTypeText = "Permission"
// Show API information
item.kind = FIELD
item.completionKind = FIELD
item.data =
FieldCompletionData(
memberName = value.name,