mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
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:
committed by
intellij-monorepo-bot
parent
c40db04b6c
commit
fdb6714a2b
+1
-14
@@ -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<Integer> extractKey</b>, Function<String, Integer> 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());
|
||||
}
|
||||
|
||||
|
||||
-3
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+73
-103
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
-171
@@ -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();
|
||||
}
|
||||
}
|
||||
+91
-68
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user