From 3066cb05c6b115c3196d42a126bd39b4b91372ab Mon Sep 17 00:00:00 2001 From: Dennis Ushakov Date: Mon, 7 May 2018 13:24:01 +0300 Subject: [PATCH] extract parameter info view model --- .../hint/ParameterInfoComponent.java | 57 +++++++++++++--- .../hint/ParameterInfoController.java | 66 ++++++++++++++++--- .../hint/ParameterInfoListener.java | 22 +++++++ .../hint/ShowParameterInfoContext.java | 4 +- .../src/META-INF/LangExtensionPoints.xml | 1 + 5 files changed, 130 insertions(+), 20 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoListener.java diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoComponent.java b/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoComponent.java index 13313c9861fa..64f8f5ad1373 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoComponent.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoComponent.java @@ -13,6 +13,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.ui.*; import com.intellij.util.Function; @@ -28,6 +29,7 @@ import javax.swing.*; import javax.swing.border.Border; import java.awt.*; import java.util.*; +import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -86,7 +88,7 @@ public class ParameterInfoComponent extends JPanel { super(new BorderLayout()); myRequestFocus = requestFocus; - if (!ApplicationManager.getApplication().isUnitTestMode()) { + if (!ApplicationManager.getApplication().isUnitTestMode() && !ApplicationManager.getApplication().isHeadlessEnvironment()) { JComponent editorComponent = editor.getComponent(); JLayeredPane layeredPane = editorComponent.getRootPane().getLayeredPane(); myWidthLimit = layeredPane.getWidth(); @@ -169,6 +171,7 @@ public class ParameterInfoComponent extends JPanel { private final boolean mySingleParameterInfo; private int i; private Function myEscapeFunction; + private final ParameterInfoController.Model result = new ParameterInfoController.Model(); public MyParameterContext(boolean singleParameterInfo) { mySingleParameterInfo = singleParameterInfo; @@ -182,6 +185,29 @@ public class ParameterInfoComponent extends JPanel { boolean strikeout, boolean isDisabledBeforeHighlight, Color background) { + List split = StringUtil.split(text, ",", false); + StringBuilder plainLine = new StringBuilder(); + final List startOffsets = new ArrayList<>(); + final List endOffsets = new ArrayList<>(); + + TextRange highlightRange = highlightStartOffset >=0 && highlightEndOffset >= highlightStartOffset ? + new TextRange(highlightStartOffset, highlightEndOffset) : + null; + for (int j = 0; j < split.size(); j++) { + String line = split.get(j); + int startOffset = plainLine.length(); + startOffsets.add(startOffset); + plainLine.append(line); + int endOffset = plainLine.length(); + endOffsets.add(endOffset); + if (highlightRange != null && highlightRange.intersects(new TextRange(startOffset, endOffset))) { + result.current = j; + } + } + ParameterInfoController.SignatureItem item = new ParameterInfoController.SignatureItem(plainLine.toString(), strikeout, isDisabled, + startOffsets, endOffsets); + result.signatures.add(item); + final String resultedText = myPanels[i].setup(text, myEscapeFunction, highlightStartOffset, highlightEndOffset, isDisabled, strikeout, isDisabledBeforeHighlight, background); myPanels[i].setBorder(isLastParameterOwner() || isSingleParameterInfo() ? EMPTY_BORDER : BOTTOM_BORDER); @@ -196,7 +222,7 @@ public class ParameterInfoComponent extends JPanel { @Override public String setupUIComponentPresentation(final String[] texts, final EnumSet[] flags, final Color background) { - final String resultedText = myPanels[i].setup(texts, myEscapeFunction, flags, background); + final String resultedText = myPanels[i].setup(result, texts, myEscapeFunction, flags, background); myPanels[i].setBorder(isLastParameterOwner() || isSingleParameterInfo() ? EMPTY_BORDER : BOTTOM_BORDER); return resultedText; } @@ -252,7 +278,7 @@ public class ParameterInfoComponent extends JPanel { } } - public void update(boolean singleParameterInfo) { + public ParameterInfoController.Model update(boolean singleParameterInfo) { MyParameterContext context = new MyParameterContext(singleParameterInfo); for (int i = 0; i < myObjects.length; i++) { @@ -270,6 +296,7 @@ public class ParameterInfoComponent extends JPanel { } if (myShortcutLabel != null) myShortcutLabel.setVisible(!singleParameterInfo); + return context.result; } public Object[] getObjects() { @@ -388,7 +415,8 @@ public class ParameterInfoComponent extends JPanel { return escapeFunction == null ? line : escapeFunction.fun(line); } - public String setup(final String[] texts, + public String setup(final ParameterInfoController.Model result, + final String[] texts, Function escapeFunction, final EnumSet[] flags, final Color background) { @@ -398,16 +426,24 @@ public class ParameterInfoComponent extends JPanel { int index = 0; int curOffset = 0; final ArrayList components = new ArrayList<>(); + final List startOffsets = new ArrayList<>(); + final List endOffsets = new ArrayList<>(); + Map flagsMap = new TreeMap<>(TEXT_RANGE_COMPARATOR); - String line = ""; + StringBuilder fullLine = new StringBuilder(); + StringBuilder line = new StringBuilder(); for (int i = 0; i < texts.length; i++) { String paramText = escapeString(texts[i], escapeFunction); if (paramText == null) break; - line += texts[i]; + startOffsets.add(fullLine.length()); + fullLine.append(texts[i]); + endOffsets.add(fullLine.length()); + line.append(texts[i]); final EnumSet flag = flags[i]; if (flag.contains(ParameterInfoUIContextEx.Flag.HIGHLIGHT)) { + result.current = i; flagsMap.put(TextRange.create(curOffset, curOffset + paramText.trim().length()), ParameterInfoUIContextEx.Flag.HIGHLIGHT); } @@ -422,18 +458,21 @@ public class ParameterInfoComponent extends JPanel { curOffset += paramText.length(); if (line.length() >= 50) { final OneLineComponent component = new OneLineComponent(); - buf.append(component.setup(escapeString(line, escapeFunction), flagsMap, background)); + buf.append(component.setup(escapeString(line.toString(), escapeFunction), flagsMap, background)); add(component, new GridBagConstraints(0, index, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); index += 1; flagsMap.clear(); curOffset = 0; - line = ""; + line = new StringBuilder(); components.add(component); } } + ParameterInfoController.SignatureItem item = new ParameterInfoController.SignatureItem(fullLine.toString(), false, false, + startOffsets, endOffsets); + result.signatures.add(item); final OneLineComponent component = new OneLineComponent(); - buf.append(component.setup(escapeString(line, escapeFunction), flagsMap, background)); + buf.append(component.setup(escapeString(line.toString(), escapeFunction), flagsMap, background)); add(component, new GridBagConstraints(0, index, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); components.add(component); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoController.java b/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoController.java index fa2d18dee568..489870f31cae 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoController.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoController.java @@ -67,6 +67,7 @@ public class ParameterInfoController extends UserDataHolderBase implements Visib private final CaretListener myEditorCaretListener; @NotNull private final ParameterInfoHandler myHandler; private final MyBestLocationPointProvider myProvider; + private final ParameterInfoListener[] myListeners; private final Alarm myAlarm = new Alarm(); private static final int DELAY = 200; @@ -129,6 +130,7 @@ public class ParameterInfoController extends UserDataHolderBase implements Visib myEditor = editor; myHandler = handler; myProvider = new MyBestLocationPointProvider(editor); + myListeners = ParameterInfoListener.EP_NAME.getExtensions(); myLbraceMarker = editor.getDocument().createRangeMarker(lbraceOffset, lbraceOffset); myComponent = new ParameterInfoComponent(descriptors, editor, handler, requestFocus, true); myHint = createHint(); @@ -197,7 +199,7 @@ public class ParameterInfoController extends UserDataHolderBase implements Visib public void dispose(){ if (myDisposed) return; myDisposed = true; - myHint.hide(); + hideHint(); myHandler.dispose(new MyDeleteParameterInfoContext()); List allControllers = getAllControllers(myEditor); allControllers.remove(this); @@ -213,7 +215,7 @@ public class ParameterInfoController extends UserDataHolderBase implements Visib public void showHint(boolean requestFocus, boolean singleParameterInfo) { if (myHint.isVisible()) { myHint.getComponent().remove(myComponent); - myHint.hide(); + hideHint(); myHint = createHint(); } @@ -291,7 +293,7 @@ public class ParameterInfoController extends UserDataHolderBase implements Visib } public void updateComponent(){ - if (!myKeepOnHintHidden && !myHint.isVisible() || myEditor instanceof EditorWindow && !((EditorWindow)myEditor).isValid()) { + if (!myKeepOnHintHidden && !myHint.isVisible() && !ApplicationManager.getApplication().isHeadlessEnvironment() || myEditor instanceof EditorWindow && !((EditorWindow)myEditor).isValid()) { Disposer.dispose(this); return; } @@ -310,14 +312,22 @@ public class ParameterInfoController extends UserDataHolderBase implements Visib boolean knownParameter = (myComponent.getObjects().length == 1 || myComponent.getHighlighted() != null) && myComponent.getCurrentParameterIndex() != -1; if (mySingleParameterInfo && !knownParameter && myHint.isVisible()) { - myHint.hide(); + hideHint(); } if (myKeepOnHintHidden && knownParameter && !myHint.isVisible()) { AutoPopupController.getInstance(myProject).autoPopupParameterInfo(myEditor, null); } - if (!myDisposed && myHint.isVisible() && !myEditor.isDisposed() && - (myEditor.getComponent().getRootPane() != null || ApplicationManager.getApplication().isUnitTestMode())) { - myComponent.update(mySingleParameterInfo); + if (!myDisposed && (myHint.isVisible() && !myEditor.isDisposed() && + (myEditor.getComponent().getRootPane() != null || ApplicationManager.getApplication().isUnitTestMode()) || + ApplicationManager.getApplication().isHeadlessEnvironment())) { + Model result = myComponent.update(mySingleParameterInfo); + result.project = myProject; + result.range = myComponent.getParameterOwner().getTextRange(); + result.editor = myEditor; + for (ParameterInfoListener listener : myListeners) { + listener.hintUpdated(result); + } + if (ApplicationManager.getApplication().isHeadlessEnvironment()) return; IdeTooltip tooltip = myHint.getCurrentIdeTooltip(); short position = tooltip != null ? toShort(tooltip.getPreferredPosition()) @@ -329,7 +339,7 @@ public class ParameterInfoController extends UserDataHolderBase implements Visib } } else { - myHint.hide(); + hideHint(); if (!myKeepOnHintHidden) { Disposer.dispose(this); } @@ -499,7 +509,8 @@ public class ParameterInfoController extends UserDataHolderBase implements Visib VisualPosition pos, LightweightHint hint, short preferredPosition, boolean showLookupHint) { - if (ApplicationManager.getApplication().isUnitTestMode()) return Pair.pair(new Point(), HintManager.DEFAULT); + if (ApplicationManager.getApplication().isUnitTestMode() || + ApplicationManager.getApplication().isHeadlessEnvironment()) return Pair.pair(new Point(), HintManager.DEFAULT); HintManagerImpl hintManager = HintManagerImpl.getInstanceImpl(); Dimension hintSize = hint.getComponent().getPreferredSize(); @@ -580,7 +591,7 @@ public class ParameterInfoController extends UserDataHolderBase implements Visib @Override public void removeHint() { - myHint.hide(); + hideHint(); if (!myKeepOnHintHidden) Disposer.dispose(ParameterInfoController.this); } @@ -659,6 +670,41 @@ public class ParameterInfoController extends UserDataHolderBase implements Visib } } + protected void hideHint() { + myHint.hide(); + for (ParameterInfoListener listener : myListeners) { + listener.hintHidden(myProject); + } + } + + public static class SignatureItem { + public final String text; + public final boolean deprecated; + public final boolean disabled; + public final List startOffsets; + public final List endOffsets; + + public SignatureItem(String text, + boolean deprecated, + boolean disabled, + List startOffsets, + List endOffsets) { + this.text = text; + this.deprecated = deprecated; + this.disabled = disabled; + this.startOffsets = startOffsets; + this.endOffsets = endOffsets; + } + } + + public static class Model { + public final List signatures = new ArrayList<>(); + public int current = -1; + public TextRange range; + public Editor editor; + public Project project; + } + private static class MyBestLocationPointProvider { private final Editor myEditor; private int previousOffset = -1; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoListener.java b/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoListener.java new file mode 100644 index 000000000000..bf6a725f0eb4 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoListener.java @@ -0,0 +1,22 @@ +// Copyright 2000-2018 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.extensions.ExtensionPointName; +import com.intellij.openapi.project.Project; + +public interface ParameterInfoListener { + ExtensionPointName EP_NAME = new ExtensionPointName<>("com.intellij.codeInsight.parameterInfo.listener"); + + /** + * This method is invoked when parameter info hint content is updated (including first time show) and there are some signatures to show + * @param result model describing signatures shown and current context + */ + void hintUpdated(ParameterInfoController.Model result); + + /** + * This method is invoked when parameter info hint is hidden due to closing the hint, leaving parameters range area or because there are + * no signatures to show + * @param project for which hint was shown originally + */ + void hintHidden(Project project); +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hint/ShowParameterInfoContext.java b/platform/lang-impl/src/com/intellij/codeInsight/hint/ShowParameterInfoContext.java index e61f2999b86f..f3b9dc3bd8d1 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hint/ShowParameterInfoContext.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/hint/ShowParameterInfoContext.java @@ -118,7 +118,9 @@ public class ShowParameterInfoContext implements CreateParameterInfoContext { PsiDocumentManager.getInstance(project).performLaterWhenAllCommitted(() -> { if (editor.isDisposed() || DumbService.isDumb(project) || - (!ApplicationManager.getApplication().isUnitTestMode() && !editor.getComponent().isShowing())) return; + (!ApplicationManager.getApplication().isUnitTestMode() && + !ApplicationManager.getApplication().isHeadlessEnvironment() && + !editor.getComponent().isShowing())) return; final Document document = editor.getDocument(); if (document.getTextLength() < elementStart) return; diff --git a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml index a76ea38f14f6..12cfe467f3e1 100644 --- a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml @@ -619,6 +619,7 @@ +