From 2ff70734e1cdca5d612acdc0704f3ed491e25921 Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Fri, 14 Jul 2017 19:19:45 +0300 Subject: [PATCH] reorganize code showing parameter hints on completion Both completion and 'static' hints are generated in one place now - by hints provider. This should also fix IDEA-173963. --- .../completion/JavaMethodCallElement.java | 55 +++------------ .../JavaMethodOverloadSwitchHandler.java | 26 ++----- .../JavaVarArgNextParameterHandler.java | 45 ++---------- .../JavaVarArgPrevParameterHandler.java | 48 ++----------- .../api/impls/MethodParameterInfoHandler.java | 14 +--- .../codeInsight/hints/JavaHintUtils.kt | 24 ++++++- .../hints/JavaInlayParameterHintsProvider.kt | 3 +- .../completion/CompletionHintsTest.java | 2 +- .../hints/InlayParameterHintsProvider.kt | 9 +-- .../highlighting/InlineElementData.java | 2 +- .../ParameterHintsPresentationManager.java | 12 +--- .../codeInsight/hints/ParameterHintsPass.java | 38 +++++++++-- .../hints/ParameterHintsPassFactory.java | 2 +- .../hints/ParameterHintsUpdater.java | 68 +++++++++++++++---- 14 files changed, 148 insertions(+), 200 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaMethodCallElement.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaMethodCallElement.java index 1d7ba8cc4cc3..5f106d2680c5 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaMethodCallElement.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaMethodCallElement.java @@ -17,13 +17,13 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.AutoPopupController; import com.intellij.codeInsight.completion.util.MethodParenthesesHandler; -import com.intellij.codeInsight.daemon.impl.ParameterHintsPresentationManager; import com.intellij.codeInsight.hint.ParameterInfoController; import com.intellij.codeInsight.hint.ShowParameterInfoContext; import com.intellij.codeInsight.hint.api.impls.MethodParameterInfoHandler; import com.intellij.codeInsight.hints.HintInfo; import com.intellij.codeInsight.hints.JavaInlayParameterHintsProvider; import com.intellij.codeInsight.hints.MethodInfoBlacklistFilter; +import com.intellij.codeInsight.hints.ParameterHintsPass; import com.intellij.codeInsight.lookup.*; import com.intellij.codeInsight.lookup.impl.JavaElementLookupRenderer; import com.intellij.codeInsight.template.*; @@ -32,10 +32,11 @@ import com.intellij.codeInsight.template.impl.TemplateImpl; import com.intellij.codeInsight.template.impl.TemplateManagerImpl; import com.intellij.codeInsight.template.impl.TemplateState; import com.intellij.featureStatistics.FeatureUsageTracker; -import com.intellij.lang.java.JavaLanguage; import com.intellij.openapi.Disposable; import com.intellij.openapi.command.WriteCommandAction; -import com.intellij.openapi.editor.*; +import com.intellij.openapi.editor.CaretModel; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.event.DocumentListener; import com.intellij.openapi.project.Project; @@ -55,7 +56,6 @@ import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; @@ -65,7 +65,7 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class JavaMethodCallElement extends LookupItem implements TypedLookupItem, StaticallyImportable { public static final ClassConditionKey CLASS_CONDITION_KEY = ClassConditionKey.create(JavaMethodCallElement.class); - public static final Key> COMPLETION_HINTS = Key.create("completion.hints"); + public static final Key COMPLETION_HINTS = Key.create("completion.hints"); @Nullable private final PsiClass myContainingClass; private final PsiMethod myMethod; private final MemberLookupHelper myHelper; @@ -314,19 +314,10 @@ public class JavaMethodCallElement extends LookupItem implements Type return; } - boolean showHints = true; - if (parametersCount == 1) { - HintInfo.MethodInfo methodInfo = JavaInlayParameterHintsProvider.Companion.getInstance().getMethodInfo(method); - if (methodInfo != null) { - showHints = MethodInfoBlacklistFilter.forLanguage(JavaLanguage.INSTANCE).showHint(methodInfo); - } - } - Editor editor = context.getEditor(); CaretModel caretModel = editor.getCaretModel(); int offset = caretModel.getOffset(); - caretModel.moveToOffset(offset - 1); // avoid caret impact on hints location - int braceOffset = caretModel.getOffset(); + int braceOffset = offset - 1; int numberOfCommas = parametersCount - 1; if (parametersCount > 1 && PsiImplUtil.isVarArgs(method)) numberOfCommas--; String commas = StringUtil.repeat(", ", numberOfCommas); @@ -338,48 +329,24 @@ public class JavaMethodCallElement extends LookupItem implements Type ShowParameterInfoContext infoContext = new ShowParameterInfoContext(editor, project, context.getFile(), braceOffset, braceOffset); if (handler.findElementForParameterInfo(infoContext) == null) { editor.getDocument().deleteString(offset, offset + commas.length()); - caretModel.moveToOffset(offset); return; } - List addedHints = new ArrayList<>(parametersCount); - if (showHints) { - for (PsiParameter parameter : parameterList.getParameters()) { - String name = parameter.getName(); - if (name != null) { - if (parametersCount > 1 && parameter.isVarArgs()) { - name = ", " + name; - offset -= 2; - } - addedHints.add(ParameterHintsPresentationManager.getInstance().addHint(editor, offset, name + ":", false, true)); - } - offset += 2; - } - } - VisualPosition afterBracePosition = editor.offsetToVisualPosition(braceOffset + 1); - caretModel.moveToVisualPosition(new VisualPosition(afterBracePosition.line, - afterBracePosition.column + (showHints ? 1 : 0))); // after hint - - parameterOwner.putUserData(COMPLETION_HINTS, addedHints); + methodCall.putUserData(COMPLETION_HINTS, Boolean.TRUE); ParameterInfoController controller = new ParameterInfoController(project, editor, braceOffset, infoContext.getItemsToShow(), null, parameterOwner, handler, false, false); - Disposable hintsDisposal = () -> { - for (Inlay inlay : addedHints) { - if (inlay != null) ParameterHintsPresentationManager.getInstance().unpin(inlay); - } - addedHints.clear(); - }; + Disposable hintsDisposal = () -> methodCall.putUserData(COMPLETION_HINTS, null); if (Disposer.isDisposed(controller)) { Disposer.dispose(hintsDisposal); } else { + ParameterHintsPass.syncUpdate(methodCall, editor); Disposer.register(controller, hintsDisposal); } } - public static boolean hasCompletionHints(@NotNull PsiCallExpression expression) { - PsiExpressionList argumentList = expression.getArgumentList(); - return argumentList != null && !ContainerUtil.isEmpty(argumentList.getUserData(COMPLETION_HINTS)); + public static boolean showCompletionHints(@NotNull PsiCallExpression expression) { + return expression.getUserData(COMPLETION_HINTS) != null; } private static void setupNonFilledArgumentRemoving(final Editor editor, final TemplateState templateState) { diff --git a/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaMethodOverloadSwitchHandler.java b/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaMethodOverloadSwitchHandler.java index 7631ded15c7c..4f8de02b85a6 100644 --- a/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaMethodOverloadSwitchHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaMethodOverloadSwitchHandler.java @@ -17,16 +17,14 @@ package com.intellij.codeInsight.editorActions; import com.intellij.codeInsight.completion.CompletionMemory; import com.intellij.codeInsight.completion.JavaMethodCallElement; -import com.intellij.codeInsight.daemon.impl.ParameterHintsPresentationManager; import com.intellij.codeInsight.hint.ParameterInfoController; +import com.intellij.codeInsight.hints.ParameterHintsPass; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Caret; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.Inlay; import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Key; import com.intellij.psi.*; import com.intellij.psi.infos.CandidateInfo; @@ -34,7 +32,9 @@ import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; public class JavaMethodOverloadSwitchHandler extends EditorWriteActionHandler { private static final Key> ENTERED_PARAMETERS = Key.create("entered.parameters"); @@ -117,39 +117,25 @@ public class JavaMethodOverloadSwitchHandler extends EditorWriteActionHandler { caret.moveToOffset(lbraceOffset); // avoid caret impact on hints location int offset = lbraceOffset + 1; int endOffset = exprList.getTextRange().getEndOffset() - 1; - List oldInlays = editor.getInlayModel().getInlineElementsInRange(offset, endOffset); - for (Inlay inlay : oldInlays) { - Disposer.dispose(inlay); - } editor.getDocument().deleteString(offset, endOffset); int targetCaretPosition = -1; - List addedHints = new ArrayList<>(parametersCount); for (int i = 0; i < parametersCount; i++) { String key = getParameterKey(targetMethod, i); String value = enteredParameters.getOrDefault(key, ""); if (value.isEmpty() && targetCaretPosition == -1) targetCaretPosition = offset; if (i < parametersCount - 1) value += ", "; editor.getDocument().insertString(offset, value); - String name = parameterList.getParameters()[i].getName(); - if (name != null) { - addedHints.add(ParameterHintsPresentationManager.getInstance().addHint(editor, offset, name + ":", false, true)); - } offset += value.length(); } if (targetCaretPosition == -1) targetCaretPosition = offset; caret.moveToLogicalPosition(editor.offsetToLogicalPosition(targetCaretPosition).leanForward(true)); - exprList.putUserData(JavaMethodCallElement.COMPLETION_HINTS, addedHints); - Disposer.register(controller, () -> { - for (Inlay hint : addedHints) { - if (hint != null) ParameterHintsPresentationManager.getInstance().unpin(hint); - } - addedHints.clear(); - }); + call.putUserData(JavaMethodCallElement.COMPLETION_HINTS, Boolean.TRUE); PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument()); CompletionMemory.registerChosenMethod(targetMethod, (PsiCall)call); controller.resetHighlighted(); controller.updateComponent(); // update popup immediately (otherwise, it will be updated only after delay) + ParameterHintsPass.syncUpdate(call, editor); } private static String getParameterKey(PsiMethod method, int parameterIndex) { diff --git a/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaVarArgNextParameterHandler.java b/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaVarArgNextParameterHandler.java index 82e5347fc259..0fd8414642c7 100644 --- a/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaVarArgNextParameterHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaVarArgNextParameterHandler.java @@ -15,18 +15,15 @@ */ package com.intellij.codeInsight.editorActions; -import com.intellij.codeInsight.completion.JavaMethodCallElement; -import com.intellij.codeInsight.daemon.impl.ParameterHintsPresentationManager; import com.intellij.codeInsight.hint.ParameterInfoController; +import com.intellij.codeInsight.hints.ParameterHintsPass; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.editor.Caret; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.Inlay; import com.intellij.openapi.editor.actionSystem.EditorActionHandler; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Disposer; import com.intellij.psi.*; import com.intellij.psi.infos.CandidateInfo; import com.intellij.util.containers.ContainerUtil; @@ -35,7 +32,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Arrays; -import java.util.List; public class JavaVarArgNextParameterHandler extends EditorActionHandler { private final EditorActionHandler myDelegate; @@ -79,51 +75,18 @@ public class JavaVarArgNextParameterHandler extends EditorActionHandler { PsiMethod currentMethod = (PsiMethod)((CandidateInfo)objects[currentIndex]).getElement(); if (currentMethod.isVarArgs()) { int rParOffset = list.getTextRange().getEndOffset() - 1; - boolean lastParameterIsEmpty = - CharArrayUtil.containsOnlyWhiteSpaces( - text.subSequence(prev + 1, rParOffset)); + boolean lastParameterIsEmpty = CharArrayUtil.containsOnlyWhiteSpaces(text.subSequence(prev + 1, rParOffset)); if (lastParameterIsEmpty) { if (prevChar == ',') { - List inlays = editor.getInlayModel().getInlineElementsInRange(prev, rParOffset); - ParameterHintsPresentationManager presentationManager = ParameterHintsPresentationManager.getInstance(); - String inlayText = null; - List hints = exprList.getUserData(JavaMethodCallElement.COMPLETION_HINTS); - for (Inlay inlay : inlays) { - if (presentationManager.isParameterHint(inlay)) { - inlayText = presentationManager.getHintText(inlay); - if (hints != null) hints.remove(inlay); - Disposer.dispose(inlay); - break; - } - } WriteAction.run(() -> editor.getDocument().deleteString(prev, rParOffset)); - if (inlayText != null) { - Inlay inlay = presentationManager.addHint(editor, prev, ", " + inlayText, false, true); - if (hints != null) hints.add(inlay); - } } } else { - int wsStart = CharArrayUtil.shiftBackward(text, rParOffset - 1, " \t") + 1; - List inlays = editor.getInlayModel().getInlineElementsInRange(wsStart, rParOffset); - ParameterHintsPresentationManager presentationManager = ParameterHintsPresentationManager.getInstance(); - String inlayText = null; - List hints = exprList.getUserData(JavaMethodCallElement.COMPLETION_HINTS); - for (Inlay inlay : inlays) { - if (presentationManager.isParameterHint(inlay) && presentationManager.getHintText(inlay).startsWith(", ")) { - inlayText = presentationManager.getHintText(inlay); - if (hints != null) hints.remove(inlay); - Disposer.dispose(inlay); - break; - } - } WriteAction.run(() -> editor.getDocument().insertString(rParOffset, ", ")); - if (inlayText != null) { - Inlay inlay = presentationManager.addHint(editor, rParOffset + 2, inlayText.substring(2), false, true); - if (hints != null) hints.add(inlay); - } } PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument()); + PsiElement call = list.getParent(); + if (call != null) ParameterHintsPass.syncUpdate(call, editor); } } } diff --git a/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaVarArgPrevParameterHandler.java b/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaVarArgPrevParameterHandler.java index 518c82fe2bd4..50281fa48520 100644 --- a/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaVarArgPrevParameterHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaVarArgPrevParameterHandler.java @@ -15,18 +15,15 @@ */ package com.intellij.codeInsight.editorActions; -import com.intellij.codeInsight.completion.JavaMethodCallElement; -import com.intellij.codeInsight.daemon.impl.ParameterHintsPresentationManager; import com.intellij.codeInsight.hint.ParameterInfoController; +import com.intellij.codeInsight.hints.ParameterHintsPass; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.editor.Caret; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.Inlay; import com.intellij.openapi.editor.actionSystem.EditorActionHandler; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Disposer; import com.intellij.psi.*; import com.intellij.psi.infos.CandidateInfo; import com.intellij.util.containers.ContainerUtil; @@ -35,7 +32,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Arrays; -import java.util.List; public class JavaVarArgPrevParameterHandler extends EditorActionHandler { private final EditorActionHandler myDelegate; @@ -73,48 +69,18 @@ public class JavaVarArgPrevParameterHandler extends EditorActionHandler { if (currentIndex >= 0) { PsiMethod currentMethod = (PsiMethod)((CandidateInfo)objects[currentIndex]).getElement(); if (currentMethod.isVarArgs()) { + boolean toReturn = false; if (offset == rParOffset + 1) { - int wsStart = CharArrayUtil.shiftBackward(text, rParOffset - 1, " \t") + 1; - List inlays = editor.getInlayModel().getInlineElementsInRange(wsStart, rParOffset); - ParameterHintsPresentationManager presentationManager = ParameterHintsPresentationManager.getInstance(); - String inlayText = null; - List hints = exprList.getUserData(JavaMethodCallElement.COMPLETION_HINTS); - for (Inlay inlay : inlays) { - if (presentationManager.isParameterHint(inlay) && presentationManager.getHintText(inlay).startsWith(", ")) { - inlayText = presentationManager.getHintText(inlay); - if (hints != null) hints.remove(inlay); - Disposer.dispose(inlay); - break; - } - } WriteAction.run(() -> editor.getDocument().insertString(rParOffset, ", ")); - if (inlayText != null) { - Inlay inlay = presentationManager.addHint(editor, rParOffset + 2, inlayText.substring(2), false, true); - if (hints != null) hints.add(inlay); - } - PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument()); } else { - List inlays = editor.getInlayModel().getInlineElementsInRange(prev, next); - ParameterHintsPresentationManager presentationManager = ParameterHintsPresentationManager.getInstance(); - String inlayText = null; - List hints = exprList.getUserData(JavaMethodCallElement.COMPLETION_HINTS); - for (Inlay inlay : inlays) { - if (presentationManager.isParameterHint(inlay)) { - inlayText = presentationManager.getHintText(inlay); - if (hints != null) hints.remove(inlay); - Disposer.dispose(inlay); - break; - } - } WriteAction.run(() -> editor.getDocument().deleteString(prev, next)); - if (inlayText != null) { - Inlay inlay = presentationManager.addHint(editor, prev, ", " + inlayText, false, true); - if (hints != null) hints.add(inlay); - editor.getCaretModel().moveToVisualPosition(inlay.getVisualPosition()); - } - return; + toReturn = true; } + PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument()); + PsiElement call = exprList.getParent(); + if (call != null) ParameterHintsPass.syncUpdate(call, editor); + if (toReturn) return; } } } diff --git a/java/java-impl/src/com/intellij/codeInsight/hint/api/impls/MethodParameterInfoHandler.java b/java/java-impl/src/com/intellij/codeInsight/hint/api/impls/MethodParameterInfoHandler.java index 7cbc01fea10f..6819a9b4a276 100644 --- a/java/java-impl/src/com/intellij/codeInsight/hint/api/impls/MethodParameterInfoHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/hint/api/impls/MethodParameterInfoHandler.java @@ -23,12 +23,10 @@ import com.intellij.codeInsight.completion.CompletionMemory; import com.intellij.codeInsight.completion.JavaCompletionUtil; import com.intellij.codeInsight.completion.JavaMethodCallElement; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; -import com.intellij.codeInsight.daemon.impl.ParameterHintsPresentationManager; import com.intellij.codeInsight.javadoc.JavaDocInfoGenerator; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.lang.parameterInfo.*; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Inlay; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.DumbService; import com.intellij.psi.*; @@ -137,24 +135,18 @@ public class MethodParameterInfoHandler implements ParameterInfoHandlerWithTabAc if (!(element instanceof PsiMethod)) return null; PsiMethod method = (PsiMethod)element; + PsiElement parent = expressionList.getParent(); int currentNumberOfParameters = expressionList.getExpressions().length; PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(context.getProject()); Document document = psiDocumentManager.getCachedDocument(context.getFile()); - if ((context.getHighlightedParameter() != null || candidates.length == 1) && + if ((context.getHighlightedParameter() != null || candidates.length == 1) && parent != null && document != null && psiDocumentManager.isCommitted(document) && isIncompatibleParameterCount(method, currentNumberOfParameters)) { - List hints = expressionList.getUserData(JavaMethodCallElement.COMPLETION_HINTS); - if (hints != null) { - for (Inlay hint : hints) { - if (hint != null) ParameterHintsPresentationManager.getInstance().unpin(hint); - } - hints.clear(); - } + parent.putUserData(JavaMethodCallElement.COMPLETION_HINTS, null); } String originalMethodName = method.getName(); PsiQualifiedReference currentMethodReference = null; - PsiElement parent = expressionList.getParent(); if (parent instanceof PsiMethodCallExpression && !method.isConstructor()) { currentMethodReference = ((PsiMethodCallExpression)parent).getMethodExpression(); } diff --git a/java/java-impl/src/com/intellij/codeInsight/hints/JavaHintUtils.kt b/java/java-impl/src/com/intellij/codeInsight/hints/JavaHintUtils.kt index 70a2e303e8cf..87d682e0e057 100644 --- a/java/java-impl/src/com/intellij/codeInsight/hints/JavaHintUtils.kt +++ b/java/java-impl/src/com/intellij/codeInsight/hints/JavaHintUtils.kt @@ -27,7 +27,23 @@ import com.intellij.psi.util.TypeConversionUtil object JavaInlayHintsProvider { fun hints(callExpression: PsiCallExpression): Set { - if (JavaMethodCallElement.hasCompletionHints(callExpression)) return emptySet() + if (JavaMethodCallElement.showCompletionHints(callExpression)) { + val method = CompletionMemory.getChosenMethod(callExpression)?:return emptySet() + + val params = method.parameterList.parameters + val arguments = callExpression.argumentList?.expressions ?: emptyArray() + + return params.mapIndexedNotNull { i, parameter -> + val paramName = parameter.name ?: return@mapIndexedNotNull null + val varargHint = parameter.type is PsiEllipsisType && params.size > 1 && + (arguments.size == params.size - 1 || params.size == 2 && arguments.isEmpty()) + val paramToShow = (if (varargHint) ", " else "") + paramName + val offset = if (i < arguments.size) inlayOffset(arguments[i]) + else if (varargHint && i <= arguments.size) inlayOffset(arguments[i - 1], true) + else (callExpression.argumentList?.textOffset?:return@mapIndexedNotNull null) + 1 + InlayInfo(paramToShow, offset, false, params.size == 1, varargHint) + }.toSet() + } val resolveResult = callExpression.resolveMethodGenerics() val hints = methodHints(callExpression, resolveResult) @@ -163,12 +179,14 @@ private fun inlayInfo(callArgument: PsiExpression, methodParam: PsiParameter, sh return InlayInfo(paramToShow, offset, showOnlyIfExistedBefore) } -fun inlayOffset(callArgument: PsiExpression): Int { +fun inlayOffset(callArgument: PsiExpression): Int = inlayOffset(callArgument, false) + +fun inlayOffset(callArgument: PsiExpression, atEnd: Boolean): Int { if (callArgument.textRange.isEmpty) { val next = callArgument.nextSibling as? PsiWhiteSpace if (next != null) return next.textRange.endOffset } - return callArgument.textRange.startOffset + return if (atEnd) callArgument.textRange.endOffset else callArgument.textRange.startOffset } private fun isUnclearExpression(callArgument: PsiElement): Boolean { diff --git a/java/java-impl/src/com/intellij/codeInsight/hints/JavaInlayParameterHintsProvider.kt b/java/java-impl/src/com/intellij/codeInsight/hints/JavaInlayParameterHintsProvider.kt index 6edccf3df83e..be26c5dce7c6 100644 --- a/java/java-impl/src/com/intellij/codeInsight/hints/JavaInlayParameterHintsProvider.kt +++ b/java/java-impl/src/com/intellij/codeInsight/hints/JavaInlayParameterHintsProvider.kt @@ -15,6 +15,7 @@ */ package com.intellij.codeInsight.hints +import com.intellij.codeInsight.completion.CompletionMemory import com.intellij.codeInsight.hints.HintInfo.MethodInfo import com.intellij.lang.java.JavaLanguage import com.intellij.openapi.util.text.StringUtil @@ -30,7 +31,7 @@ class JavaInlayParameterHintsProvider : InlayParameterHintsProvider { override fun getHintInfo(element: PsiElement): MethodInfo? { if (element is PsiCallExpression) { - val resolvedElement = element.resolveMethodGenerics().element + val resolvedElement = CompletionMemory.getChosenMethod(element) ?: element.resolveMethodGenerics ().element if (resolvedElement is PsiMethod) { return getMethodInfo(resolvedElement) } diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/CompletionHintsTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/CompletionHintsTest.java index 5b5cc6158904..8473678c4dbf 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/CompletionHintsTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/CompletionHintsTest.java @@ -282,7 +282,7 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { right(); type("2"); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { Character.forDigit(1,2 ) } }"); + checkResultWithInlays("class C { void m() { Character.forDigit(1,2 ) } }"); myFixture.performEditorAction(IdeActions.ACTION_EDITOR_MOVE_LINE_END); waitForAllAsyncStuff(); checkResultWithInlays("class C { void m() { Character.forDigit(1,2 ) } }"); diff --git a/platform/lang-api/src/com/intellij/codeInsight/hints/InlayParameterHintsProvider.kt b/platform/lang-api/src/com/intellij/codeInsight/hints/InlayParameterHintsProvider.kt index 26cf5386ef2a..d2db5391bb69 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/hints/InlayParameterHintsProvider.kt +++ b/platform/lang-api/src/com/intellij/codeInsight/hints/InlayParameterHintsProvider.kt @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,10 +26,11 @@ object InlayParameterHintsExtension : LanguageExtension displayText) { int offset = getStartOffset(); - ParameterHintsPresentationManager.getInstance().addHint(view, offset, myText, false, false); + ParameterHintsPresentationManager.getInstance().addHint(view, offset, myText, false); List inlays = view.getInlayModel().getInlineElementsInRange(offset, offset); for (Inlay inlay : inlays) { EditorCustomElementRenderer renderer = inlay.getRenderer(); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ParameterHintsPresentationManager.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ParameterHintsPresentationManager.java index 18d306ad34b7..e54126c33f3f 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ParameterHintsPresentationManager.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ParameterHintsPresentationManager.java @@ -43,7 +43,6 @@ import java.util.Iterator; import java.util.Set; public class ParameterHintsPresentationManager implements Disposable { - private static final Key PINNED = Key.create("parameter.hint.pinned"); private static final Key HINT_FONT_METRICS = Key.create("ParameterHintFontMetrics"); private static final Key ANIMATION_STEP = Key.create("ParameterHintAnimationStep"); @@ -64,24 +63,15 @@ public class ParameterHintsPresentationManager implements Disposable { return inlay.getRenderer() instanceof MyRenderer; } - public boolean isPinned(@NotNull Inlay inlay) { - return Boolean.TRUE.equals(inlay.getUserData(PINNED)); - } - - public void unpin(@NotNull Inlay inlay) { - inlay.putUserData(PINNED, null); - } - public String getHintText(@NotNull Inlay inlay) { EditorCustomElementRenderer renderer = inlay.getRenderer(); return renderer instanceof MyRenderer ? ((MyRenderer)renderer).getText() : null; } - public Inlay addHint(@NotNull Editor editor, int offset, @NotNull String hintText, boolean useAnimation, boolean pinned) { + public Inlay addHint(@NotNull Editor editor, int offset, @NotNull String hintText, boolean useAnimation) { MyRenderer renderer = new MyRenderer(editor, hintText, useAnimation); Inlay inlay = editor.getInlayModel().addInlineElement(offset, renderer); if (inlay != null) { - if (pinned) inlay.putUserData(PINNED, Boolean.TRUE); if (useAnimation) scheduleRendererUpdate(editor, inlay); } return inlay; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hints/ParameterHintsPass.java b/platform/lang-impl/src/com/intellij/codeInsight/hints/ParameterHintsPass.java index 70f911dab9cd..45c0f4d78bb1 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hints/ParameterHintsPass.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/hints/ParameterHintsPass.java @@ -23,6 +23,7 @@ import com.intellij.openapi.editor.Inlay; import com.intellij.openapi.editor.ex.EditorSettingsExternalizable; import com.intellij.openapi.editor.ex.util.CaretVisualPositionKeeper; import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.util.ProgressIndicatorBase; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.SyntaxTraverser; @@ -30,25 +31,36 @@ import com.intellij.util.containers.ContainerUtil; import gnu.trove.TIntObjectHashMap; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; import static com.intellij.codeInsight.hints.ParameterHintsPassFactory.putCurrentPsiModificationStamp; public class ParameterHintsPass extends EditorBoundHighlightingPass { - private final TIntObjectHashMap myHints = new TIntObjectHashMap<>(); + private final TIntObjectHashMap> myHints = new TIntObjectHashMap<>(); private final TIntObjectHashMap myShowOnlyIfExistedBeforeHints = new TIntObjectHashMap<>(); private final SyntaxTraverser myTraverser; private final PsiElement myRootElement; private final HintInfoFilter myHintInfoFilter; + private final boolean myForceImmediateUpdate; + + public static void syncUpdate(@NotNull PsiElement element, @NotNull Editor editor) { + MethodInfoBlacklistFilter filter = MethodInfoBlacklistFilter.forLanguage(element.getLanguage()); + ParameterHintsPass pass = new ParameterHintsPass(element, editor, filter, true); + pass.doCollectInformation(new ProgressIndicatorBase()); + pass.applyInformationToEditor(); + } public ParameterHintsPass(@NotNull PsiElement element, @NotNull Editor editor, - @NotNull HintInfoFilter hintsFilter) { + @NotNull HintInfoFilter hintsFilter, + boolean forceImmediateUpdate) { super(editor, element.getContainingFile(), true); myRootElement = element; myTraverser = SyntaxTraverser.psiTraverser(element); myHintInfoFilter = hintsFilter; + myForceImmediateUpdate = forceImmediateUpdate; } @Override @@ -81,13 +93,17 @@ public class ParameterHintsPass extends EditorBoundHighlightingPass { } inlays.forEach((hint) -> { - String presentation = provider.getInlayPresentation(hint.getText()); int offset = hint.getOffset(); + TextRange rootRange = myRootElement.getTextRange(); + if (offset <= rootRange.getStartOffset() || offset >= rootRange.getEndOffset()) return; + String presentation = provider.getInlayPresentation(hint.getText()); if (hint.isShowOnlyIfExistedBefore()) { myShowOnlyIfExistedBeforeHints.put(offset, presentation); } else { - myHints.put(offset, presentation); + List hintList = myHints.get(offset); + if (hintList == null) myHints.put(offset, hintList = new ArrayList<>()); + hintList.add(new HintData(presentation, hint.getShowAfterCaret())); } }); } @@ -97,7 +113,7 @@ public class ParameterHintsPass extends EditorBoundHighlightingPass { CaretVisualPositionKeeper keeper = new CaretVisualPositionKeeper(myEditor); ParameterHintsPresentationManager manager = ParameterHintsPresentationManager.getInstance(); List hints = hintsInRootElementArea(manager); - ParameterHintsUpdater updater = new ParameterHintsUpdater(myEditor, hints, myHints, myShowOnlyIfExistedBeforeHints); + ParameterHintsUpdater updater = new ParameterHintsUpdater(myEditor, hints, myHints, myShowOnlyIfExistedBeforeHints, myForceImmediateUpdate); updater.update(); keeper.restoreOriginalLocation(false); putCurrentPsiModificationStamp(myEditor, myFile); @@ -112,8 +128,18 @@ public class ParameterHintsPass extends EditorBoundHighlightingPass { int elementEnd = range.getEndOffset(); List inlays = myEditor.getInlayModel() - .getInlineElementsInRange(elementStart, elementEnd); + .getInlineElementsInRange(elementStart + 1, elementEnd - 1); return ContainerUtil.filter(inlays, (hint) -> manager.isParameterHint(hint)); } + + public static class HintData { + public final String presentationText; + public final boolean showAfterCaret; + + public HintData(String text, boolean afterCaret) { + presentationText = text; + showAfterCaret = afterCaret; + } + } } \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hints/ParameterHintsPassFactory.java b/platform/lang-impl/src/com/intellij/codeInsight/hints/ParameterHintsPassFactory.java index 0680bd085542..b2980c264a6e 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hints/ParameterHintsPassFactory.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/hints/ParameterHintsPassFactory.java @@ -46,7 +46,7 @@ public class ParameterHintsPassFactory extends AbstractProjectComponent implemen Language language = file.getLanguage(); InlayParameterHintsProvider provider = InlayParameterHintsExtension.INSTANCE.forLanguage(language); if (provider == null) return null; - return new ParameterHintsPass(file, editor, MethodInfoBlacklistFilter.forLanguage(language)); + return new ParameterHintsPass(file, editor, MethodInfoBlacklistFilter.forLanguage(language), false); } public static long getCurrentModificationStamp(@NotNull PsiFile file) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hints/ParameterHintsUpdater.java b/platform/lang-impl/src/com/intellij/codeInsight/hints/ParameterHintsUpdater.java index f863168b0dec..24f1051e2f6a 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hints/ParameterHintsUpdater.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/hints/ParameterHintsUpdater.java @@ -27,10 +27,7 @@ import gnu.trove.TIntObjectHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.stream.Stream; public class ParameterHintsUpdater { @@ -40,19 +37,22 @@ public class ParameterHintsUpdater { private final ParameterHintsPresentationManager myHintsManager = ParameterHintsPresentationManager.getInstance(); private final TIntObjectHashMap myCaretMap; - private final TIntObjectHashMap myNewHints; + private final TIntObjectHashMap> myNewHints; private final TIntObjectHashMap myHintsToPreserve; + private final boolean myForceImmediateUpdate; private final Editor myEditor; private final List myUpdateList; public ParameterHintsUpdater(@NotNull Editor editor, @NotNull List inlays, - @NotNull TIntObjectHashMap newHints, - @NotNull TIntObjectHashMap hintsToPreserve) { + @NotNull TIntObjectHashMap> newHints, + @NotNull TIntObjectHashMap hintsToPreserve, + boolean forceImmediateUpdate) { myEditor = editor; myNewHints = newHints; myHintsToPreserve = hintsToPreserve; + myForceImmediateUpdate = forceImmediateUpdate; myCaretMap = new TIntObjectHashMap<>(); List allCarets = myEditor.getCaretModel().getAllCarets(); @@ -64,20 +64,50 @@ public class ParameterHintsUpdater { private List getInlayUpdates(List editorHints) { List updates = ContainerUtil.newArrayList(); + ParameterHintsPresentationManager presentationManager = ParameterHintsPresentationManager.getInstance(); editorHints.forEach(editorHint -> { int offset = editorHint.getOffset(); - String newText = myNewHints.remove(offset); - if (delayRemoval(editorHint) || myHintsManager.isPinned(editorHint) || isPreserveHint(editorHint, newText)) return; - updates.add(new InlayUpdateInfo(offset, editorHint, newText)); + String presentationText = presentationManager.getHintText(editorHint); + ParameterHintsPass.HintData newHint = findAndRemoveMatchingHint(offset, presentationText, myNewHints); + String newText = newHint == null ? null : newHint.presentationText; + if (!myForceImmediateUpdate && delayRemoval(editorHint) || isPreserveHint(editorHint, newText)) return; + updates.add(new InlayUpdateInfo(offset, editorHint, newText, newHint != null && newHint.showAfterCaret)); }); - Arrays.stream(myNewHints.keys()).forEach((offset) -> updates.add(new InlayUpdateInfo(offset, null, myNewHints.get(offset)))); + Arrays.stream(myNewHints.keys()).forEach((offset) -> { + for (ParameterHintsPass.HintData hint : myNewHints.get(offset)) { + updates.add(new InlayUpdateInfo(offset, null, hint.presentationText, hint.showAfterCaret)); + } + }); updates.sort(Comparator.comparing((update) -> update.offset)); return updates; } + @Nullable + private static ParameterHintsPass.HintData findAndRemoveMatchingHint(int offset, String presentationText, + TIntObjectHashMap> data) { + List newHintList = data.get(offset); + ParameterHintsPass.HintData newHint = null; + if (newHintList != null) { + ParameterHintsPass.HintData lastHint = null; + for (Iterator iterator = newHintList.iterator(); iterator.hasNext(); ) { + lastHint = iterator.next(); + if (Objects.equals(lastHint.presentationText, presentationText)) { + newHint = lastHint; + iterator.remove(); + break; + } + } + if (newHint == null && lastHint != null) { + newHint = lastHint; + newHintList.remove(lastHint); + } + if (newHintList.isEmpty()) data.remove(offset); + } + return newHint; + } private boolean isPreserveHint(@NotNull Inlay inlay, @Nullable String newText) { if (newText == null) { @@ -103,11 +133,17 @@ public class ParameterHintsUpdater { InlayUpdateInfo.Action action = info.action(); if (action == InlayUpdateInfo.Action.ADD) { - boolean useAnimation = !firstTime && !isSameHintRemovedNear(newText, infoIndex) && !isInBulkMode; - myHintsManager.addHint(myEditor, info.offset, newText, useAnimation, false); + boolean useAnimation = !myForceImmediateUpdate && !firstTime && !isSameHintRemovedNear(newText, infoIndex) && !isInBulkMode; + Inlay inlay = myHintsManager.addHint(myEditor, info.offset, newText, useAnimation); + if (inlay != null) { + VisualPosition inlayPosition = inlay.getVisualPosition(); + VisualPosition visualPosition = new VisualPosition(inlayPosition.line, inlayPosition.column + (info.showAfterCaret ? 1 : 0)); + Caret caret = myEditor.getCaretModel().getCaretAt(visualPosition); + if (caret != null) caret.moveToVisualPosition(new VisualPosition(inlayPosition.line, inlayPosition.column + (info.showAfterCaret ? 0 : 1))); + } } else if (action == InlayUpdateInfo.Action.DELETE) { - boolean useAnimation = oldText != null && !isSameHintAddedNear(oldText, infoIndex) && !isInBulkMode; + boolean useAnimation = !myForceImmediateUpdate && oldText != null && !isSameHintAddedNear(oldText, infoIndex) && !isInBulkMode; myHintsManager.deleteHint(myEditor, info.inlay, useAnimation); } else if (action == InlayUpdateInfo.Action.REPLACE) { @@ -162,12 +198,14 @@ public class ParameterHintsUpdater { public final Inlay inlay; public final String newText; public final String oldText; + public final boolean showAfterCaret; - public InlayUpdateInfo(int offset, @Nullable Inlay current, @Nullable String newText) { + public InlayUpdateInfo(int offset, @Nullable Inlay current, @Nullable String newText, boolean showAfterCaret) { this.offset = offset; this.inlay = current; this.newText = newText; this.oldText = getHintText(); + this.showAfterCaret = showAfterCaret; } public Action action() {