Revert "IDEA-CR-56862: Calculate method parameter info in a bg modal task"

This reverts commit 4400b66a

GitOrigin-RevId: 505adf3116bc0ce788cadb5058268c53f0eecffc
This commit is contained in:
Vladimir Dolzhenko
2020-01-30 07:34:58 +00:00
committed by intellij-monorepo-bot
parent c40db04b6c
commit fdb6714a2b
7 changed files with 177 additions and 370 deletions
@@ -11,7 +11,6 @@ import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementPresentation;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.application.impl.NonBlockingReadActionImpl;
import com.intellij.openapi.editor.Editor;
import com.intellij.testFramework.fixtures.EditorHintFixture;
import com.intellij.util.ui.UIUtil;
@@ -46,15 +45,7 @@ public abstract class AbstractParameterInfoTestCase extends LightFixtureCompleti
protected void showParameterInfo() {
myFixture.performEditorAction(IdeActions.ACTION_EDITOR_SHOW_PARAMETER_INFO);
waitForParameterInfo();
}
public static void waitForParameterInfo() {
// effective there is a chain of 3 nonBlockingRead actions
for (int i = 0; i < 3; i++) {
UIUtil.dispatchAllInvocationEvents();
NonBlockingReadActionImpl.waitForAsyncTaskCompletion();
}
UIUtil.dispatchAllInvocationEvents();
}
protected void checkHintContents(String hintText) {
@@ -68,7 +59,6 @@ public abstract class AbstractParameterInfoTestCase extends LightFixtureCompleti
public void complete(String partOfItemText) {
LookupElement[] elements = myFixture.completeBasic();
selectItem(elements, partOfItemText);
waitForParameterInfo();
}
public void completeSmart() {
@@ -78,7 +68,6 @@ public abstract class AbstractParameterInfoTestCase extends LightFixtureCompleti
public void completeSmart(String partOfItemText) {
LookupElement[] lookupElements = myFixture.complete(CompletionType.SMART);
selectItem(lookupElements, partOfItemText);
waitForParameterInfo();
}
private void selectItem(LookupElement[] elements, String partOfItemText) {
@@ -92,7 +81,6 @@ public abstract class AbstractParameterInfoTestCase extends LightFixtureCompleti
private void waitForParameterInfoUpdate() throws TimeoutException {
ParameterInfoController.waitForDelayedActions(getEditor(), 1, TimeUnit.MINUTES);
waitForParameterInfo();
}
public static void waitTillAnimationCompletes(Editor editor) {
@@ -118,6 +106,5 @@ public abstract class AbstractParameterInfoTestCase extends LightFixtureCompleti
myFixture.doHighlighting();
waitTillAnimationCompletes(getEditor());
waitForAutoPopup();
waitForParameterInfo();
}
}
@@ -83,7 +83,8 @@ public class ParameterInfoTest extends AbstractParameterInfoTestCase {
" }\n" +
"}\n");
showParameterInfo();
myFixture.performEditorAction(IdeActions.ACTION_EDITOR_SHOW_PARAMETER_INFO);
UIUtil.dispatchAllInvocationEvents();
assertEquals("<html><b>Supplier&lt;Integer&gt; extractKey</b>, Function&lt;String, Integer&gt; right</html>", hintFixture.getCurrentHintText());
}
@@ -132,7 +133,8 @@ public class ParameterInfoTest extends AbstractParameterInfoTestCase {
" super(<caret>\"a\", 1);\n" +
" }\n" +
" }");
showParameterInfo();
myFixture.performEditorAction(IdeActions.ACTION_EDITOR_SHOW_PARAMETER_INFO);
UIUtil.dispatchAllInvocationEvents();
assertEquals("<html><b>String s</b>, int... p</html>", hintFixture.getCurrentHintText());
}
@@ -147,7 +149,8 @@ public class ParameterInfoTest extends AbstractParameterInfoTestCase {
" String[] a = foo(args, args.len<caret>gth);\n" +
" }\n" +
"}");
showParameterInfo();
myFixture.performEditorAction(IdeActions.ACTION_EDITOR_SHOW_PARAMETER_INFO);
UIUtil.dispatchAllInvocationEvents();
assertEquals("<html>String[] args, <b>int l</b></html>", hintFixture.getCurrentHintText());
}
@@ -1699,7 +1699,6 @@ public class CompletionHintsTest extends AbstractParameterInfoTestCase {
}
private void checkResultWithInlays(String text) {
waitForParameterInfo();
myFixture.checkResultWithInlays(text);
}
@@ -1725,12 +1724,10 @@ public class CompletionHintsTest extends AbstractParameterInfoTestCase {
private void methodOverloadUp() {
myFixture.performEditorAction(IdeActions.ACTION_METHOD_OVERLOAD_SWITCH_UP);
waitForParameterInfo();
}
private void methodOverloadDown() {
myFixture.performEditorAction(IdeActions.ACTION_METHOD_OVERLOAD_SWITCH_DOWN);
waitForParameterInfo();
}
private void home() {
@@ -3,26 +3,24 @@ package com.intellij.codeInsight.hint;
import com.intellij.openapi.editor.event.VisibleAreaEvent;
import com.intellij.openapi.editor.event.VisibleAreaListener;
import com.intellij.openapi.progress.ProgressIndicator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.concurrency.CancellablePromise;
import java.awt.*;
import java.util.concurrent.atomic.AtomicReference;
class CancelProgressOnScrolling implements VisibleAreaListener {
private final AtomicReference<CancellablePromise<?>> myCancellablePromiseRef;
private final ProgressIndicator myProgressIndicator;
CancelProgressOnScrolling(AtomicReference<CancellablePromise<?>> cancellablePromiseRef) {
myCancellablePromiseRef = cancellablePromiseRef;
CancelProgressOnScrolling(ProgressIndicator indicator) {
myProgressIndicator = indicator;
}
@Override
public void visibleAreaChanged(@NotNull VisibleAreaEvent e) {
Rectangle oldRect = e.getOldRectangle();
Rectangle newRect = e.getNewRectangle();
CancellablePromise<?> promise = myCancellablePromiseRef.get();
if (oldRect != null && (oldRect.x != newRect.x || oldRect.y != newRect.y) && promise != null) {
promise.cancel();
if (oldRect != null && (oldRect.x != newRect.x || oldRect.y != newRect.y)) {
myProgressIndicator.cancel();
}
}
}
@@ -21,15 +21,18 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.event.*;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.progress.util.ProgressWindow;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.LoadingDecorator;
import com.intellij.openapi.ui.popup.Balloon.Position;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
@@ -39,16 +42,12 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.ui.HintHint;
import com.intellij.ui.HintListener;
import com.intellij.ui.LightweightHint;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBLoadingPanel;
import com.intellij.ui.components.panels.NonOpaquePanel;
import com.intellij.util.Alarm;
import com.intellij.util.Consumer;
import com.intellij.util.concurrency.AppExecutorUtil;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.util.ui.AsyncProcessIcon;
import com.intellij.util.ui.EmptyIcon;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
@@ -60,25 +59,18 @@ import java.awt.*;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.LockSupport;
import java.util.function.Consumer;
import static com.intellij.codeInsight.hint.ParameterInfoTaskRunnerUtil.runTask;
public class ParameterInfoController extends UserDataHolderBase implements Disposable {
private static final Logger LOG = Logger.getInstance(ParameterInfoController.class);
private static final String WHITESPACE = " \t";
private static final String LOADING_TAG = "loading";
private static final String COMPONENT_TAG = "component";
private final Project myProject;
@NotNull private final Editor myEditor;
private final RangeMarker myLbraceMarker;
private final JBLoadingPanel myLoadingPanel;
private LightweightHint myHint;
private final ParameterInfoComponent myComponent;
private boolean myKeepOnHintHidden;
@@ -103,8 +95,7 @@ public class ParameterInfoController extends UserDataHolderBase implements Dispo
for (int i = 0; i < allControllers.size(); ++i) {
ParameterInfoController controller = allControllers.get(i);
int lbraceOffset = controller.myLbraceMarker.getStartOffset();
if (lbraceOffset == offset) {
if (controller.myLbraceMarker.getStartOffset() == offset) {
if (controller.myKeepOnHintHidden || controller.myHint.isVisible()) return controller;
Disposer.dispose(controller);
//noinspection AssignmentToForLoopParameter
@@ -151,28 +142,6 @@ public class ParameterInfoController extends UserDataHolderBase implements Dispo
myProvider = new MyBestLocationPointProvider(editor);
myLbraceMarker = editor.getDocument().createRangeMarker(lbraceOffset, lbraceOffset);
myComponent = new ParameterInfoComponent(descriptors, editor, handler, requestFocus, true);
myLoadingPanel = new JBLoadingPanel(null, panel -> new LoadingDecorator(panel, this, ProgressWindow.DEFAULT_PROGRESS_DIALOG_POSTPONE_TIME_MILLIS, false, new AsyncProcessIcon("ShowParameterInfo")){
protected NonOpaquePanel customizeLoadingLayer(JPanel parent, JLabel text, AsyncProcessIcon icon) {
parent.setLayout(new FlowLayout(FlowLayout.LEFT));
final NonOpaquePanel result = new NonOpaquePanel();
result.add(icon);
parent.add(result);
return result;
}
@Override
protected void _startLoading(boolean takeSnapshot) {
super._startLoading(takeSnapshot);
showHintLoading(true);
}
}) {
@Override
public String toString() {
return myComponent.toString();
}
};
myLoadingPanel.add(new JBLabel(EmptyIcon.ICON_18));
myLoadingPanel.add(new JBLabel(CodeInsightBundle.message("parameter.info.progress.title")));
myHint = createHint();
myKeepOnHintHidden = !showHint;
mySingleParameterInfo = !showHint;
@@ -236,8 +205,7 @@ public class ParameterInfoController extends UserDataHolderBase implements Dispo
private LightweightHint createHint() {
JPanel wrapper = new WrapperPanel();
wrapper.add(myLoadingPanel, LOADING_TAG);
wrapper.add(myComponent, COMPONENT_TAG);
wrapper.add(myComponent);
return new LightweightHint(wrapper);
}
@@ -252,37 +220,9 @@ public class ParameterInfoController extends UserDataHolderBase implements Dispo
myEditor.getCaretModel().removeCaretListener(myEditorCaretListener);
}
public boolean showLoading(HintListener hintListener) {
if (myKeepOnHintHidden || myHint.isVisible()) {
myLoadingPanel.startLoading();
myHint.addHintListener(hintListener);
return true;
}
return false;
}
public void hideLoading(HintListener hintListener) {
if (myKeepOnHintHidden || myHint.isVisible()) {
myLoadingPanel.stopLoading();
showHintLoading(false);
}
myHint.removeHintListener(hintListener);
}
private void showHintLoading(boolean showLoading) {
if (myKeepOnHintHidden || myHint.isVisible()) {
JComponent component = myHint.getComponent();
CardLayout layout = (CardLayout)component.getLayout();
layout.show(component, showLoading ? LOADING_TAG : COMPONENT_TAG);
myHint.pack();
}
}
public void showHint(boolean requestFocus, boolean singleParameterInfo) {
if (myHint.isVisible()) {
JComponent myHintComponent = myHint.getComponent();
myHintComponent.removeAll();
myHint.getComponent().remove(myComponent);
hideHint();
myHint = createHint();
}
@@ -425,18 +365,36 @@ public class ParameterInfoController extends UserDataHolderBase implements Dispo
private void executeFindElementForUpdatingParameterInfo(UpdateParameterInfoContext context,
@NotNull Consumer<PsiElement> elementForUpdatingConsumer) {
runTask(myProject,
final Component focusOwner = IdeFocusManager.getInstance(myProject).getFocusOwner();
ProgressManager.getInstance().run(
new Task.Backgroundable(myProject, CodeInsightBundle.message("parameter.info.progress.title"), true) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
assert !ApplicationManager.getApplication().isDispatchThread() :
"Show parameter info on dispatcher thread leads to live lock";
final VisibleAreaListener visibleAreaListener = new CancelProgressOnScrolling(indicator);
myEditor.getScrollingModel().addVisibleAreaListener(visibleAreaListener);
ProgressIndicatorUtils.awaitWithCheckCanceled(
ReadAction
.nonBlocking(() -> {
return myHandler.findElementForUpdatingParameterInfo(context);
}).withDocumentsCommitted(myProject)
.cancelWith(indicator)
.expireWhen(() -> getCurrentOffset() != context.getOffset())
.coalesceBy(this)
.expireWith(this),
elementForUpdatingConsumer,
CodeInsightBundle.message("parameter.info.progress.title"),
myLbraceMarker.getStartOffset(),
myEditor);
.coalesceBy(ParameterInfoController.this)
.expireWith(ParameterInfoController.this)
.finishOnUiThread(ModalityState.defaultModalityState(), elementForUpdating -> {
if (Objects.equals(focusOwner, IdeFocusManager.getInstance(myProject).getFocusOwner())) {
elementForUpdatingConsumer.consume(elementForUpdating);
}
})
.submit(AppExecutorUtil.getAppExecutorService())
.onProcessed(ignore -> myEditor.getScrollingModel().removeVisibleAreaListener(visibleAreaListener)));
}
});
}
private void executeUpdateParameterInfo(PsiElement elementForUpdating,
@@ -448,32 +406,44 @@ public class ParameterInfoController extends UserDataHolderBase implements Dispo
return;
}
runTask(myProject,
ReadAction.nonBlocking(() -> {
try {
myHandler.updateParameterInfo(elementForUpdating, context);
return elementForUpdating;
}
catch (IndexNotReadyException e) {
DumbService.getInstance(myProject)
.showDumbModeNotification(CodeInsightBundle.message("parameter.info.indexing.mode.not.supported"));
}
return null;
})
.withDocumentsCommitted(myProject)
.expireWhen(() -> !myKeepOnHintHidden && !myHint.isVisible() ||
getCurrentOffset() != context.getOffset() ||
!elementForUpdating.isValid())
.expireWith(this),
element -> {
if (element != null && continuation != null) {
final Component focusOwner = IdeFocusManager.getInstance(myProject).getFocusOwner();
ProgressManager.getInstance().run(
new Task.Backgroundable(myProject, CodeInsightBundle.message("parameter.info.progress.title"), true) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
assert !ApplicationManager.getApplication().isDispatchThread() :
"Show parameter info on dispatcher thread leads to live lock";
final VisibleAreaListener visibleAreaListener = new CancelProgressOnScrolling(indicator);
myEditor.getScrollingModel().addVisibleAreaListener(visibleAreaListener);
ProgressIndicatorUtils.awaitWithCheckCanceled(ReadAction
.nonBlocking(() -> {
try {
myHandler.updateParameterInfo(elementForUpdating, context);
return elementForUpdating;
}
catch (IndexNotReadyException e) {
DumbService.getInstance(myProject)
.showDumbModeNotification(CodeInsightBundle.message("parameter.info.indexing.mode.not.supported"));
}
return null;
})
.withDocumentsCommitted(myProject)
.cancelWith(indicator)
.expireWhen(() -> !myKeepOnHintHidden && !myHint.isVisible() && !ApplicationManager.getApplication().isHeadlessEnvironment() || getCurrentOffset() != context.getOffset() || !elementForUpdating.isValid())
.expireWith(ParameterInfoController.this)
.finishOnUiThread(ModalityState.defaultModalityState(), element -> {
if (element != null && continuation != null && Objects.equals(focusOwner, IdeFocusManager.getInstance(myProject).getFocusOwner())) {
context.applyUIChanges();
continuation.run();
}
},
CodeInsightBundle.message("parameter.info.progress.title"),
myLbraceMarker.getStartOffset(),
myEditor);
})
.submit(AppExecutorUtil.getAppExecutorService())
.onProcessed(ignore -> myEditor.getScrollingModel().removeVisibleAreaListener(visibleAreaListener)));
}
});
}
@HintManager.PositionFlags
@@ -577,7 +547,7 @@ public class ParameterInfoController extends UserDataHolderBase implements Dispo
}
}
private static int getParameterIndex(@NotNull PsiElement[] parameters, @NotNull IElementType delimiter, int offset) {
private static int getParameterIndex(PsiElement @NotNull [] parameters, @NotNull IElementType delimiter, int offset) {
for (int i = 0; i < parameters.length; i++) {
PsiElement parameter = parameters[i];
TextRange textRange = parameter.getTextRange();
@@ -967,7 +937,7 @@ public class ParameterInfoController extends UserDataHolderBase implements Dispo
private static class WrapperPanel extends JPanel {
WrapperPanel() {
super(new CardLayout());
super(new BorderLayout());
setBorder(JBUI.Borders.empty());
}
@@ -1,171 +0,0 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.hint;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.NonBlockingReadAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.event.VisibleAreaListener;
import com.intellij.openapi.progress.util.ProgressWindow;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.LoadingDecorator;
import com.intellij.openapi.ui.popup.ComponentPopupBuilder;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.ui.HintListener;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBLoadingPanel;
import com.intellij.ui.components.panels.NonOpaquePanel;
import com.intellij.util.concurrency.AppExecutorUtil;
import com.intellij.util.concurrency.EdtScheduledExecutorService;
import com.intellij.util.ui.AsyncProcessIcon;
import com.intellij.util.ui.EmptyIcon;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.concurrency.CancellablePromise;
import javax.swing.*;
import java.awt.*;
import java.util.EventObject;
import java.util.Objects;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
class ParameterInfoTaskRunnerUtil {
static <T> void runTask(Project project,
NonBlockingReadAction<T> nonBlockingReadAction,
Consumer<T> continuationConsumer,
String progressTitle,
int offset,
Editor editor) {
AtomicReference<CancellablePromise<?>> cancellablePromiseRef = new AtomicReference<>();
Consumer<Boolean> stopAction =
startProgressAndCreateStopAction(editor.getProject(), progressTitle, cancellablePromiseRef, offset, editor);
final VisibleAreaListener visibleAreaListener = new CancelProgressOnScrolling(cancellablePromiseRef);
editor.getScrollingModel().addVisibleAreaListener(visibleAreaListener);
final Component focusOwner = getFocusOwner(project);
cancellablePromiseRef.set(
nonBlockingReadAction.finishOnUiThread(
ModalityState.defaultModalityState(),
continuation -> {
CancellablePromise<?> promise = cancellablePromiseRef.get();
if (promise != null && promise.isSucceeded() && Objects.equals(focusOwner, getFocusOwner(project))) {
continuationConsumer.accept(continuation);
}
})
.submit(AppExecutorUtil.getAppExecutorService())
.onProcessed(ignore -> {
stopAction.accept(false);
editor.getScrollingModel().removeVisibleAreaListener(visibleAreaListener);
}));
}
private static Component getFocusOwner(Project project) {
return IdeFocusManager.getInstance(project).getFocusOwner();
}
@NotNull
private static Consumer<Boolean> startProgressAndCreateStopAction(Project project,
String progressTitle,
AtomicReference<CancellablePromise<?>> promiseRef,
int offset,
Editor editor) {
offset = offset > 0 ? offset : editor.getCaretModel().getOffset() - 1;
ParameterInfoController controller = ParameterInfoController.findControllerAtOffset(editor, offset);
AtomicReference<Consumer<Boolean>> stopActionRef = new AtomicReference<>();
Consumer<Boolean> originalStopAction = (cancel) -> {
stopActionRef.set(null);
if (cancel) {
CancellablePromise<?> promise = promiseRef.get();
if (promise != null) {
promise.cancel();
}
}
};
HintListener hintListener = new HintListener() {
@Override
public void hintHidden(@NotNull EventObject event) {
Consumer<Boolean> stopAction = stopActionRef.get();
if (stopAction != null) {
stopAction.accept(true);
}
}
};
if (controller != null && controller.showLoading(hintListener)) {
stopActionRef.set((cancel) -> {
try {
controller.hideLoading(hintListener);
} finally {
originalStopAction.accept(cancel);
}
});
} else {
final Disposable disposable = Disposer.newDisposable();
Disposer.register(project, disposable);
JBLoadingPanel loadingPanel =
new JBLoadingPanel(null, panel -> new LoadingDecorator(panel, disposable, 0, false, new AsyncProcessIcon("ShowParameterInfo")) {
@Override
protected NonOpaquePanel customizeLoadingLayer(JPanel parent, JLabel text, AsyncProcessIcon icon) {
parent.setLayout(new FlowLayout(FlowLayout.LEFT));
final NonOpaquePanel result = new NonOpaquePanel();
result.add(icon);
parent.add(result);
return result;
}
});
loadingPanel.add(new JBLabel(EmptyIcon.ICON_18));
loadingPanel.add(new JBLabel(progressTitle));
ComponentPopupBuilder builder =
JBPopupFactory.getInstance().createComponentPopupBuilder(loadingPanel, null)
.setProject(project)
.setCancelCallback(() -> {
Consumer<Boolean> stopAction = stopActionRef.get();
if (stopAction != null) {
stopAction.accept(true);
}
return true;
});
JBPopup popup = builder.createPopup();
Disposer.register(disposable, popup);
ScheduledFuture<?> showPopupFuture = EdtScheduledExecutorService.getInstance().schedule(() -> {
if (!popup.isDisposed() && !popup.isVisible()) {
RelativePoint popupPosition = JBPopupFactory.getInstance().guessBestPopupLocation(editor);
loadingPanel.startLoading();
popup.show(popupPosition);
}
}, ModalityState.defaultModalityState(), ProgressWindow.DEFAULT_PROGRESS_DIALOG_POSTPONE_TIME_MILLIS, TimeUnit.MILLISECONDS);
stopActionRef.set((cancel) -> {
try {
loadingPanel.stopLoading();
originalStopAction.accept(cancel);
} finally {
showPopupFuture.cancel(false);
UIUtil.invokeLaterIfNeeded(() -> {
if (popup.isVisible()) {
popup.setUiVisible(false);
}
Disposer.dispose(disposable);
});
}
});
}
return stopActionRef.get();
}
}
@@ -11,26 +11,33 @@ import com.intellij.lang.Language;
import com.intellij.lang.parameterInfo.LanguageParameterInfo;
import com.intellij.lang.parameterInfo.ParameterInfoHandler;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.event.VisibleAreaListener;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.ui.LightweightHint;
import com.intellij.util.Consumer;
import com.intellij.util.ObjectUtils;
import com.intellij.util.concurrency.AppExecutorUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import static com.intellij.codeInsight.hint.ParameterInfoTaskRunnerUtil.runTask;
public class ShowParameterInfoHandler implements CodeInsightActionHandler {
private static final ParameterInfoHandler[] EMPTY_HANDLERS = new ParameterInfoHandler[0];
@@ -75,6 +82,7 @@ public class ShowParameterInfoHandler implements CodeInsightActionHandler {
boolean requestFocus, boolean singleParameterHint,
String progressTitle,
Consumer<IndexNotReadyException> indexNotReadyExceptionConsumer) {
final Component focusOwner = IdeFocusManager.getInstance(project).getFocusOwner();
final DumbService dumbService = DumbService.getInstance(project);
final int initialOffset = editor.getCaretModel().getOffset();
@@ -82,82 +90,98 @@ public class ShowParameterInfoHandler implements CodeInsightActionHandler {
Lookup lookup = LookupManager.getInstance(project).getActiveLookup();
LookupElement lookupElement = lookup != null ? lookup.getCurrentItem() : null;
runTask(project,
ReadAction.nonBlocking(() -> {
final int offset = editor.getCaretModel().getOffset();
final int fileLength = file.getTextLength();
if (fileLength == 0) return null;
ProgressManager.getInstance().run(
new Task.Backgroundable(project, progressTitle, true) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
assert !ApplicationManager.getApplication().isWriteAccessAllowed() :
"Show parameter info under write action could lead to live lock";
// file.findElementAt(file.getTextLength()) returns null but we may need to show parameter info at EOF offset (for example in SQL)
final int offsetForLangDetection = offset > 0 && offset == fileLength ? offset - 1 : offset;
final Language language = PsiUtilCore.getLanguageAtOffset(file, offsetForLangDetection);
final VisibleAreaListener visibleAreaListener = new CancelProgressOnScrolling(indicator);
final ShowParameterInfoContext context = new ShowParameterInfoContext(
editor,
project,
file,
offset,
lbraceOffset,
requestFocus,
singleParameterHint
);
editor.getScrollingModel().addVisibleAreaListener(visibleAreaListener);
context.setHighlightedElement(highlightedElement);
context.setRequestFocus(requestFocus);
ProgressIndicatorUtils.awaitWithCheckCanceled(ReadAction
.nonBlocking(() -> {
final int offset = editor.getCaretModel().getOffset();
final int fileLength = file.getTextLength();
if (fileLength == 0) return null;
final ParameterInfoHandler<PsiElement, Object>[] handlers =
ObjectUtils.notNull(getHandlers(project, language, file.getViewProvider().getBaseLanguage()), EMPTY_HANDLERS);
// file.findElementAt(file.getTextLength()) returns null but we may need to show parameter info at EOF offset (for example in SQL)
final int offsetForLangDetection = offset > 0 && offset == fileLength ? offset - 1 : offset;
final Language language = PsiUtilCore.getLanguageAtOffset(file, offsetForLangDetection);
if (lookup != null) {
if (lookupElement != null) {
for (ParameterInfoHandler<PsiElement, Object> handler : handlers) {
if (handler.couldShowInLookup()) {
final Object[] items = handler.getParametersForLookup(lookupElement, context);
if (items != null && items.length > 0) {
return (Runnable)() -> {
showLookupEditorHint(items, editor, handler, requestFocus);
};
}
return null;
final ShowParameterInfoContext context = new ShowParameterInfoContext(
editor,
project,
file,
offset,
lbraceOffset,
requestFocus,
singleParameterHint
);
context.setHighlightedElement(highlightedElement);
context.setRequestFocus(requestFocus);
final ParameterInfoHandler<PsiElement, Object>[] handlers =
ObjectUtils.notNull(getHandlers(project, language, file.getViewProvider().getBaseLanguage()), EMPTY_HANDLERS);
if (lookup != null) {
if (lookupElement != null) {
for (ParameterInfoHandler<PsiElement, Object> handler : handlers) {
if (handler.couldShowInLookup()) {
final Object[] items = handler.getParametersForLookup(lookupElement, context);
if (items != null && items.length > 0) {
return (Runnable)() -> {
showLookupEditorHint(items, editor, handler, requestFocus);
};
}
return null;
}
}
return null;
}
dumbService.setAlternativeResolveEnabled(true);
try {
for (int i = 0; i < handlers.length; i++) {
ParameterInfoHandler<PsiElement, Object> handler = handlers[i];
PsiElement element = handler.findElementForParameterInfo(context);
if (element != null) {
return (Runnable)() -> {
if (element.isValid()) {
handler.showParameterInfo(element, context);
}
};
}
}
}
catch (IndexNotReadyException e) {
indexNotReadyExceptionConsumer.accept(e);
}
finally {
dumbService.setAlternativeResolveEnabled(false);
}
return null;
})
.withDocumentsCommitted(project)
.expireWhen(() -> editor.getCaretModel().getOffset() != initialOffset)
.coalesceBy(ShowParameterInfoHandler.class, editor),
}
dumbService.setAlternativeResolveEnabled(true);
try {
for (int i = 0; i < handlers.length; i++) {
ParameterInfoHandler<PsiElement, Object> handler = handlers[i];
PsiElement element = handler.findElementForParameterInfo(context);
if (element != null) {
return (Runnable)() -> {
if (element.isValid()) {
handler.showParameterInfo(element, context);
}
};
}
}
}
catch (IndexNotReadyException e) {
indexNotReadyExceptionConsumer.consume(e);
}
finally {
dumbService.setAlternativeResolveEnabled(false);
}
return null;
})
.withDocumentsCommitted(project)
.finishOnUiThread(
ModalityState.defaultModalityState(),
continuation -> {
if (continuation != null) {
if (continuation != null && Objects.equals(focusOwner, IdeFocusManager.getInstance(project).getFocusOwner())) {
continuation.run();
}
},
progressTitle,
initialOffset,
editor);
})
.cancelWith(indicator)
.expireWhen(() -> editor.getCaretModel().getOffset() != initialOffset)
.coalesceBy(ShowParameterInfoHandler.class, editor)
.submit(AppExecutorUtil.getAppExecutorService())
.onProcessed(ignore -> editor.getScrollingModel().removeVisibleAreaListener(visibleAreaListener)));
}
}
);
}
private static void showLookupEditorHint(Object[] descriptors,
@@ -179,8 +203,7 @@ public class ShowParameterInfoHandler implements CodeInsightActionHandler {
});
}
@Nullable
public static ParameterInfoHandler[] getHandlers(Project project, final Language... languages) {
public static ParameterInfoHandler @Nullable [] getHandlers(Project project, final Language... languages) {
Set<ParameterInfoHandler> handlers = new LinkedHashSet<>();
DumbService dumbService = DumbService.getInstance(project);
for (final Language language : languages) {