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 bebe51cc1424..a80550a48e1d 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 @@ -16,11 +16,11 @@ import com.intellij.codeInsight.javadoc.JavaDocInfoGenerator; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.injected.editor.EditorWindow; import com.intellij.lang.parameterInfo.*; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.Inlay; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.editor.*; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.DumbService; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; @@ -794,4 +794,38 @@ public class MethodParameterInfoHandler implements ParameterInfoHandlerWithTabAc public boolean supportsOverloadSwitching() { return CodeInsightSettings.getInstance().SHOW_PARAMETER_NAME_HINTS_ON_COMPLETION; } + + @Override + public void syncUpdateOnCaretMove(@NotNull UpdateParameterInfoContext context) { + if (!Registry.is("editor.completion.hints.virtual.comma")) return; + + Editor editor = context.getEditor(); + Caret caret = editor.getCaretModel().getCurrentCaret(); + int caretOffset = caret.getOffset(); + List inlays = editor.getInlayModel().getInlineElementsInRange(caretOffset, caretOffset); + if (inlays.isEmpty()) return; + + VisualPosition caretPosition = caret.getVisualPosition(); + ParameterHintsPresentationManager pm = ParameterHintsPresentationManager.getInstance(); + int inlaysBeforeCaretWithComma = ContainerUtil.count(inlays, inlay -> pm.isParameterHint(inlay) && + StringUtil.startsWithChar(pm.getHintText(inlay), ',') && + caretPosition.after(inlay.getVisualPosition())); + if (inlaysBeforeCaretWithComma == 0) return; + + Project project = context.getProject(); + String textToInsert = StringUtil.repeat(", ", inlaysBeforeCaretWithComma); + WriteCommandAction.runWriteCommandAction(project, () -> { + editor.getDocument().insertString(caretOffset, textToInsert); + caret.moveToOffset(caretOffset + textToInsert.length()); + }); + + PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument()); + PsiElement exprList = context.getParameterOwner(); + if (!(exprList instanceof PsiExpressionList) || !exprList.isValid()) return; + PsiElement call = exprList.getParent(); + if (call == null || !call.isValid()) return; + ParameterHintsPass.syncUpdate(call, editor); + int index = ParameterInfoUtils.getCurrentParameterIndex(exprList.getNode(), editor.getCaretModel().getOffset(), JavaTokenType.COMMA); + highlightHints(editor, (PsiExpressionList)exprList, index, context.getCustomContext()); + } } 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 d01c3ffcae46..d26a90eaa30c 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 @@ -1369,6 +1369,17 @@ public class CompletionHintsTest extends AbstractParameterInfoTestCase { "}"); } + public void testCaretMovementOverVirtualComma() throws Exception { + enableVirtualComma(); + + configureJava("class C { void m() { System.getPro } }"); + complete("getProperty(String key, String def)"); + checkResultWithInlays("class C { void m() { System.getProperty() } }"); + + right(); + checkResultWithInlays("class C { void m() { System.getProperty(, ) } }"); + } + private void checkResultWithInlays(String text) { myFixture.checkResultWithInlays(text); } diff --git a/platform/lang-api/src/com/intellij/lang/parameterInfo/ParameterInfoHandler.java b/platform/lang-api/src/com/intellij/lang/parameterInfo/ParameterInfoHandler.java index 9aa8c6054f94..b71a05f7c224 100644 --- a/platform/lang-api/src/com/intellij/lang/parameterInfo/ParameterInfoHandler.java +++ b/platform/lang-api/src/com/intellij/lang/parameterInfo/ParameterInfoHandler.java @@ -42,6 +42,7 @@ public interface ParameterInfoHandler { default boolean isWhitespaceSensitive() { return false; } + default void syncUpdateOnCaretMove(@NotNull UpdateParameterInfoContext context) {} /** @deprecated not used */ @Deprecated 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 ff03410ac8e6..e774c7b5cf74 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoController.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoController.java @@ -145,6 +145,7 @@ public class ParameterInfoController extends UserDataHolderBase implements Visib myEditorCaretListener = new CaretListener(){ @Override public void caretPositionChanged(CaretEvent e) { + syncUpdateOnCaretMove(); rescheduleUpdate(); } }; @@ -182,6 +183,10 @@ public class ParameterInfoController extends UserDataHolderBase implements Visib updateComponent(); } + private void syncUpdateOnCaretMove() { + myHandler.syncUpdateOnCaretMove(new MyLazyUpdateParameterInfoContext()); + } + private LightweightHint createHint() { JPanel wrapper = new WrapperPanel(); wrapper.add(myComponent); @@ -663,6 +668,22 @@ public class ParameterInfoController extends UserDataHolderBase implements Visib } } + private class MyLazyUpdateParameterInfoContext extends MyUpdateParameterInfoContext { + private PsiFile myFile; + + private MyLazyUpdateParameterInfoContext() { + super(myEditor.getCaretModel().getOffset(), null); + } + + @Override + public PsiFile getFile() { + if (myFile == null) { + myFile = PsiUtilBase.getPsiFileInEditor(myEditor, myProject); + } + return myFile; + } + } + protected void hideHint() { myHint.hide(); for (ParameterInfoListener listener : myListeners) { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java index c1a58ef5de84..dbf791ace112 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java @@ -483,7 +483,7 @@ public class CaretImpl extends UserDataHolderBase implements Caret, Dumpable { } } - if (!oldVisualPosition.equals(myVisibleCaret)) { + if (!oldVisualPosition.equals(myVisibleCaret) || !oldCaretPosition.equals(myLogicalCaret)) { CaretEvent event = new CaretEvent(myEditor, this, oldCaretPosition, myLogicalCaret); if (fireListeners) { myEditor.getCaretModel().fireCaretPositionChanged(event); @@ -573,6 +573,7 @@ public class CaretImpl extends UserDataHolderBase implements Caret, Dumpable { } } + VisualPosition oldVisualPosition = myVisibleCaret; myVisibleCaret = new VisualPosition(line, column, leanRight); VerticalInfo oldInfo = myCaretInfo; @@ -593,7 +594,7 @@ public class CaretImpl extends UserDataHolderBase implements Caret, Dumpable { myEditor.updateCaretCursor(); requestRepaint(oldInfo); - if (fireListeners && !oldPosition.equals(myLogicalCaret)) { + if (fireListeners && (!oldPosition.equals(myLogicalCaret) || !oldVisualPosition.equals(myVisibleCaret))) { CaretEvent event = new CaretEvent(myEditor, this, oldPosition, myLogicalCaret); myEditor.getCaretModel().fireCaretPositionChanged(event); } @@ -735,8 +736,8 @@ public class CaretImpl extends UserDataHolderBase implements Caret, Dumpable { void onInlayAdded(int offset) { updateCachedStateIfNeeded(); int currentOffset = getOffset(); - if (offset == currentOffset && myLogicalCaret.leansForward) { - VisualPosition pos = myEditor.offsetToVisualPosition(currentOffset, true, false); + if (offset == currentOffset) { + VisualPosition pos = EditorUtil.inlayAwareOffsetToVisualPosition(myEditor, offset); moveToVisualPosition(pos); } else {