diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/completion/LSPCompletionContributor.java b/src/main/java/com/redhat/devtools/lsp4ij/features/completion/LSPCompletionContributor.java index 34cb0f0d9..4787fd3b2 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/completion/LSPCompletionContributor.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/completion/LSPCompletionContributor.java @@ -10,17 +10,17 @@ ******************************************************************************/ package com.redhat.devtools.lsp4ij.features.completion; -import com.intellij.codeInsight.completion.CompletionContributor; -import com.intellij.codeInsight.completion.CompletionParameters; -import com.intellij.codeInsight.completion.CompletionResultSet; +import com.intellij.codeInsight.completion.*; import com.intellij.codeInsight.lookup.*; import com.intellij.codeInsight.lookup.impl.LookupImpl; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.util.UserDataHolder; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; +import com.intellij.util.containers.ContainerUtil; import com.redhat.devtools.lsp4ij.LSPFileSupport; import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.lsp4ij.LanguageServerItem; @@ -35,9 +35,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.*; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -125,6 +123,7 @@ private void addCompletionItems(@NotNull CompletionParameters parameters, items.sort(completionProposalComparator); int size = items.size(); + Set addedLookupStrings = new HashSet<>(); var completionFeature = languageServer.getClientFeatures().getCompletionFeature(); LSPCompletionFeature.LSPCompletionContext context = new LSPCompletionFeature.LSPCompletionContext(parameters, languageServer); // Items now sorted by priority, low index == high priority @@ -137,6 +136,51 @@ private void addCompletionItems(@NotNull CompletionParameters parameters, LookupElement lookupItem = completionFeature.createLookupElement(item, context); if (lookupItem != null) { completionFeature.addLookupItem(context, completionPrefix, result, lookupItem, size - i, item); + ContainerUtil.addIfNotNull(addedLookupStrings, lookupItem.getLookupString()); + } + } + + // If completions were added from LSP and this is auto-completion or the explicit completion trigger was typed + // only once, add completions from other all other contributors except for the word completion contributor. + // Note that the word completion contributor only really kicks in when LSP completions are added in the context + // of a TextMate file (see TextMateCompletionContributor), but that's going to be very common for LSP usage + // since it's often adding (pseudo-)language support when not already present in the host JetBrains IDE. + if ((size > 0) && (parameters.getInvocationCount() < 2)) { + // Don't allow automatic execution of the remaining completion contributors; we'll execute them ourselves below + result.stopHere(); + + // Try to disable word completion by temporarily setting FORBID_WORD_COMPLETION=true on the completion process. + // Note that this will not work in some older IDE versions where WordCompletionContributor.addWordCompletionVariants() + // did not yet check the value of FORBID_WORD_COMPLETION + CompletionProcess completionProcess = parameters.getProcess(); + UserDataHolder userDataHolder = completionProcess instanceof UserDataHolder ? (UserDataHolder) completionProcess : null; + boolean originalForbidWordCompletion = false; + if (userDataHolder != null) { + originalForbidWordCompletion = userDataHolder.getUserData(BaseCompletionService.FORBID_WORD_COMPLETION) == Boolean.TRUE; + userDataHolder.putUserData(BaseCompletionService.FORBID_WORD_COMPLETION, true); + } + try { + if (userDataHolder != null) { + // If we disabled word completion, just run all remaining contributors + result.runRemainingContributors(parameters, true); + } else { + // If not, run all remaining contributors and only add results with distinct lookup strings + result.runRemainingContributors(parameters, completionResult -> { + LookupElement lookupElement = completionResult.getLookupElement(); + if (lookupElement != null) { + String lookupString = lookupElement.getLookupString(); + if (!addedLookupStrings.contains(lookupString)) { + result.consume(lookupElement); + addedLookupStrings.add(lookupString); + } + } + }); + } + } finally { + // If appropriate, re-enable word completion + if (userDataHolder != null) { + userDataHolder.putUserData(BaseCompletionService.FORBID_WORD_COMPLETION, originalForbidWordCompletion); + } } } } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index d78d4e3bf..2c4ac41ac 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -246,7 +246,7 @@ id="LSPCompletionContributor" language="any" implementationClass="com.redhat.devtools.lsp4ij.features.completion.LSPCompletionContributor" - order="first"/> + order="first, before wordCompletion"/>