IJPL-158286 LookupCellRenderer - get rid of SingleAlarm in favour of flow

GitOrigin-RevId: 8cffbdd2089d02d04bb012cb3dd579406acb2eaf
This commit is contained in:
Vladimir Krivosheev
2024-07-13 17:25:39 +00:00
committed by intellij-monorepo-bot
parent 44dda124a9
commit 6372bfea0a
7 changed files with 60 additions and 29 deletions
@@ -7902,7 +7902,6 @@ f:com.intellij.codeInsight.lookup.impl.LookupCellRenderer
- f:refreshUi():V
- f:scheduleUpdateLookupWidthFromVisibleItems():V
- f:updateItemPresentation(com.intellij.codeInsight.lookup.LookupElement):V
- f:updateLookupWidthFromVisibleItems():V
f:com.intellij.codeInsight.lookup.impl.LookupCellRenderer$Companion
- f:augmentIcon(com.intellij.openapi.editor.Editor,javax.swing.Icon,javax.swing.Icon):javax.swing.Icon
- f:bodyInsets():java.awt.Insets
@@ -520,7 +520,7 @@ public final class CompletionProgressIndicator extends ProgressIndicatorBase imp
private void addItemToLookup(CompletionResult item) {
Ref<Boolean> stopRef = new Ref<>(Boolean.FALSE);
DumbModeAccessType.RELIABLE_DATA_ONLY.ignoreDumbMode(() -> {
stopRef.set(!lookup.addItem(item.getLookupElement(), item.getPrefixMatcher()));
stopRef.set(lookup.isLookupDisposed() || !lookup.addItem(item.getLookupElement(), item.getPrefixMatcher()));
});
if (stopRef.get()) {
@@ -7,9 +7,7 @@ import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementPresentation
import com.intellij.codeInsight.lookup.LookupElementRenderer
import com.intellij.openapi.application.readAction
import com.intellij.openapi.components.ComponentManagerEx
import com.intellij.openapi.util.Key
import com.intellij.util.cancelOnDispose
import com.intellij.util.indexing.DumbModeAccessType
import kotlinx.coroutines.*
@@ -38,8 +36,11 @@ internal class AsyncRendering(private val lookup: LookupImpl) {
synchronized(LAST_COMPUTATION) {
cancelRendering(element)
@Suppress("UsagesOfObsoleteApi")
val job = (lookup.project as ComponentManagerEx).getCoroutineScope().launch(limitedDispatcher) {
if (lookup.isLookupDisposed) {
return
}
val job = lookup.coroutineScope.launch(limitedDispatcher) {
val job = coroutineContext.job
readAction {
if (element.isValid) {
@@ -49,7 +50,7 @@ internal class AsyncRendering(private val lookup: LookupImpl) {
synchronized(LAST_COMPUTATION) {
element.replace(LAST_COMPUTATION, job, null)
}
}.also { it.cancelOnDispose(lookup) }
}
element.putUserData(LAST_COMPUTATION, job)
}
}
@@ -9,7 +9,9 @@ import com.intellij.codeInsight.lookup.impl.LookupCellRenderer.Companion.bodyIns
import com.intellij.codeInsight.lookup.impl.LookupCellRenderer.Companion.getGrayedForeground
import com.intellij.codeInsight.lookup.impl.LookupCellRenderer.IconDecorator
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.EDT
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.application.asContextElement
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.colors.CodeInsightColors
import com.intellij.openapi.editor.colors.EditorColorsManager
@@ -23,6 +25,7 @@ import com.intellij.openapi.util.Key
import com.intellij.openapi.util.NlsSafe
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.util.registry.Registry
import com.intellij.platform.util.coroutines.flow.throttle
import com.intellij.psi.codeStyle.NameUtil
import com.intellij.ui.*
import com.intellij.ui.ExperimentalUI.Companion.isNewUI
@@ -34,7 +37,6 @@ import com.intellij.ui.scale.JBUIScale.scale
import com.intellij.ui.speedSearch.SpeedSearchUtil
import com.intellij.util.IconUtil.cropIcon
import com.intellij.util.ObjectUtils
import com.intellij.util.SingleAlarm
import com.intellij.util.containers.ContainerUtil
import com.intellij.util.containers.FList
import com.intellij.util.ui.EmptyIcon
@@ -43,6 +45,11 @@ import com.intellij.util.ui.JBUI
import com.intellij.util.ui.UIUtil
import com.intellij.util.ui.accessibility.AccessibleContextUtil
import it.unimi.dsi.fastutil.ints.Int2BooleanOpenHashMap
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.Nls
import org.jetbrains.annotations.VisibleForTesting
@@ -75,7 +82,7 @@ class LookupCellRenderer(lookup: LookupImpl, editorComponent: JComponent) : List
var lookupTextWidth: Int = 50
private set
private val widthLock = ObjectUtils.sentinel("lookup width lock")
private val lookupWidthUpdater: Runnable
private val lookupWidthUpdater: () -> Unit
private val shrinkLookup: Boolean
private val asyncRendering: AsyncRendering
@@ -111,23 +118,30 @@ class LookupCellRenderer(lookup: LookupImpl, editorComponent: JComponent) : List
panel.add(tailComponent, BorderLayout.CENTER)
panel.add(typeLabel, BorderLayout.EAST)
normalMetrics = this.lookup.topLevelEditor.component.getFontMetrics(normalFont)
boldMetrics = this.lookup.topLevelEditor.component.getFontMetrics(boldFont)
asyncRendering = AsyncRendering(this.lookup)
normalMetrics = lookup.topLevelEditor.component.getFontMetrics(normalFont)
boldMetrics = lookup.topLevelEditor.component.getFontMetrics(boldFont)
asyncRendering = AsyncRendering(lookup)
if (ApplicationManager.getApplication().isUnitTestMode) {
// avoid delay in unit tests
lookupWidthUpdater = Runnable { ApplicationManager.getApplication().invokeLater { this.updateLookupWidthFromVisibleItems() } }
lookupWidthUpdater = {
ApplicationManager.getApplication().invokeLater({ updateLookupWidthFromVisibleItems() }, lookup.project.disposed)
}
}
else {
val alarm =
SingleAlarm({ this.updateLookupWidthFromVisibleItems() }, 50, lookup, ModalityState.stateForComponent(editorComponent))
lookupWidthUpdater = Runnable {
synchronized(alarm) {
if (!alarm.isDisposed) {
alarm.request()
val lookupWidthUpdateRequests = MutableSharedFlow<Unit>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
val coroutineContext = Dispatchers.EDT + ModalityState.stateForComponent(editorComponent).asContextElement()
lookup.coroutineScope.launch {
lookupWidthUpdateRequests
.throttle(50)
.collect {
withContext(coroutineContext) {
updateLookupWidthFromVisibleItems()
}
}
}
}
lookupWidthUpdater = {
check(lookupWidthUpdateRequests.tryEmit(Unit))
}
}
@@ -478,7 +492,7 @@ class LookupCellRenderer(lookup: LookupImpl, editorComponent: JComponent) : List
/**
* Update lookup width due to visible in lookup items
*/
fun updateLookupWidthFromVisibleItems() {
private fun updateLookupWidthFromVisibleItems() {
val visibleItems = lookup.visibleItems
var maxWidth = if (shrinkLookup) 0 else lookupTextWidth
@@ -505,7 +519,7 @@ class LookupCellRenderer(lookup: LookupImpl, editorComponent: JComponent) : List
}
fun scheduleUpdateLookupWidthFromVisibleItems() {
lookupWidthUpdater.run()
lookupWidthUpdater()
}
fun itemAdded(element: LookupElement, fastPresentation: LookupElementPresentation) {
@@ -58,6 +58,9 @@ import com.intellij.util.ui.accessibility.AccessibleContextUtil;
import com.intellij.util.ui.accessibility.ScreenReader;
import com.intellij.util.ui.update.Activatable;
import com.intellij.util.ui.update.UiNotifyConnector;
import kotlinx.coroutines.CoroutineScope;
import kotlinx.coroutines.CoroutineScopeKt;
import kotlinx.coroutines.Dispatchers;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
@@ -77,6 +80,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import static com.intellij.codeInsight.lookup.LookupElement.LOOKUP_ELEMENT_SHOW_TIMESTAMP_MILLIS;
import static kotlinx.coroutines.SupervisorKt.SupervisorJob;
public class LookupImpl extends LightweightHint implements LookupEx, Disposable, LookupElementListPresenter {
private static final Logger LOG = Logger.getInstance(LookupImpl.class);
@@ -116,6 +120,8 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable,
private final EmptyLookupItem myDummyItem = new EmptyLookupItem(CommonBundle.message("tree.node.loading"), true);
private boolean myFirstElementAdded = false;
final CoroutineScope coroutineScope = CoroutineScopeKt.CoroutineScope(SupervisorJob(null).plus(Dispatchers.getDefault()));
public LookupImpl(Project project, Editor editor, @NotNull LookupArranger arranger) {
super(new JPanel(new BorderLayout()));
setForceShowAsPopup(true);
@@ -346,7 +352,9 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable,
}
public void fireBeforeAppendPrefix(char c) {
myPrefixChangeListeners.forEach((listener -> listener.beforeAppend(c)));
for (PrefixChangeListener listener : myPrefixChangeListeners) {
listener.beforeAppend(c);
}
}
public void appendPrefix(char c) {
@@ -362,7 +370,9 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable,
refreshUi(false, true);
ensureSelectionVisible(true);
}
myPrefixChangeListeners.forEach((listener -> listener.afterAppend(c)));
for (PrefixChangeListener listener : myPrefixChangeListeners) {
listener.afterAppend(c);
}
}
public void setStartCompletionWhenNothingMatches(boolean startCompletionWhenNothingMatches) {
@@ -419,7 +429,9 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable,
ensureSelectionVisible(true);
}
myPrefixChangeListeners.forEach((listener -> listener.afterTruncate()));
for (PrefixChangeListener listener : myPrefixChangeListeners) {
listener.afterTruncate();
}
}
void moveToCaretPosition() {
@@ -449,7 +461,9 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable,
if (!myFirstElementAdded && !items.isEmpty()) {
myFirstElementAdded = true;
myListeners.forEach(LookupListener::firstElementShown);
for (LookupListener listener : myListeners) {
listener.firstElementShown();
}
}
myOffsets.checkMinPrefixLengthChanges(items, this);
@@ -461,7 +475,9 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable,
model.removeAll();
if (!finalItems.isEmpty()) {
Long currentTimeMillis = System.currentTimeMillis();
finalItems.forEach(item -> item.putUserDataIfAbsent(LOOKUP_ELEMENT_SHOW_TIMESTAMP_MILLIS, currentTimeMillis));
for (LookupElement item : finalItems) {
item.putUserDataIfAbsent(LOOKUP_ELEMENT_SHOW_TIMESTAMP_MILLIS, currentTimeMillis);
}
model.add(finalItems);
addDummyItems(myDummyItemCount.get());
}
@@ -1195,6 +1211,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable,
@Override
public void dispose() {
CoroutineScopeKt.cancel(coroutineScope, null);
ThreadingAssertions.assertEventDispatchThread();
assert myHidden;
@@ -60,7 +60,7 @@ internal class MacScrollBarAnimationBehavior(
) : DefaultScrollBarAnimationBehavior(trackAnimator, thumbAnimator) {
private var isTrackHovered: Boolean = false
@Suppress("SSBasedInspection")
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default + CoroutineName("IdeRootPane"))
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
private val hideThumbRequests = MutableSharedFlow<Boolean>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
init {
@@ -60,7 +60,7 @@ class ProgressDialog(
private var popup: DialogWrapper? = null
@Suppress("SSBasedInspection")
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default + CoroutineName("IdeRootPane"))
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
init {
ui.progressBar.isIndeterminate = progressWindow.isIndeterminate