mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
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.
This commit is contained in:
@@ -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<PsiMethod> implements TypedLookupItem, StaticallyImportable {
|
||||
public static final ClassConditionKey<JavaMethodCallElement> CLASS_CONDITION_KEY = ClassConditionKey.create(JavaMethodCallElement.class);
|
||||
public static final Key<List<Inlay>> COMPLETION_HINTS = Key.create("completion.hints");
|
||||
public static final Key<Boolean> 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<PsiMethod> 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<PsiMethod> 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<Inlay> 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) {
|
||||
|
||||
+6
-20
@@ -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<Map<String, String>> 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<Inlay> oldInlays = editor.getInlayModel().getInlineElementsInRange(offset, endOffset);
|
||||
for (Inlay inlay : oldInlays) {
|
||||
Disposer.dispose(inlay);
|
||||
}
|
||||
editor.getDocument().deleteString(offset, endOffset);
|
||||
int targetCaretPosition = -1;
|
||||
List<Inlay> 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) {
|
||||
|
||||
+4
-41
@@ -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<Inlay> inlays = editor.getInlayModel().getInlineElementsInRange(prev, rParOffset);
|
||||
ParameterHintsPresentationManager presentationManager = ParameterHintsPresentationManager.getInstance();
|
||||
String inlayText = null;
|
||||
List<Inlay> 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<Inlay> inlays = editor.getInlayModel().getInlineElementsInRange(wsStart, rParOffset);
|
||||
ParameterHintsPresentationManager presentationManager = ParameterHintsPresentationManager.getInstance();
|
||||
String inlayText = null;
|
||||
List<Inlay> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-41
@@ -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<Inlay> inlays = editor.getInlayModel().getInlineElementsInRange(wsStart, rParOffset);
|
||||
ParameterHintsPresentationManager presentationManager = ParameterHintsPresentationManager.getInstance();
|
||||
String inlayText = null;
|
||||
List<Inlay> 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<Inlay> inlays = editor.getInlayModel().getInlineElementsInRange(prev, next);
|
||||
ParameterHintsPresentationManager presentationManager = ParameterHintsPresentationManager.getInstance();
|
||||
String inlayText = null;
|
||||
List<Inlay> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-11
@@ -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<Inlay> 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();
|
||||
}
|
||||
|
||||
@@ -27,7 +27,23 @@ import com.intellij.psi.util.TypeConversionUtil
|
||||
object JavaInlayHintsProvider {
|
||||
|
||||
fun hints(callExpression: PsiCallExpression): Set<InlayInfo> {
|
||||
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 {
|
||||
|
||||
+2
-1
@@ -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)
|
||||
}
|
||||
|
||||
+1
-1
@@ -282,7 +282,7 @@ public class CompletionHintsTest extends LightFixtureCompletionTestCase {
|
||||
right();
|
||||
type("2");
|
||||
waitForAllAsyncStuff();
|
||||
checkResultWithInlays("class C { void m() { Character.forDigit(<hint text=\"digit:\"/>1,2 <hint text=\"radix:\"/>) } }");
|
||||
checkResultWithInlays("class C { void m() { Character.forDigit(<hint text=\"digit:\"/>1,<hint text=\"radix:\"/>2 ) } }");
|
||||
myFixture.performEditorAction(IdeActions.ACTION_EDITOR_MOVE_LINE_END);
|
||||
waitForAllAsyncStuff();
|
||||
checkResultWithInlays("class C { void m() { Character.forDigit(<hint text=\"digit:\"/>1,<hint text=\"radix:\"/>2 ) } }");
|
||||
|
||||
@@ -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<InlayParameterHintsProvi
|
||||
class InlayInfo(val text: String,
|
||||
val offset: Int,
|
||||
val isShowOnlyIfExistedBefore: Boolean,
|
||||
val isFilterByBlacklist: Boolean) {
|
||||
val isFilterByBlacklist: Boolean,
|
||||
val showAfterCaret: Boolean) {
|
||||
|
||||
constructor(text: String, offset: Int, isShowOnlyIfExistedBefore: Boolean) : this(text, offset, isShowOnlyIfExistedBefore, true)
|
||||
constructor(text: String, offset: Int) : this(text, offset, false, true)
|
||||
constructor(text: String, offset: Int, isShowOnlyIfExistedBefore: Boolean) : this(text, offset, isShowOnlyIfExistedBefore, true, false)
|
||||
constructor(text: String, offset: Int) : this(text, offset, false, true, false)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ public class InlineElementData extends HighlightData {
|
||||
@Override
|
||||
public void addHighlToView(Editor view, EditorColorsScheme scheme, Map<TextAttributesKey, String> displayText) {
|
||||
int offset = getStartOffset();
|
||||
ParameterHintsPresentationManager.getInstance().addHint(view, offset, myText, false, false);
|
||||
ParameterHintsPresentationManager.getInstance().addHint(view, offset, myText, false);
|
||||
List<Inlay> inlays = view.getInlayModel().getInlineElementsInRange(offset, offset);
|
||||
for (Inlay inlay : inlays) {
|
||||
EditorCustomElementRenderer renderer = inlay.getRenderer();
|
||||
|
||||
+1
-11
@@ -43,7 +43,6 @@ import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
public class ParameterHintsPresentationManager implements Disposable {
|
||||
private static final Key<Boolean> PINNED = Key.create("parameter.hint.pinned");
|
||||
private static final Key<MyFontMetrics> HINT_FONT_METRICS = Key.create("ParameterHintFontMetrics");
|
||||
private static final Key<AnimationStep> 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;
|
||||
|
||||
@@ -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<String> myHints = new TIntObjectHashMap<>();
|
||||
private final TIntObjectHashMap<List<HintData>> myHints = new TIntObjectHashMap<>();
|
||||
private final TIntObjectHashMap<String> myShowOnlyIfExistedBeforeHints = new TIntObjectHashMap<>();
|
||||
private final SyntaxTraverser<PsiElement> 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<HintData> 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<Inlay> 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<Inlay> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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) {
|
||||
|
||||
@@ -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<Caret> myCaretMap;
|
||||
|
||||
private final TIntObjectHashMap<String> myNewHints;
|
||||
private final TIntObjectHashMap<List<ParameterHintsPass.HintData>> myNewHints;
|
||||
private final TIntObjectHashMap<String> myHintsToPreserve;
|
||||
private final boolean myForceImmediateUpdate;
|
||||
|
||||
private final Editor myEditor;
|
||||
private final List<InlayUpdateInfo> myUpdateList;
|
||||
|
||||
public ParameterHintsUpdater(@NotNull Editor editor,
|
||||
@NotNull List<Inlay> inlays,
|
||||
@NotNull TIntObjectHashMap<String> newHints,
|
||||
@NotNull TIntObjectHashMap<String> hintsToPreserve) {
|
||||
@NotNull TIntObjectHashMap<List<ParameterHintsPass.HintData>> newHints,
|
||||
@NotNull TIntObjectHashMap<String> hintsToPreserve,
|
||||
boolean forceImmediateUpdate) {
|
||||
myEditor = editor;
|
||||
myNewHints = newHints;
|
||||
myHintsToPreserve = hintsToPreserve;
|
||||
myForceImmediateUpdate = forceImmediateUpdate;
|
||||
|
||||
myCaretMap = new TIntObjectHashMap<>();
|
||||
List<Caret> allCarets = myEditor.getCaretModel().getAllCarets();
|
||||
@@ -64,20 +64,50 @@ public class ParameterHintsUpdater {
|
||||
|
||||
private List<InlayUpdateInfo> getInlayUpdates(List<Inlay> editorHints) {
|
||||
List<InlayUpdateInfo> 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<List<ParameterHintsPass.HintData>> data) {
|
||||
List<ParameterHintsPass.HintData> newHintList = data.get(offset);
|
||||
ParameterHintsPass.HintData newHint = null;
|
||||
if (newHintList != null) {
|
||||
ParameterHintsPass.HintData lastHint = null;
|
||||
for (Iterator<ParameterHintsPass.HintData> 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() {
|
||||
|
||||
Reference in New Issue
Block a user