diff --git a/platform/analysis-api/src/com/intellij/codeInsight/lookup/LookupArranger.java b/platform/analysis-api/src/com/intellij/codeInsight/lookup/LookupArranger.java index 7132bea37cf6..57ce4d5e23cf 100644 --- a/platform/analysis-api/src/com/intellij/codeInsight/lookup/LookupArranger.java +++ b/platform/analysis-api/src/com/intellij/codeInsight/lookup/LookupArranger.java @@ -24,6 +24,9 @@ import java.util.function.Predicate; * If accessed from multiple threads, it needs to take care of proper synchronization itself. */ public abstract class LookupArranger implements WeighingContext { + //not static! + private final Key ADDING_CALLBACK = Key.create("on_adding_callback"); + protected final List myItems = new ArrayList<>(); private final List myMatchingItems = new ArrayList<>(); private final List myExactPrefixItems = new ArrayList<>(); @@ -36,6 +39,7 @@ public abstract class LookupArranger implements WeighingContext { public void addElement(@NotNull LookupElement item, @NotNull LookupElementPresentation presentation) { myItems.add(item); updateCache(item); + runAddingCallback(item); } private void updateCache(@NotNull LookupElement item) { @@ -240,6 +244,23 @@ public abstract class LookupArranger implements WeighingContext { return myMatchingItems; } + /** + * Invokes the specified callback when the specified item is added to this arranger. + * Is useful when using {@link com.intellij.codeInsight.completion.BaseCompletionLookupArranger} in batch mode, see {@link com.intellij.codeInsight.completion.BaseCompletionLookupArranger#batchUpdate}. + * In batch mode, items are added with a delay, so it's useful to invoke this method to get notified when the item is actually added. + */ + @ApiStatus.Internal + public void invokeWhenLookupElementAdded(@NotNull LookupElement item, @NotNull Runnable callback) { + item.putUserData(ADDING_CALLBACK, callback); + } + + private void runAddingCallback(@NotNull LookupElement item) { + Runnable callback = item.getUserData(ADDING_CALLBACK); + if (callback == null) return; + item.putUserData(ADDING_CALLBACK, null); + callback.run(); + } + /** * @param items the items to give relevance weight for * @param hideSingleValued whether criteria that gave same values for all items should be skipped diff --git a/platform/analysis-impl/src/com/intellij/codeInsight/completion/BaseCompletionLookupArranger.java b/platform/analysis-impl/src/com/intellij/codeInsight/completion/BaseCompletionLookupArranger.java index 3cb0a4521ce8..22ad594fa4fc 100644 --- a/platform/analysis-impl/src/com/intellij/codeInsight/completion/BaseCompletionLookupArranger.java +++ b/platform/analysis-impl/src/com/intellij/codeInsight/completion/BaseCompletionLookupArranger.java @@ -307,7 +307,7 @@ public class BaseCompletionLookupArranger extends LookupArranger implements Comp private void printTestWarning() { System.err.println("Your test might miss some lookup items, because only " + (myLimit / 2) + " most relevant items are guaranteed to be shown in the lookup. You can:"); System.err.println("1. Make the prefix used for completion longer, so that there are less suggestions."); - System.err.println("2. Increase 'ide.completion.variant.limit' (using RegistryValue#setValue with a test root disposable)."); + System.err.println("2. Increase 'ide.completion.variant.limit' (using `Registry.get(\"ide.completion.variant.limit\").setValue(10000, getTestRootDisposable())`)."); System.err.println("3. Ignore this warning."); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionThreading.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionThreading.kt index ee1e051c0983..4f494c7e2492 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionThreading.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionThreading.kt @@ -175,32 +175,46 @@ private class AddItemJob( ) : Runnable { override fun run() { try { + val batch = mutableListOf() while (true) { indicator.checkCanceled() - when (val event = workingQueue.poll(30, TimeUnit.MILLISECONDS)) { - Stop -> { - tryReadOrCancel(indicator) { - indicator.addDelayedMiddleMatches() - } - return - } - is AddItem -> { - tryReadOrCancel(indicator) { - indicator.addItem(event.result) - } - } - is AddBatch -> { - tryReadOrCancel(indicator) { - indicator.withSingleUpdate { - for (result in event.results) { - indicator.addItem(result) + workingQueue.drainTo(batch) + if (batch.isEmpty()) { + // try awaiting the next event + val next = workingQueue.poll(30, TimeUnit.MILLISECONDS) ?: continue + batch.add(next) + } + + var stop = false + tryReadOrCancel(indicator) { + indicator.withSingleUpdate { + for (event in batch) { + indicator.checkCanceled() + when (event) { + is AddItem -> { + indicator.addItem(event.result) + } + is AddBatch -> { + for (result in event.results) { + indicator.addItem(result) + } + } + Stop -> { + indicator.addDelayedMiddleMatches() + stop = true + break } } } } - null -> { /* keep waiting for the value */ } } + + if (stop) { + return + } + + batch.clear() } } catch (e: InterruptedException) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java index faaf3c1cd9f8..827e7353ea06 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java @@ -414,9 +414,9 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable, cellRenderer.itemAdded(item, presentation); LookupArranger arranger = myArranger; + arranger.invokeWhenLookupElementAdded(item, () -> cellRenderer.itemAddedToArranger(item)); arranger.registerMatcher(item, matcher); arranger.addElement(item, presentation); - cellRenderer.itemAddedToArranger(item); return true; } diff --git a/plugins/editorconfig/backend/test/language/codeinsight/EditorConfigCompletionTest.kt b/plugins/editorconfig/backend/test/language/codeinsight/EditorConfigCompletionTest.kt index 96a1a0b1e29f..012c7625bc7b 100644 --- a/plugins/editorconfig/backend/test/language/codeinsight/EditorConfigCompletionTest.kt +++ b/plugins/editorconfig/backend/test/language/codeinsight/EditorConfigCompletionTest.kt @@ -4,6 +4,7 @@ package org.editorconfig.language.codeinsight import com.intellij.codeInsight.lookup.LookupElement import com.intellij.openapi.application.ex.PathManagerEx import com.intellij.openapi.util.registry.Registry +import com.intellij.openapi.util.registry.RegistryValue import com.intellij.testFramework.fixtures.BasePlatformTestCase import org.editorconfig.EditorConfigRegistry import org.editorconfig.language.assertIterableEquals @@ -61,6 +62,7 @@ class EditorConfigCompletionTest : BasePlatformTestCase() { fun doTest(vararg required: String) = with(myFixture) { val name = getTestName(true) configureByFile("$name/.editorconfig") + Registry.get("ide.completion.variant.limit").setValue(10000, testRootDisposable) assertTrue(required.all(completeBasic().map(LookupElement::getLookupString)::contains)) }