From d96d1e00d15d2ae6bdc4ffe3be494e2800b618af Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Wed, 27 Dec 2017 10:56:31 +0300 Subject: [PATCH] highlight completion hints of currently filled method call for IDEA-180808 --- .../api/impls/MethodParameterInfoHandler.java | 119 ++++++++++----- .../completion/CompletionHintsTest.java | 144 +++++++++--------- .../highlighting/InlineElementData.java | 15 +- .../ParameterHintsPresentationManager.java | 26 +++- .../pages/DefaultLanguageColorsPage.java | 17 +-- .../utils/inlays/InlayParameterHintsTest.kt | 45 +++--- 6 files changed, 200 insertions(+), 166 deletions(-) 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 9a4df56ee9dc..f897f6009016 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 @@ -39,13 +39,11 @@ import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.MethodSignatureUtil; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; +import java.util.*; /** * @author Maxim.Mossienko @@ -55,8 +53,10 @@ public class MethodParameterInfoHandler implements ParameterInfoHandlerWithTabAc PsiMethodCallExpression.class, PsiNewExpression.class, PsiAnonymousClass.class, PsiEnumConstant.class); private static final Set ourStopSearch = Collections.singleton(PsiMethod.class); - - private Inlay myHighlightedHint; + private static final String WHITESPACE = " \t"; + + private Inlay myCurrentHint; + private List myHighlightedHints; @Override public Object[] getParametersForLookup(LookupElement item, ParameterInfoContext context) { @@ -337,51 +337,86 @@ public class MethodParameterInfoHandler implements ParameterInfoHandlerWithTabAc private void highlightHints(@NotNull Editor editor, @Nullable PsiExpressionList expressionList, int currentHintIndex) { if (editor.isDisposed()) return; ParameterHintsPresentationManager presentationManager = ParameterHintsPresentationManager.getInstance(); - Inlay hint = null; - if (expressionList != null && expressionList.isValid() && - currentHintIndex >= 0 && (currentHintIndex < expressionList.getExpressions().length || - currentHintIndex == 0 && expressionList.getExpressions().length == 0)) { - PsiElement prevDelimiter, nextDelimiter; - if (currentHintIndex < expressionList.getExpressions().length) { - PsiExpression expression = expressionList.getExpressions()[currentHintIndex]; - //noinspection StatementWithEmptyBody - for (prevDelimiter = expression; - prevDelimiter != null && !(prevDelimiter instanceof PsiJavaToken); - prevDelimiter = prevDelimiter.getPrevSibling()) - ; - //noinspection StatementWithEmptyBody - for (nextDelimiter = expression; - nextDelimiter != null && !(nextDelimiter instanceof PsiJavaToken); - nextDelimiter = nextDelimiter.getNextSibling()) - ; - } - else { - prevDelimiter = expressionList.getFirstChild(); // left parenthesis - nextDelimiter = expressionList.getLastChild(); // right parenthesis - } - if (prevDelimiter != null && nextDelimiter != null) { + Inlay currentHint = null; + List highlightedHints = null; + if (expressionList != null && expressionList.isValid()) { + int expressionCount = expressionList.getExpressions().length; + if (currentHintIndex == 0 || currentHintIndex > 0 && currentHintIndex < expressionCount) { + highlightedHints = new ArrayList<>(expressionCount); ParameterHintsPass.syncUpdate(expressionList.getParent(), editor); - for (Inlay inlay : editor.getInlayModel().getInlineElementsInRange(prevDelimiter.getTextRange().getEndOffset(), - nextDelimiter.getTextRange().getStartOffset())) { - if (presentationManager.isParameterHint(inlay)) { - hint = inlay; - break; + PsiElement prevDelimiter, nextDelimiter; + for (int i = 0; i < Math.max(expressionCount, currentHintIndex == 0 ? 1 : 0); i++) { + if (i < expressionCount) { + PsiExpression expression = expressionList.getExpressions()[i]; + //noinspection StatementWithEmptyBody + for (prevDelimiter = expression; + prevDelimiter != null && !(prevDelimiter instanceof PsiJavaToken); + prevDelimiter = prevDelimiter.getPrevSibling()) + ; + //noinspection StatementWithEmptyBody + for (nextDelimiter = expression; + nextDelimiter != null && !(nextDelimiter instanceof PsiJavaToken); + nextDelimiter = nextDelimiter.getNextSibling()) + ; + } + else { + prevDelimiter = expressionList.getFirstChild(); // left parenthesis + nextDelimiter = expressionList.getLastChild(); // right parenthesis + } + if (prevDelimiter != null && nextDelimiter != null) { + CharSequence text = editor.getDocument().getImmutableCharSequence(); + int firstRangeStartOffset = prevDelimiter.getTextRange().getEndOffset(); + int firstRangeEndOffset = CharArrayUtil.shiftForward(text, firstRangeStartOffset, WHITESPACE); + for (Inlay inlay : editor.getInlayModel().getInlineElementsInRange(firstRangeStartOffset, firstRangeEndOffset)) { + if (presentationManager.isParameterHint(inlay)) { + highlightedHints.add(inlay); + if (i == currentHintIndex && currentHint == null) currentHint = inlay; + } + } + int secondRangeEndOffset = nextDelimiter.getTextRange().getStartOffset(); + if (secondRangeEndOffset > firstRangeEndOffset) { + int secondRangeStartOffset = CharArrayUtil.shiftBackward(text, secondRangeEndOffset - 1, WHITESPACE) + 1; + for (Inlay inlay : editor.getInlayModel().getInlineElementsInRange(secondRangeStartOffset, secondRangeEndOffset)) { + if (presentationManager.isParameterHint(inlay)) { + highlightedHints.add(inlay); + } + } + } } } } } - if (hint == myHighlightedHint) return; - if (myHighlightedHint != null && myHighlightedHint.isValid()) presentationManager.setHighlighted(myHighlightedHint, false); - myHighlightedHint = hint; - if (myHighlightedHint != null && myHighlightedHint.isValid()) presentationManager.setHighlighted(myHighlightedHint, true); + if (currentHint == myCurrentHint && Objects.equals(highlightedHints, myHighlightedHints)) return; + resetHints(); + if (currentHint != null) { + presentationManager.setCurrent(currentHint, true); + myCurrentHint = currentHint; + } + if (!ContainerUtil.isEmpty(highlightedHints)) { + for (Inlay highlightedHint : highlightedHints) { + presentationManager.setHighlighted(highlightedHint, true); + } + myHighlightedHints = highlightedHints; + } + } + + private void resetHints() { + ParameterHintsPresentationManager presentationManager = ParameterHintsPresentationManager.getInstance(); + if (myCurrentHint != null) { + presentationManager.setCurrent(myCurrentHint, false); + myCurrentHint = null; + } + if (myHighlightedHints != null) { + for (Inlay hint : myHighlightedHints) { + presentationManager.setHighlighted(hint, false); + } + myHighlightedHints = null; + } } @Override public void dispose() { - if (myHighlightedHint != null) { - if (myHighlightedHint.isValid()) ParameterHintsPresentationManager.getInstance().setHighlighted(myHighlightedHint, false); - myHighlightedHint = null; - } + resetHints(); } private static PsiSubstitutor getCandidateInfoSubstitutor(PsiElement argList, CandidateInfo candidate, boolean resolveResult) { 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 edac1c52cadc..1f646c176cc2 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 @@ -60,26 +60,26 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { // check hints appearance on completion configureJava("class C { void m() { System.setPro } }"); complete("setProperty"); - checkResultWithInlays("class C { void m() { System.setProperty(, ) } }"); + checkResultWithInlays("class C { void m() { System.setProperty(, ) } }"); // check that hints don't disappear after daemon highlighting passes waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.setProperty(, ) } }"); + checkResultWithInlays("class C { void m() { System.setProperty(, ) } }"); // test Tab/Shift+Tab navigation next(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.setProperty(, ) } }"); + checkResultWithInlays("class C { void m() { System.setProperty(, ) } }"); prev(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.setProperty(, ) } }"); + checkResultWithInlays("class C { void m() { System.setProperty(, ) } }"); // test hints remain shown while entering parameter values myFixture.type("\"a"); next(); myFixture.type("\"b"); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.setProperty(\"a\", \"b\") } }"); + checkResultWithInlays("class C { void m() { System.setProperty(\"a\", \"b\") } }"); // test hints disappearance when caret moves out of parameter list right(); @@ -95,26 +95,26 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { // check hints appearance on completion configureJava("class C { void m() { Character.for } }"); complete("forDigit"); - checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); + checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); // check that hints don't disappear after daemon highlighting passes waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); + checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); // test Tab/Shift+Tab navigation next(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); + checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); prev(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); + checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); // test hints remain shown while entering parameter values myFixture.type("1"); next(); myFixture.type("2"); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { Character.forDigit(1, 2) } }"); + checkResultWithInlays("class C { void m() { Character.forDigit(1, 2) } }"); // test hints don't disappear when caret moves out of parameter list right(); @@ -133,10 +133,10 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { configureJava("class C { void m() { Character.for } }"); complete("forDigit"); - checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); + checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); + checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); } finally { option.set(savedValue); @@ -150,7 +150,7 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { showParameterInfo(); methodOverloadDown(); checkResultWithInlays( - "class C { void m() { Character.toChars(, , ) } }"); + "class C { void m() { Character.toChars(, , ) } }"); } public void testSwitchingOverloadsWithParameterFilled() { @@ -160,7 +160,7 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { checkResultWithInlays("class C { void m() { Character.toChars(123) } }"); showParameterInfo(); methodOverloadDown(); - checkResultWithInlays("class C { void m() { Character.toChars(123, , ) } }"); + checkResultWithInlays("class C { void m() { Character.toChars(123, , ) } }"); } public void testSwitchingOverloadsForMethodWithDisabledHints() throws Exception { @@ -173,7 +173,7 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { checkResultWithInlays("class C {\n" + " int some(int from, int to) { return 0; }\n" + " int some(int from, int to, int other) { return 0; }\n" + - " void m() { some(, , ) }\n" + + " void m() { some(, , ) }\n" + "}"); showParameterInfo(); checkHintContents("int from, int to\n" + @@ -184,7 +184,7 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { checkResultWithInlays("class C {\n" + " int some(int from, int to) { return 0; }\n" + " int some(int from, int to, int other) { return 0; }\n" + - " void m() { some(, ) }\n" + + " void m() { some(, ) }\n" + "}"); checkHintContents("[int from, int to]\n" + "-\n" + @@ -211,11 +211,11 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { configureJava("class C { void m() { System.setPro } }"); complete("setProperty"); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.setProperty(, ) } }"); + checkResultWithInlays("class C { void m() { System.setProperty(, ) } }"); myFixture.type("System.getPro"); complete("getProperty(String key, String def)"); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.setProperty(System.getProperty(, ), ) } }"); + checkResultWithInlays("class C { void m() { System.setProperty(System.getProperty(, ), ) } }"); } public void testTabWithNestedCompletion() throws Exception { @@ -225,16 +225,16 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { myFixture.type("System.getPro"); complete("getProperty(String key, String def)"); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.setProperty(System.getProperty(, ), ) } }"); + checkResultWithInlays("class C { void m() { System.setProperty(System.getProperty(, ), ) } }"); next(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.setProperty(System.getProperty(, ), ) } }"); + checkResultWithInlays("class C { void m() { System.setProperty(System.getProperty(, ), ) } }"); next(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.setProperty(System.getProperty(, ), ) } }"); + checkResultWithInlays("class C { void m() { System.setProperty(System.getProperty(, ), ) } }"); next(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.setProperty(System.getProperty(, ), ) } }"); + checkResultWithInlays("class C { void m() { System.setProperty(System.getProperty(, ), ) } }"); next(); waitForAllAsyncStuff(); checkResultWithInlays("class C { void m() { System.setProperty(System.getProperty(, ), ) } }"); @@ -249,7 +249,7 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { public void testHintsDisappearWhenNumberOfParametersIsChangedDirectly() throws Exception { configureJava("class C { void m() { System.getPro } }"); complete("getProperty(String key, String def)"); - checkResultWithInlays("class C { void m() { System.getProperty(, ) } }"); + checkResultWithInlays("class C { void m() { System.getProperty(, ) } }"); delete(); waitForAllAsyncStuff(); checkResultWithInlays("class C { void m() { System.getProperty( ) } }"); @@ -258,7 +258,7 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { public void testHintsDisappearWhenNumberOfParametersIsChangedDirectlyWithNoOverloads() throws Exception { configureJava("class C { void m() { Character.for } }"); complete("forDigit"); - checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); + checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); delete(); waitForAllAsyncStuff(); checkResultWithInlays("class C { void m() { Character.forDigit( ) } }"); @@ -270,7 +270,7 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { type("new String().trim"); myFixture.complete(CompletionType.SMART); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.setProperty(new String().trim(), ) } }"); + checkResultWithInlays("class C { void m() { System.setProperty(new String().trim(), ) } }"); } public void testNoHintsDuplicationWhenTypingToTheLeftOfHint() throws Exception { @@ -281,7 +281,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 ) } }"); @@ -334,7 +334,7 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { next(); prev(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.getProperty(, ) } }"); + checkResultWithInlays("class C { void m() { System.getProperty(, ) } }"); } public void testPrevParameterFromOutsideWhenParametersAreNotEmpty() throws Exception { @@ -346,26 +346,26 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { next(); prev(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.getProperty(\"a\", \"b\") } }"); + checkResultWithInlays("class C { void m() { System.getProperty(\"a\", \"b\") } }"); } public void testVararg() throws Exception { configureJava("class C { void m() { String.for } }"); complete(); - checkResultWithInlays("class C { void m() { String.format() } }"); + checkResultWithInlays("class C { void m() { String.format() } }"); type("\"a"); next(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { String.format(\"a\", ) } }"); + checkResultWithInlays("class C { void m() { String.format(\"a\", ) } }"); next(); waitForAllAsyncStuff(); checkResultWithInlays("class C { void m() { String.format(\"a\") } }"); prev(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { String.format(\"a\", ) } }"); + checkResultWithInlays("class C { void m() { String.format(\"a\", ) } }"); prev(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { String.format(\"a\") } }"); + checkResultWithInlays("class C { void m() { String.format(\"a\") } }"); } public void testVarargWithNoMandatoryArguments() throws Exception { @@ -377,7 +377,7 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { type("1"); next(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { int vararg(int... args){ return 0; } void m() { vararg(1, ) } }"); + checkResultWithInlays("class C { int vararg(int... args){ return 0; } void m() { vararg(1, ) } }"); checkHintContents("int..."); next(); waitForAllAsyncStuff(); @@ -385,7 +385,7 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { checkHintContents(null); prev(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { int vararg(int... args){ return 0; } void m() { vararg(1, ) } }"); + checkResultWithInlays("class C { int vararg(int... args){ return 0; } void m() { vararg(1, ) } }"); checkHintContents("int..."); prev(); waitForAllAsyncStuff(); @@ -396,45 +396,45 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { public void testVarargWithTwoMandatoryArguments() throws Exception { configureJava("class C { int vararg(int a, int b, int... args){ return 0; } void m() { varar } }"); complete(); - checkResultWithInlays("class C { int vararg(int a, int b, int... args){ return 0; } void m() { vararg(, ) } }"); + checkResultWithInlays("class C { int vararg(int a, int b, int... args){ return 0; } void m() { vararg(, ) } }"); type("1"); next(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { int vararg(int a, int b, int... args){ return 0; } void m() { vararg(1, ) } }"); + checkResultWithInlays("class C { int vararg(int a, int b, int... args){ return 0; } void m() { vararg(1, ) } }"); type("2"); next(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { int vararg(int a, int b, int... args){ return 0; } void m() { vararg(1, 2, ) } }"); + checkResultWithInlays("class C { int vararg(int a, int b, int... args){ return 0; } void m() { vararg(1, 2, ) } }"); next(); waitForAllAsyncStuff(); checkResultWithInlays("class C { int vararg(int a, int b, int... args){ return 0; } void m() { vararg(1, 2) } }"); prev(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { int vararg(int a, int b, int... args){ return 0; } void m() { vararg(1, 2, ) } }"); + checkResultWithInlays("class C { int vararg(int a, int b, int... args){ return 0; } void m() { vararg(1, 2, ) } }"); prev(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { int vararg(int a, int b, int... args){ return 0; } void m() { vararg(1, 2) } }"); + checkResultWithInlays("class C { int vararg(int a, int b, int... args){ return 0; } void m() { vararg(1, 2) } }"); } public void testVarargHintsDontSwitchPlaces() throws Exception { configureJava("class C { void m() { java.util.Collections.add } }"); complete(); - checkResultWithInlays("class C { void m() { java.util.Collections.addAll() } }"); + checkResultWithInlays("class C { void m() { java.util.Collections.addAll() } }"); left(); type('s'); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { java.util.Collections.addAll(s) } }"); + checkResultWithInlays("class C { void m() { java.util.Collections.addAll(s) } }"); backspace(); - checkResultWithInlays("class C { void m() { java.util.Collections.addAll() } }"); + checkResultWithInlays("class C { void m() { java.util.Collections.addAll() } }"); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { java.util.Collections.addAll() } }"); + checkResultWithInlays("class C { void m() { java.util.Collections.addAll() } }"); } public void testHintsDontDisappearWhenNavigatingAwayFromUncompletedInvocation() throws Exception { configureJava("class C { void m() { System.setPro } }"); complete("setProperty"); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.setProperty(, ) } }"); + checkResultWithInlays("class C { void m() { System.setProperty(, ) } }"); home(); type(' '); waitForAllAsyncStuff(); @@ -465,7 +465,7 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { "}\n" + "class C extends P {\n" + " void method(int a, int b) {\n" + - " super.method(, );\n" + + " super.method(, );\n" + " }\n" + "}"); complete("a, b"); @@ -489,26 +489,26 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { // check hints appearance on completion configureJava("class C { void m() { Character.for } }"); complete("forDigit"); - checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); + checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); // check that hints don't disappear after daemon highlighting passes waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); + checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); // test Tab/Shift+Tab navigation next(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); + checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); prev(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); + checkResultWithInlays("class C { void m() { Character.forDigit(, ) } }"); // test hints remain shown while entering parameter values myFixture.type("1"); next(); myFixture.type("2"); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { Character.forDigit(1, 2) } }"); + checkResultWithInlays("class C { void m() { Character.forDigit(1, 2) } }"); // test hints disappear when caret moves out of parameter list right(); @@ -531,7 +531,7 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { complete(); checkResultWithInlays("class C {\n" + " void mmm(int a, int b, int c, int d, int e, int f) {}\n" + - " void m2() { mmm(, , , , , ); }\n" + + " void m2() { mmm(, , , , , ); }\n" + "}"); } @@ -542,27 +542,27 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { myFixture.type("System.getPro"); complete("getProperty(String key, String def)"); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.setProperty(System.getProperty(, ), ) } }"); + checkResultWithInlays("class C { void m() { System.setProperty(System.getProperty(, ), ) } }"); next(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.setProperty(System.getProperty(, ), ) } }"); + checkResultWithInlays("class C { void m() { System.setProperty(System.getProperty(, ), ) } }"); next(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.setProperty(System.getProperty(, ), ) } }"); + checkResultWithInlays("class C { void m() { System.setProperty(System.getProperty(, ), ) } }"); left(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.setProperty(System.getProperty(, ), ) } }"); + checkResultWithInlays("class C { void m() { System.setProperty(System.getProperty(, ), ) } }"); } public void testHintPopupContentsForMethodWithOverloads() throws Exception { configureJava("class C { void m() { System.getPro } }"); complete("getProperty(String key, String def)"); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.getProperty(, ) } }"); + checkResultWithInlays("class C { void m() { System.getProperty(, ) } }"); checkHintContents("@NotNull String  the name of the system property. "); next(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.getProperty(, ) } }"); + checkResultWithInlays("class C { void m() { System.getProperty(, ) } }"); checkHintContents("String  a default value. "); showParameterInfo(); waitForAllAsyncStuff(); @@ -575,11 +575,11 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { configureJava("class C { void m() { System.setPro } }"); complete("setProperty"); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.setProperty(, ) } }"); + checkResultWithInlays("class C { void m() { System.setProperty(, ) } }"); checkHintContents("@NotNull String  the name of the system property. "); next(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { System.setProperty(, ) } }"); + checkResultWithInlays("class C { void m() { System.setProperty(, ) } }"); checkHintContents("String  the value of the system property. "); showParameterInfo(); waitForAllAsyncStuff(); @@ -651,14 +651,14 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { checkResultWithInlays("class C {\n" + " void a(int p, int q) {}\n" + " void a(int p, int q, int r) {}\n" + - " void m() { a(, ) }\n" + + " void m() { a(, ) }\n" + "}"); next(); waitForAllAsyncStuff(); checkResultWithInlays("class C {\n" + " void a(int p, int q) {}\n" + " void a(int p, int q, int r) {}\n" + - " void m() { a(, ) }\n" + + " void m() { a(, ) }\n" + "}"); } @@ -712,7 +712,7 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { assertEquals("local", myFixture.getLookupElements()[0].getLookupString()); myFixture.performEditorAction(IdeActions.ACTION_CHOOSE_LOOKUP_ITEM_REPLACE); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void m() { String local = \"a\"; String local2 = \"b\"; System.getProperty(local, ) } }"); + checkResultWithInlays("class C { void m() { String local = \"a\"; String local2 = \"b\"; System.getProperty(local, ) } }"); } public void testGenericType() throws Exception { @@ -725,19 +725,19 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { public void testCompletionBetweenVarargHints() { configureJava("class C { int myVal = 1; void vararg(int a, int... b) {} void m() { varar } }"); complete(); - checkResultWithInlays("class C { int myVal = 1; void vararg(int a, int... b) {} void m() { vararg(); } }"); + checkResultWithInlays("class C { int myVal = 1; void vararg(int a, int... b) {} void m() { vararg(); } }"); type("myVa"); complete(); - checkResultWithInlays("class C { int myVal = 1; void vararg(int a, int... b) {} void m() { vararg(myVal); } }"); + checkResultWithInlays("class C { int myVal = 1; void vararg(int a, int... b) {} void m() { vararg(myVal); } }"); } public void testEnteringSpaceBetweenVarargHints() throws Exception { configureJava("class C { void vararg(Object a, int... b) {} void m() { varar } }"); complete(); - checkResultWithInlays("class C { void vararg(Object a, int... b) {} void m() { vararg(); } }"); + checkResultWithInlays("class C { void vararg(Object a, int... b) {} void m() { vararg(); } }"); type("new "); waitForAllAsyncStuff(); - checkResultWithInlays("class C { void vararg(Object a, int... b) {} void m() { vararg(new ); } }"); + checkResultWithInlays("class C { void vararg(Object a, int... b) {} void m() { vararg(new ); } }"); } public void testNoTooltipForInvalidParameter() throws Exception { @@ -788,7 +788,7 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { public void testNoLinksInParameterJavadoc() throws Exception { configureJava("class C { void m() { String.for } }"); complete(); - checkResultWithInlays("class C { void m() { String.format() } }"); + checkResultWithInlays("class C { void m() { String.format() } }"); waitForAllAsyncStuff(); checkHintContents("String   A format string "); } @@ -799,25 +799,25 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase { // check hints appearance on completion configureJava("class C { C(int a, int b) {} void m() { new C } }"); complete("C(int a, int b)"); - checkResultWithInlays("class C { C(int a, int b) {} void m() { new C(, ) } }"); + checkResultWithInlays("class C { C(int a, int b) {} void m() { new C(, ) } }"); // check that hints don't disappear after daemon highlighting passes waitForAllAsyncStuff(); - checkResultWithInlays("class C { C(int a, int b) {} void m() { new C(, ) } }"); + checkResultWithInlays("class C { C(int a, int b) {} void m() { new C(, ) } }"); // test Tab/Shift+Tab navigation next(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { C(int a, int b) {} void m() { new C(, ) } }"); + checkResultWithInlays("class C { C(int a, int b) {} void m() { new C(, ) } }"); prev(); waitForAllAsyncStuff(); - checkResultWithInlays("class C { C(int a, int b) {} void m() { new C(, ) } }"); + checkResultWithInlays("class C { C(int a, int b) {} void m() { new C(, ) } }"); // test hints remain shown while entering parameter values myFixture.type("1"); next(); myFixture.type("2"); waitForAllAsyncStuff(); - checkResultWithInlays("class C { C(int a, int b) {} void m() { new C(1, 2) } }"); + checkResultWithInlays("class C { C(int a, int b) {} void m() { new C(1, 2) } }"); // test hints don't disappear when caret moves out of parameter list right(); diff --git a/platform/lang-impl/src/com/intellij/application/options/colors/highlighting/InlineElementData.java b/platform/lang-impl/src/com/intellij/application/options/colors/highlighting/InlineElementData.java index 5d1ef52e7e47..c6f912a930b1 100644 --- a/platform/lang-impl/src/com/intellij/application/options/colors/highlighting/InlineElementData.java +++ b/platform/lang-impl/src/com/intellij/application/options/colors/highlighting/InlineElementData.java @@ -1,17 +1,5 @@ /* - * 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2000-2017 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.application.options.colors.highlighting; @@ -58,6 +46,7 @@ public class InlineElementData extends HighlightData { Inlay hint = hintsPresentationManager.addHint(view, offset, false, myText, false); hintsPresentationManager.setHighlighted(hint, DefaultLanguageHighlighterColors.INLINE_PARAMETER_HINT_HIGHLIGHTED.equals(getHighlightKey())); + hintsPresentationManager.setCurrent(hint, myText.contains("current")); 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 be334861dc7f..1600078ccfd1 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 @@ -98,6 +98,22 @@ public class ParameterHintsPresentationManager implements Disposable { return renderer.highlighted; } + public void setCurrent(@NotNull Inlay hint, boolean current) { + if (!isParameterHint(hint)) throw new IllegalArgumentException("Not a parameter hint"); + MyRenderer renderer = (MyRenderer)hint.getRenderer(); + boolean oldValue = renderer.current; + if (current != oldValue) { + renderer.current = current; + hint.repaint(); + } + } + + public boolean isCurrent(@NotNull Inlay hint) { + if (!isParameterHint(hint)) throw new IllegalArgumentException("Not a parameter hint"); + MyRenderer renderer = (MyRenderer)hint.getRenderer(); + return renderer.current; + } + private void updateRenderer(@NotNull Editor editor, @NotNull Inlay hint, @Nullable String newText) { MyRenderer renderer = (MyRenderer)hint.getRenderer(); renderer.update(editor, newText, true); @@ -186,6 +202,7 @@ public class ParameterHintsPresentationManager implements Disposable { private int steps; private int step; private boolean highlighted; + private boolean current; private MyRenderer(Editor editor, String text, boolean animated) { updateState(editor, text, animated); @@ -241,12 +258,12 @@ public class ParameterHintsPresentationManager implements Disposable { : DefaultLanguageHighlighterColors.INLINE_PARAMETER_HINT); if (attributes != null) { MyFontMetrics fontMetrics = getFontMetrics(editor); + int gap = r.height < (fontMetrics.lineHeight + 2) ? 1 : 2; Color backgroundColor = attributes.getBackgroundColor(); if (backgroundColor != null) { GraphicsConfig config = GraphicsUtil.setupAAPainting(g); GraphicsUtil.paintWithAlpha(g, BACKGROUND_ALPHA); g.setColor(backgroundColor); - int gap = r.height < (fontMetrics.lineHeight + 2) ? 1 : 2; g.fillRoundRect(r.x + 2, r.y + gap, r.width - 4, r.height - gap * 2, 8, 8); config.restore(); } @@ -264,6 +281,13 @@ public class ParameterHintsPresentationManager implements Disposable { g.setClip(savedClip); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, savedHint); + + if (current) { + savedHint = g2d.getRenderingHint(RenderingHints.KEY_ANTIALIASING); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.drawRoundRect(r.x + 2, r.y + gap, r.width - 4, r.height - gap * 2, 8, 8); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, savedHint); + } } } } diff --git a/platform/lang-impl/src/com/intellij/openapi/options/colors/pages/DefaultLanguageColorsPage.java b/platform/lang-impl/src/com/intellij/openapi/options/colors/pages/DefaultLanguageColorsPage.java index 3d341f878393..74fdcaa1dc61 100644 --- a/platform/lang-impl/src/com/intellij/openapi/options/colors/pages/DefaultLanguageColorsPage.java +++ b/platform/lang-impl/src/com/intellij/openapi/options/colors/pages/DefaultLanguageColorsPage.java @@ -1,17 +1,5 @@ /* - * 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2000-2017 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.openapi.options.colors.pages; @@ -225,8 +213,9 @@ public class DefaultLanguageColorsPage implements RainbowColorSettingsPage, Disp "Function declaration (parameter1 parameter2 parameter3 parameter4)\n" + " Local variable1 variable2 variable3 variable4\n" + "Function call(" + - "0, 1, 2" + + "0, 1, 2" + ")\n" + + "Current function call(0, 1)\n" + "Interface Name\n" + "@Metadata\n" + "Class Name\n" + diff --git a/platform/testFramework/src/com/intellij/testFramework/utils/inlays/InlayParameterHintsTest.kt b/platform/testFramework/src/com/intellij/testFramework/utils/inlays/InlayParameterHintsTest.kt index ad3ef5995e22..876fede8e925 100644 --- a/platform/testFramework/src/com/intellij/testFramework/utils/inlays/InlayParameterHintsTest.kt +++ b/platform/testFramework/src/com/intellij/testFramework/utils/inlays/InlayParameterHintsTest.kt @@ -1,17 +1,5 @@ /* - * 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2000-2017 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.testFramework.utils.inlays @@ -36,7 +24,7 @@ class InlayHintsChecker(private val myFixture: CodeInsightTestFixture) { private var isParamHintsEnabledBefore = false companion object { - val pattern: Pattern = Pattern.compile("()|()|()|<(hint|HINT)\\s+text=\"([^\"\n\r]+)\"\\s*/>") + val pattern: Pattern = Pattern.compile("()|()|()|<(hint|HINT|Hint|hINT)\\s+text=\"([^\"\n\r]+)\"\\s*/>") private val default = ParameterNameHintsSettings() } @@ -66,20 +54,27 @@ class InlayHintsChecker(private val myFixture: CodeInsightTestFixture) { fun verifyInlaysAndCaretInfo(expectedInlaysAndCaret: CaretAndInlaysInfo, originalText: String) { val file = myFixture.file val document = myFixture.getDocument(file) - val actual: List> = getActualInlays() + val actual: List = getActualInlays() val expected = expectedInlaysAndCaret.inlays if (expectedInlaysAndCaret.inlays.size != actual.size || actual.zip(expected).any { it.first != it.second }) { val entries: MutableList> = mutableListOf() - actual.forEach { entries.add(Pair(it.first, "<${if(it.third) "HINT" else "hint"} text=\"${it.second}\" />")) } + actual.forEach { entries.add(Pair(it.offset, buildString { + append("<") + append((if (it.highlighted) "H" else "h")) + append((if (it.current) "INT" else "int")) + append(" text=\"") + append(it.text) + append("\"/>") + }))} if (expectedInlaysAndCaret.caretOffset != null) { val actualCaretOffset = myFixture.editor.caretModel.offset val actualInlaysBeforeCaret = myFixture.editor.caretModel.visualPosition.column - myFixture.editor.offsetToVisualPosition(actualCaretOffset).column val first = entries.indexOfFirst { it.first == actualCaretOffset } val insertIndex = if (first == -1) -entries.binarySearch { it.first - actualCaretOffset } - 1 - else first + actualInlaysBeforeCaret; + else first + actualInlaysBeforeCaret entries.add(insertIndex, Pair(actualCaretOffset, "")) } val proposedText = StringBuilder(document.text) @@ -104,22 +99,22 @@ class InlayHintsChecker(private val myFixture: CodeInsightTestFixture) { } } - private fun getActualInlays(): List> { + private fun getActualInlays(): List { val editor = myFixture.editor val allInlays = editor.inlayModel.getInlineElementsInRange(0, editor.document.textLength) val hintManager = ParameterHintsPresentationManager.getInstance() return allInlays .filter { hintManager.isParameterHint(it) } - .map { Triple(it.offset, hintManager.getHintText(it), hintManager.isHighlighted(it))} - .sortedBy { it.first } + .map { ParamHintInfo(it.offset, hintManager.getHintText(it), hintManager.isHighlighted(it), hintManager.isCurrent(it))} + .sortedBy { it.offset } } fun extractInlaysAndCaretInfo(document: Document): CaretAndInlaysInfo { val text = document.text val matcher = pattern.matcher(text) - val inlays = mutableListOf>() + val inlays = mutableListOf() var extractedLength = 0 var caretOffset : Int? = null var inlaysBeforeCaret = 0 @@ -134,7 +129,7 @@ class InlayHintsChecker(private val myFixture: CodeInsightTestFixture) { if (matcher.group(1) != null) { caretOffset = realStartOffset inlays.asReversed() - .takeWhile { it.first == caretOffset } + .takeWhile { it.offset == caretOffset } .forEach { inlaysBeforeCaret++ } } else if (matcher.group(2) != null) { @@ -144,7 +139,7 @@ class InlayHintsChecker(private val myFixture: CodeInsightTestFixture) { selectionEnd = realStartOffset } else { - inlays += Triple(realStartOffset, matcher.group(5), matcher.group(4) == "HINT") + inlays += ParamHintInfo(realStartOffset, matcher.group(5), matcher.group(4).startsWith("H"), matcher.group(4).endsWith("INT")) } removeText(document, realStartOffset, matchedLength) @@ -166,4 +161,6 @@ class InlayHintsChecker(private val myFixture: CodeInsightTestFixture) { } class CaretAndInlaysInfo (val caretOffset: Int?, val inlaysBeforeCaret: Int, val selection: TextRange?, - val inlays: List>) \ No newline at end of file + val inlays: List) + +data class ParamHintInfo (val offset: Int, val text: String, val highlighted: Boolean, val current: Boolean) \ No newline at end of file