diff --git a/editor/src/main/java/com/itsaky/androidide/editor/adapters/CompletionListAdapter.kt b/editor/src/main/java/com/itsaky/androidide/editor/adapters/CompletionListAdapter.kt index a1780ac3b..497a426dc 100755 --- a/editor/src/main/java/com/itsaky/androidide/editor/adapters/CompletionListAdapter.kt +++ b/editor/src/main/java/com/itsaky/androidide/editor/adapters/CompletionListAdapter.kt @@ -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 || diff --git a/editor/src/main/java/com/itsaky/androidide/editor/language/groovy/GroovyAutoComplete.java b/editor/src/main/java/com/itsaky/androidide/editor/language/groovy/GroovyAutoComplete.java index 291f175bd..d117aa23e 100755 --- a/editor/src/main/java/com/itsaky/androidide/editor/language/groovy/GroovyAutoComplete.java +++ b/editor/src/main/java/com/itsaky/androidide/editor/language/groovy/GroovyAutoComplete.java @@ -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; } } diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/CompletionProvider.java b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/CompletionProvider.java index 9f373cfc5..971932d66 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/CompletionProvider.java +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/CompletionProvider.java @@ -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 nextCacheConsumer) { + public synchronized CompletionProvider reset(JavaCompilerService compiler, + IServerSettings settings, CachedCompletion cache, + Consumer 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 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(); diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/completion/IJavaCompletionProvider.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/completion/IJavaCompletionProvider.kt index ca993648c..a95984766 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/completion/IJavaCompletionProvider.kt +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/completion/IJavaCompletionProvider.kt @@ -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 } diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/completion/ScopeCompletionProvider.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/completion/ScopeCompletionProvider.kt index 16e0be6c3..268f1f3b1 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/completion/ScopeCompletionProvider.kt +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/completion/ScopeCompletionProvider.kt @@ -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 diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/completion/TopLevelSnippetsProvider.kt b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/completion/TopLevelSnippetsProvider.kt deleted file mode 100644 index 99788f55f..000000000 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/completion/TopLevelSnippetsProvider.kt +++ /dev/null @@ -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 . - */ - -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() - } -} diff --git a/lsp/models/src/main/java/com/itsaky/androidide/lsp/models/Completions.kt b/lsp/models/src/main/java/com/itsaky/androidide/lsp/models/Completions.kt index 7975752b8..2e91e4811 100644 --- a/lsp/models/src/main/java/com/itsaky/androidide/lsp/models/Completions.kt +++ b/lsp/models/src/main/java/com/itsaky/androidide/lsp/models/Completions.kt @@ -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?, 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)" } } diff --git a/lsp/xml/src/main/java/com/itsaky/androidide/lsp/xml/providers/completion/IXmlCompletionProvider.kt b/lsp/xml/src/main/java/com/itsaky/androidide/lsp/xml/providers/completion/IXmlCompletionProvider.kt index c95f874ca..8ab03bb3f 100644 --- a/lsp/xml/src/main/java/com/itsaky/androidide/lsp/xml/providers/completion/IXmlCompletionProvider.kt +++ b/lsp/xml/src/main/java/com/itsaky/androidide/lsp/xml/providers/completion/IXmlCompletionProvider.kt @@ -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() diff --git a/lsp/xml/src/main/java/com/itsaky/androidide/lsp/xml/providers/completion/manifest/ManifestAttrValueCompletionProvider.kt b/lsp/xml/src/main/java/com/itsaky/androidide/lsp/xml/providers/completion/manifest/ManifestAttrValueCompletionProvider.kt index 04fad07b8..e84c95eb6 100644 --- a/lsp/xml/src/main/java/com/itsaky/androidide/lsp/xml/providers/completion/manifest/ManifestAttrValueCompletionProvider.kt +++ b/lsp/xml/src/main/java/com/itsaky/androidide/lsp/xml/providers/completion/manifest/ManifestAttrValueCompletionProvider.kt @@ -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,