mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
option to show parameter hints on method completion, ability to switch method overloads (Java only)
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2000-2009 JetBrains s.r.o.
|
* Copyright 2000-2017 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,6 +17,10 @@ package com.intellij.codeInsight.completion;
|
|||||||
|
|
||||||
import com.intellij.codeInsight.AutoPopupController;
|
import com.intellij.codeInsight.AutoPopupController;
|
||||||
import com.intellij.codeInsight.completion.util.MethodParenthesesHandler;
|
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.lookup.*;
|
import com.intellij.codeInsight.lookup.*;
|
||||||
import com.intellij.codeInsight.lookup.impl.JavaElementLookupRenderer;
|
import com.intellij.codeInsight.lookup.impl.JavaElementLookupRenderer;
|
||||||
import com.intellij.codeInsight.template.*;
|
import com.intellij.codeInsight.template.*;
|
||||||
@@ -26,10 +30,13 @@ import com.intellij.codeInsight.template.impl.TemplateManagerImpl;
|
|||||||
import com.intellij.codeInsight.template.impl.TemplateState;
|
import com.intellij.codeInsight.template.impl.TemplateState;
|
||||||
import com.intellij.featureStatistics.FeatureUsageTracker;
|
import com.intellij.featureStatistics.FeatureUsageTracker;
|
||||||
import com.intellij.openapi.command.WriteCommandAction;
|
import com.intellij.openapi.command.WriteCommandAction;
|
||||||
|
import com.intellij.openapi.editor.CaretModel;
|
||||||
import com.intellij.openapi.editor.Document;
|
import com.intellij.openapi.editor.Document;
|
||||||
import com.intellij.openapi.editor.Editor;
|
import com.intellij.openapi.editor.Editor;
|
||||||
|
import com.intellij.openapi.editor.Inlay;
|
||||||
import com.intellij.openapi.editor.event.DocumentAdapter;
|
import com.intellij.openapi.editor.event.DocumentAdapter;
|
||||||
import com.intellij.openapi.editor.event.DocumentEvent;
|
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||||
|
import com.intellij.openapi.project.Project;
|
||||||
import com.intellij.openapi.util.ClassConditionKey;
|
import com.intellij.openapi.util.ClassConditionKey;
|
||||||
import com.intellij.openapi.util.Disposer;
|
import com.intellij.openapi.util.Disposer;
|
||||||
import com.intellij.openapi.util.Key;
|
import com.intellij.openapi.util.Key;
|
||||||
@@ -44,6 +51,8 @@ import com.intellij.psi.util.TypeConversionUtil;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -175,6 +184,7 @@ public class JavaMethodCallElement extends LookupItem<PsiMethod> implements Type
|
|||||||
}
|
}
|
||||||
|
|
||||||
startArgumentLiveTemplate(context, method);
|
startArgumentLiveTemplate(context, method);
|
||||||
|
showParameterHints(context, method, methodCall);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PsiCallExpression findCallAtOffset(InsertionContext context, int offset) {
|
static PsiCallExpression findCallAtOffset(InsertionContext context, int offset) {
|
||||||
@@ -270,6 +280,45 @@ public class JavaMethodCallElement extends LookupItem<PsiMethod> implements Type
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void showParameterHints(InsertionContext context, PsiMethod method, PsiCallExpression methodCall) {
|
||||||
|
PsiParameterList parameterList = method.getParameterList();
|
||||||
|
int parametersCount = parameterList.getParametersCount();
|
||||||
|
if (methodCall == null ||
|
||||||
|
parametersCount == 0 ||
|
||||||
|
context.getCompletionChar() == Lookup.COMPLETE_STATEMENT_SELECT_CHAR ||
|
||||||
|
Registry.is("java.completion.argument.live.template") ||
|
||||||
|
!Registry.is("java.completion.argument.hints")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Editor editor = context.getEditor();
|
||||||
|
CaretModel caretModel = editor.getCaretModel();
|
||||||
|
int offset = caretModel.getOffset();
|
||||||
|
caretModel.moveToOffset(offset - 1); // avoid caret impact on hints location
|
||||||
|
editor.getDocument().insertString(offset, StringUtil.repeat(", ", parametersCount - 1));
|
||||||
|
List<Inlay> addedHints = new ArrayList<>(parametersCount);
|
||||||
|
for (PsiParameter parameter : parameterList.getParameters()) {
|
||||||
|
String name = parameter.getName();
|
||||||
|
if (name != null) {
|
||||||
|
addedHints.add(ParameterHintsPresentationManager.getInstance().addHint(editor, offset, name + ":", false, true));
|
||||||
|
}
|
||||||
|
offset += 2;
|
||||||
|
}
|
||||||
|
int braceOffset = caretModel.getOffset();
|
||||||
|
caretModel.moveToLogicalPosition(editor.offsetToLogicalPosition(braceOffset + 1).leanForward(true));
|
||||||
|
|
||||||
|
Project project = context.getProject();
|
||||||
|
MethodParameterInfoHandler handler = new MethodParameterInfoHandler();
|
||||||
|
ShowParameterInfoContext infoContext = new ShowParameterInfoContext(editor, project, context.getFile(), braceOffset, braceOffset);
|
||||||
|
handler.findElementForParameterInfo(infoContext);
|
||||||
|
|
||||||
|
Disposer.register(new ParameterInfoController(project, editor, braceOffset, infoContext.getItemsToShow(), null, methodCall.getArgumentList(), handler, false, false), () -> {
|
||||||
|
for (Inlay inlay : addedHints) {
|
||||||
|
if (inlay != null) ParameterHintsPresentationManager.getInstance().unpin(inlay);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private static void setupNonFilledArgumentRemoving(final Editor editor, final TemplateState templateState) {
|
private static void setupNonFilledArgumentRemoving(final Editor editor, final TemplateState templateState) {
|
||||||
AtomicInteger maxEditedVariable = new AtomicInteger(-1);
|
AtomicInteger maxEditedVariable = new AtomicInteger(-1);
|
||||||
editor.getDocument().addDocumentListener(new DocumentAdapter() {
|
editor.getDocument().addDocumentListener(new DocumentAdapter() {
|
||||||
|
|||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package com.intellij.codeInsight.editorActions;
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.ActionPromoter;
|
||||||
|
import com.intellij.openapi.actionSystem.AnAction;
|
||||||
|
import com.intellij.openapi.actionSystem.DataContext;
|
||||||
|
import com.intellij.util.containers.ContainerUtil;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class JavaMethodOverloadSwitchActionPromoter implements ActionPromoter {
|
||||||
|
@Override
|
||||||
|
public List<AnAction> promote(List<AnAction> actions, DataContext context) {
|
||||||
|
return ContainerUtil.findAll(actions, a -> a instanceof JavaMethodOverloadSwitchUpAction ||
|
||||||
|
a instanceof JavaMethodOverloadSwitchDownAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package com.intellij.codeInsight.editorActions;
|
||||||
|
|
||||||
|
import com.intellij.openapi.editor.actionSystem.EditorAction;
|
||||||
|
|
||||||
|
public class JavaMethodOverloadSwitchDownAction extends EditorAction {
|
||||||
|
public JavaMethodOverloadSwitchDownAction() {
|
||||||
|
super(new JavaMethodOverloadSwitchHandler(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
+149
@@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package com.intellij.codeInsight.editorActions;
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.completion.CompletionMemory;
|
||||||
|
import com.intellij.codeInsight.daemon.impl.ParameterHintsPresentationManager;
|
||||||
|
import com.intellij.codeInsight.hint.ParameterInfoController;
|
||||||
|
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.openapi.util.registry.Registry;
|
||||||
|
import com.intellij.psi.*;
|
||||||
|
import com.intellij.psi.infos.CandidateInfo;
|
||||||
|
import com.intellij.util.containers.ContainerUtil;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class JavaMethodOverloadSwitchHandler extends EditorWriteActionHandler {
|
||||||
|
private static final Key<Map<String, String>> ENTERED_PARAMETERS = Key.create("entered.parameters");
|
||||||
|
private final boolean mySwitchUp;
|
||||||
|
|
||||||
|
public JavaMethodOverloadSwitchHandler(boolean up) {
|
||||||
|
mySwitchUp = up;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isEnabledForCaret(@NotNull Editor editor, @NotNull Caret caret, DataContext dataContext) {
|
||||||
|
if (!Registry.is("java.completion.argument.hints") || !ParameterInfoController.existsForEditor(editor)) return false;
|
||||||
|
|
||||||
|
Project project = CommonDataKeys.PROJECT.getData(dataContext);
|
||||||
|
if (project == null) return false;
|
||||||
|
|
||||||
|
PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
|
||||||
|
|
||||||
|
PsiElement exprList = getExpressionList(editor, caret.getOffset(), project);
|
||||||
|
if (exprList == null) return false;
|
||||||
|
|
||||||
|
int lbraceOffset = exprList.getTextRange().getStartOffset();
|
||||||
|
return ParameterInfoController.findControllerAtOffset(editor, lbraceOffset) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static PsiElement getExpressionList(@NotNull Editor editor, int offset, @NotNull Project project) {
|
||||||
|
PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
|
||||||
|
return file != null ? ParameterInfoController.findArgumentList(file, offset, -1) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void executeWriteAction(Editor editor, @Nullable Caret caret, DataContext dataContext) {
|
||||||
|
if (caret == null) caret = editor.getCaretModel().getPrimaryCaret();
|
||||||
|
Project project = CommonDataKeys.PROJECT.getData(dataContext);
|
||||||
|
if (project == null) return;
|
||||||
|
|
||||||
|
PsiDocumentManager.getInstance(project).commitAllDocuments();
|
||||||
|
|
||||||
|
PsiElement exprList = getExpressionList(editor, caret.getOffset(), project);
|
||||||
|
if (!(exprList instanceof PsiExpressionList)) return;
|
||||||
|
|
||||||
|
int lbraceOffset = exprList.getTextRange().getStartOffset();
|
||||||
|
ParameterInfoController controller = ParameterInfoController.findControllerAtOffset(editor, lbraceOffset);
|
||||||
|
if (controller == null) return;
|
||||||
|
Object[] objects = controller.getObjects();
|
||||||
|
Object highlighted = controller.getHighlighted();
|
||||||
|
if (objects == null || objects.length <= 1 || highlighted == null) return;
|
||||||
|
|
||||||
|
int currentIndex = ContainerUtil.indexOf(Arrays.asList(objects), highlighted);
|
||||||
|
if (currentIndex < 0) return;
|
||||||
|
|
||||||
|
PsiMethod currentMethod = (PsiMethod)((CandidateInfo)objects[currentIndex]).getElement();
|
||||||
|
|
||||||
|
Map<String, String> enteredParameters = exprList.getUserData(ENTERED_PARAMETERS);
|
||||||
|
if (enteredParameters == null) {
|
||||||
|
exprList.putUserData(ENTERED_PARAMETERS, enteredParameters = new HashMap<>());
|
||||||
|
}
|
||||||
|
PsiExpression[] enteredExpressions = ((PsiExpressionList)exprList).getExpressions();
|
||||||
|
assert enteredExpressions.length == 0 || enteredExpressions.length == currentMethod.getParameterList().getParametersCount();
|
||||||
|
for (int i = 0; i < enteredExpressions.length; i++) {
|
||||||
|
PsiExpression expression = enteredExpressions[i];
|
||||||
|
String value = expression.getText().trim();
|
||||||
|
if (!value.isEmpty()) {
|
||||||
|
String key = getParameterKey(currentMethod, i);
|
||||||
|
enteredParameters.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PsiMethod targetMethod = (PsiMethod)((CandidateInfo)objects[(currentIndex + (mySwitchUp ? -1 : 1) + objects.length) % objects.length]).getElement();
|
||||||
|
PsiParameterList parameterList = targetMethod.getParameterList();
|
||||||
|
int parametersCount = parameterList.getParametersCount();
|
||||||
|
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));
|
||||||
|
Disposer.register(controller, () -> {
|
||||||
|
for (Inlay hint : addedHints) {
|
||||||
|
if (hint != null) ParameterHintsPresentationManager.getInstance().unpin(hint);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
|
||||||
|
CompletionMemory.registerChosenMethod(targetMethod, (PsiCall)exprList.getParent());
|
||||||
|
controller.updateComponent(); // update popup immediately (otherwise, it will be updated only after delay)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getParameterKey(PsiMethod method, int parameterIndex) {
|
||||||
|
PsiParameter parameter = method.getParameterList().getParameters()[parameterIndex];
|
||||||
|
return parameter.getName() + ":" + parameter.getType().getCanonicalText();
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package com.intellij.codeInsight.editorActions;
|
||||||
|
|
||||||
|
import com.intellij.openapi.editor.actionSystem.EditorAction;
|
||||||
|
|
||||||
|
public class JavaMethodOverloadSwitchUpAction extends EditorAction {
|
||||||
|
public JavaMethodOverloadSwitchUpAction() {
|
||||||
|
super(new JavaMethodOverloadSwitchHandler(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package com.intellij.codeInsight.completion;
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.daemon.impl.ParameterHintsPresentationManager;
|
||||||
|
import com.intellij.codeInsight.hint.ParameterInfoController;
|
||||||
|
import com.intellij.codeInsight.lookup.LookupElement;
|
||||||
|
import com.intellij.codeInsight.lookup.LookupElementPresentation;
|
||||||
|
import com.intellij.ide.highlighter.JavaFileType;
|
||||||
|
import com.intellij.openapi.util.registry.Registry;
|
||||||
|
import com.intellij.openapi.util.registry.RegistryValue;
|
||||||
|
import com.intellij.util.ui.UIUtil;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class CompletionHintsTest extends LightFixtureCompletionTestCase {
|
||||||
|
private RegistryValue myRegistryValue = Registry.get("java.completion.argument.hints");
|
||||||
|
private boolean myStoredRegistryValue;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
myStoredRegistryValue = myRegistryValue.asBoolean();
|
||||||
|
myRegistryValue.setValue(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() throws Exception {
|
||||||
|
try {
|
||||||
|
myRegistryValue.setValue(myStoredRegistryValue);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testBasicScenario() throws Exception {
|
||||||
|
// check hints appearance on completion
|
||||||
|
myFixture.configureByText(JavaFileType.INSTANCE, "class C { void m() { System.setPro<caret> } }");
|
||||||
|
complete("setProperty");
|
||||||
|
myFixture.checkResultWithInlays("class C { void m() { System.setProperty(<hint text=\"key:\"/>, <hint text=\"value:\"/>) } }");
|
||||||
|
|
||||||
|
// check that hints don't disappear after daemon highlighting passes
|
||||||
|
myFixture.doHighlighting();
|
||||||
|
myFixture.checkResultWithInlays("class C { void m() { System.setProperty(<hint text=\"key:\"/>, <hint text=\"value:\"/>) } }");
|
||||||
|
|
||||||
|
// test Tab/Shift+Tab navigation
|
||||||
|
myFixture.checkResult("class C { void m() { System.setProperty(<caret>, ) } }");
|
||||||
|
assertTrue(myFixture.getEditor().getCaretModel().getLogicalPosition().leansForward);
|
||||||
|
myFixture.performEditorAction("NextParameter");
|
||||||
|
myFixture.checkResult("class C { void m() { System.setProperty(, <caret>) } }");
|
||||||
|
assertTrue(myFixture.getEditor().getCaretModel().getLogicalPosition().leansForward);
|
||||||
|
myFixture.performEditorAction("PrevParameter");
|
||||||
|
myFixture.checkResult("class C { void m() { System.setProperty(<caret>, ) } }");
|
||||||
|
assertTrue(myFixture.getEditor().getCaretModel().getLogicalPosition().leansForward);
|
||||||
|
|
||||||
|
// test hints remain shown while entering parameter values
|
||||||
|
myFixture.type("\"a");
|
||||||
|
myFixture.performEditorAction("NextParameter");
|
||||||
|
myFixture.type("\"b");
|
||||||
|
myFixture.doHighlighting();
|
||||||
|
myFixture.checkResultWithInlays("class C { void m() { System.setProperty(<hint text=\"key:\"/>\"a\", <hint text=\"value:\"/>\"b\") } }");
|
||||||
|
|
||||||
|
// test hints disappearance when caret moves out of parameter list
|
||||||
|
myFixture.performEditorAction("EditorRight");
|
||||||
|
myFixture.performEditorAction("EditorRight");
|
||||||
|
ParameterInfoController.waitForDelayedActions(getEditor(), 10, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
myFixture.doHighlighting();
|
||||||
|
waitTillAnimationCompletes();
|
||||||
|
myFixture.checkResultWithInlays("class C { void m() { System.setProperty(\"a\", \"b\") } }");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSwitchingOverloads() {
|
||||||
|
myFixture.configureByText(JavaFileType.INSTANCE, "class C { void m() { Character.to<caret> } }");
|
||||||
|
complete("toChars(int codePoint)");
|
||||||
|
myFixture.checkResultWithInlays("class C { void m() { Character.toChars(<hint text=\"codePoint:\"/>) } }");
|
||||||
|
showParameterInfo();
|
||||||
|
myFixture.performEditorAction("MethodOverloadSwitchDown");
|
||||||
|
myFixture.checkResultWithInlays("class C { void m() { Character.toChars(<hint text=\"codePoint:\"/>, <hint text=\"dst:\"/>, <hint text=\"dstIndex:\"/>) } }");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSwitchingOverloadsWithParameterFilled() {
|
||||||
|
myFixture.configureByText(JavaFileType.INSTANCE, "class C { void m() { Character.to<caret> } }");
|
||||||
|
complete("toChars(int codePoint)");
|
||||||
|
type("123");
|
||||||
|
myFixture.checkResultWithInlays("class C { void m() { Character.toChars(<hint text=\"codePoint:\"/>123) } }");
|
||||||
|
showParameterInfo();
|
||||||
|
myFixture.performEditorAction("MethodOverloadSwitchDown");
|
||||||
|
myFixture.checkResultWithInlays("class C { void m() { Character.toChars(<hint text=\"codePoint:\"/>123, <hint text=\"dst:\"/>, <hint text=\"dstIndex:\"/>) } }");
|
||||||
|
myFixture.checkResult("class C { void m() { Character.toChars(123, <caret>, ) } }");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showParameterInfo() {
|
||||||
|
myFixture.performEditorAction("ParameterInfo");
|
||||||
|
UIUtil.dispatchAllInvocationEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void complete(String partOfItemText) {
|
||||||
|
LookupElement[] elements = myFixture.completeBasic();
|
||||||
|
LookupElement element = Stream.of(elements).filter(e -> {
|
||||||
|
LookupElementPresentation p = new LookupElementPresentation();
|
||||||
|
e.renderElement(p);
|
||||||
|
return (p.getItemText() + p.getTailText()).contains(partOfItemText);
|
||||||
|
}).findAny().get();
|
||||||
|
selectItem(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void waitTillAnimationCompletes() {
|
||||||
|
long deadline = System.currentTimeMillis() + 60_000;
|
||||||
|
while (ParameterHintsPresentationManager.getInstance().isAnimationInProgress(getEditor())) {
|
||||||
|
if (System.currentTimeMillis() > deadline) fail("Too long waiting for animation to finish");
|
||||||
|
UIUtil.dispatchAllInvocationEvents();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -53,7 +53,7 @@ public class InlineElementData extends HighlightData {
|
|||||||
@Override
|
@Override
|
||||||
public void addHighlToView(Editor view, EditorColorsScheme scheme, Map<TextAttributesKey, String> displayText) {
|
public void addHighlToView(Editor view, EditorColorsScheme scheme, Map<TextAttributesKey, String> displayText) {
|
||||||
int offset = getStartOffset();
|
int offset = getStartOffset();
|
||||||
ParameterHintsPresentationManager.getInstance().addHint(view, offset, myText, false);
|
ParameterHintsPresentationManager.getInstance().addHint(view, offset, myText, false, false);
|
||||||
List<Inlay> inlays = view.getInlayModel().getInlineElementsInRange(offset, offset);
|
List<Inlay> inlays = view.getInlayModel().getInlineElementsInRange(offset, offset);
|
||||||
for (Inlay inlay : inlays) {
|
for (Inlay inlay : inlays) {
|
||||||
EditorCustomElementRenderer renderer = inlay.getRenderer();
|
EditorCustomElementRenderer renderer = inlay.getRenderer();
|
||||||
|
|||||||
+22
-4
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -33,6 +33,7 @@ import com.intellij.util.Alarm;
|
|||||||
import com.intellij.util.ui.GraphicsUtil;
|
import com.intellij.util.ui.GraphicsUtil;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.annotations.TestOnly;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
@@ -42,6 +43,7 @@ import java.util.Iterator;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class ParameterHintsPresentationManager implements Disposable {
|
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<MyFontMetrics> HINT_FONT_METRICS = Key.create("ParameterHintFontMetrics");
|
||||||
private static final Key<AnimationStep> ANIMATION_STEP = Key.create("ParameterHintAnimationStep");
|
private static final Key<AnimationStep> ANIMATION_STEP = Key.create("ParameterHintAnimationStep");
|
||||||
|
|
||||||
@@ -62,17 +64,27 @@ public class ParameterHintsPresentationManager implements Disposable {
|
|||||||
return inlay.getRenderer() instanceof MyRenderer;
|
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) {
|
public String getHintText(@NotNull Inlay inlay) {
|
||||||
EditorCustomElementRenderer renderer = inlay.getRenderer();
|
EditorCustomElementRenderer renderer = inlay.getRenderer();
|
||||||
return renderer instanceof MyRenderer ? ((MyRenderer)renderer).getText() : null;
|
return renderer instanceof MyRenderer ? ((MyRenderer)renderer).getText() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addHint(@NotNull Editor editor, int offset, @NotNull String hintText, boolean useAnimation) {
|
public Inlay addHint(@NotNull Editor editor, int offset, @NotNull String hintText, boolean useAnimation, boolean pinned) {
|
||||||
MyRenderer renderer = new MyRenderer(editor, hintText, useAnimation);
|
MyRenderer renderer = new MyRenderer(editor, hintText, useAnimation);
|
||||||
Inlay inlay = editor.getInlayModel().addInlineElement(offset, renderer);
|
Inlay inlay = editor.getInlayModel().addInlineElement(offset, renderer);
|
||||||
if (useAnimation && inlay != null) {
|
if (inlay != null) {
|
||||||
scheduleRendererUpdate(editor, inlay);
|
if (pinned) inlay.putUserData(PINNED, Boolean.TRUE);
|
||||||
|
if (useAnimation) scheduleRendererUpdate(editor, inlay);
|
||||||
}
|
}
|
||||||
|
return inlay;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteHint(@NotNull Editor editor, @NotNull Inlay hint) {
|
public void deleteHint(@NotNull Editor editor, @NotNull Inlay hint) {
|
||||||
@@ -109,6 +121,12 @@ public class ParameterHintsPresentationManager implements Disposable {
|
|||||||
myAlarm.addRequest(step, ANIMATION_STEP_MS, ModalityState.any());
|
myAlarm.addRequest(step, ANIMATION_STEP_MS, ModalityState.any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestOnly
|
||||||
|
public boolean isAnimationInProgress(@NotNull Editor editor) {
|
||||||
|
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||||
|
return editor.getUserData(ANIMATION_STEP) != null;
|
||||||
|
}
|
||||||
|
|
||||||
private static Font getFont(@NotNull Editor editor) {
|
private static Font getFont(@NotNull Editor editor) {
|
||||||
return getFontMetrics(editor).getFont();
|
return getFontMetrics(editor).getFont();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2000-2014 JetBrains s.r.o.
|
* Copyright 2000-2017 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -140,10 +140,6 @@ public class ParameterInfoComponent extends JPanel {
|
|||||||
return myHighlighted;
|
return myHighlighted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRequestFocus(boolean requestFocus) {
|
|
||||||
myRequestFocus = requestFocus;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isRequestFocus() {
|
public boolean isRequestFocus() {
|
||||||
return myRequestFocus;
|
return myRequestFocus;
|
||||||
}
|
}
|
||||||
|
|||||||
+223
-22
@@ -16,18 +16,19 @@
|
|||||||
|
|
||||||
package com.intellij.codeInsight.hint;
|
package com.intellij.codeInsight.hint;
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.daemon.impl.ParameterHintsPresentationManager;
|
||||||
import com.intellij.codeInsight.lookup.Lookup;
|
import com.intellij.codeInsight.lookup.Lookup;
|
||||||
import com.intellij.codeInsight.lookup.LookupManager;
|
import com.intellij.codeInsight.lookup.LookupManager;
|
||||||
import com.intellij.ide.IdeTooltip;
|
import com.intellij.ide.IdeTooltip;
|
||||||
|
import com.intellij.injected.editor.EditorWindow;
|
||||||
import com.intellij.lang.parameterInfo.ParameterInfoHandler;
|
import com.intellij.lang.parameterInfo.ParameterInfoHandler;
|
||||||
import com.intellij.lang.parameterInfo.ParameterInfoHandlerWithTabActionSupport;
|
import com.intellij.lang.parameterInfo.ParameterInfoHandlerWithTabActionSupport;
|
||||||
import com.intellij.lang.parameterInfo.ParameterInfoUtils;
|
import com.intellij.lang.parameterInfo.ParameterInfoUtils;
|
||||||
import com.intellij.lang.parameterInfo.UpdateParameterInfoContext;
|
import com.intellij.lang.parameterInfo.UpdateParameterInfoContext;
|
||||||
import com.intellij.openapi.Disposable;
|
import com.intellij.openapi.Disposable;
|
||||||
|
import com.intellij.openapi.application.ApplicationManager;
|
||||||
import com.intellij.openapi.application.ModalityState;
|
import com.intellij.openapi.application.ModalityState;
|
||||||
import com.intellij.openapi.editor.Editor;
|
import com.intellij.openapi.editor.*;
|
||||||
import com.intellij.openapi.editor.RangeMarker;
|
|
||||||
import com.intellij.openapi.editor.ScrollType;
|
|
||||||
import com.intellij.openapi.editor.event.*;
|
import com.intellij.openapi.editor.event.*;
|
||||||
import com.intellij.openapi.editor.impl.EditorImpl;
|
import com.intellij.openapi.editor.impl.EditorImpl;
|
||||||
import com.intellij.openapi.project.DumbService;
|
import com.intellij.openapi.project.DumbService;
|
||||||
@@ -36,19 +37,21 @@ import com.intellij.openapi.ui.popup.Balloon.Position;
|
|||||||
import com.intellij.openapi.util.Disposer;
|
import com.intellij.openapi.util.Disposer;
|
||||||
import com.intellij.openapi.util.Key;
|
import com.intellij.openapi.util.Key;
|
||||||
import com.intellij.openapi.util.Pair;
|
import com.intellij.openapi.util.Pair;
|
||||||
import com.intellij.psi.PsiDocumentManager;
|
import com.intellij.openapi.util.TextRange;
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.openapi.util.text.StringUtil;
|
||||||
import com.intellij.psi.PsiFile;
|
import com.intellij.psi.*;
|
||||||
import com.intellij.psi.TokenType;
|
|
||||||
import com.intellij.psi.util.PsiUtilBase;
|
import com.intellij.psi.util.PsiUtilBase;
|
||||||
import com.intellij.psi.util.PsiUtilCore;
|
import com.intellij.psi.util.PsiUtilCore;
|
||||||
|
import com.intellij.ui.HintHint;
|
||||||
import com.intellij.ui.LightweightHint;
|
import com.intellij.ui.LightweightHint;
|
||||||
import com.intellij.util.Alarm;
|
import com.intellij.util.Alarm;
|
||||||
import com.intellij.util.containers.JBIterable;
|
import com.intellij.util.containers.JBIterable;
|
||||||
import com.intellij.util.messages.MessageBusConnection;
|
import com.intellij.util.messages.MessageBusConnection;
|
||||||
import com.intellij.util.text.CharArrayUtil;
|
import com.intellij.util.text.CharArrayUtil;
|
||||||
|
import com.intellij.util.ui.UIUtil;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.annotations.TestOnly;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
@@ -56,6 +59,8 @@ import java.beans.PropertyChangeEvent;
|
|||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
public class ParameterInfoController implements Disposable {
|
public class ParameterInfoController implements Disposable {
|
||||||
private final Project myProject;
|
private final Project myProject;
|
||||||
@@ -64,15 +69,16 @@ public class ParameterInfoController implements Disposable {
|
|||||||
private final RangeMarker myLbraceMarker;
|
private final RangeMarker myLbraceMarker;
|
||||||
private final LightweightHint myHint;
|
private final LightweightHint myHint;
|
||||||
private final ParameterInfoComponent myComponent;
|
private final ParameterInfoComponent myComponent;
|
||||||
|
private final boolean myKeepOnHintHidden;
|
||||||
|
|
||||||
private final CaretListener myEditorCaretListener;
|
private final CaretListener myEditorCaretListener;
|
||||||
@NotNull private final ParameterInfoHandler<Object, Object> myHandler;
|
@NotNull private final ParameterInfoHandler<Object, Object> myHandler;
|
||||||
private final ShowParameterInfoHandler.BestLocationPointProvider myProvider;
|
private final MyBestLocationPointProvider myProvider;
|
||||||
|
|
||||||
private final Alarm myAlarm = new Alarm();
|
private final Alarm myAlarm = new Alarm();
|
||||||
private static final int DELAY = 200;
|
private static final int DELAY = 200;
|
||||||
|
|
||||||
private boolean myDisposed = false;
|
private boolean myDisposed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keeps Vector of ParameterInfoController's in Editor
|
* Keeps Vector of ParameterInfoController's in Editor
|
||||||
@@ -85,7 +91,7 @@ public class ParameterInfoController implements Disposable {
|
|||||||
ParameterInfoController controller = allControllers.get(i);
|
ParameterInfoController controller = allControllers.get(i);
|
||||||
|
|
||||||
if (controller.myLbraceMarker.getStartOffset() == offset) {
|
if (controller.myLbraceMarker.getStartOffset() == offset) {
|
||||||
if (controller.myHint.isVisible()) return controller;
|
if (controller.myKeepOnHintHidden || controller.myHint.isVisible()) return controller;
|
||||||
Disposer.dispose(controller);
|
Disposer.dispose(controller);
|
||||||
--i;
|
--i;
|
||||||
}
|
}
|
||||||
@@ -103,27 +109,40 @@ public class ParameterInfoController implements Disposable {
|
|||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isShownForEditor(@NotNull Editor editor) {
|
public static boolean existsForEditor(@NotNull Editor editor) {
|
||||||
return !getAllControllers(editor).isEmpty();
|
return !getAllControllers(editor).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isAlreadyShown(Editor editor, int lbraceOffset) {
|
public static boolean isAlreadyShown(Editor editor, int lbraceOffset) {
|
||||||
return findControllerAtOffset(editor, lbraceOffset) != null;
|
ParameterInfoController controller = findControllerAtOffset(editor, lbraceOffset);
|
||||||
|
return controller != null && controller.myHint.isVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ParameterInfoController(@NotNull Project project,
|
public ParameterInfoController(@NotNull Project project,
|
||||||
@NotNull Editor editor,
|
@NotNull Editor editor,
|
||||||
int lbraceOffset,
|
int lbraceOffset,
|
||||||
@NotNull LightweightHint hint,
|
Object[] descriptors,
|
||||||
|
Object highlighted,
|
||||||
|
PsiElement parameterOwner,
|
||||||
@NotNull ParameterInfoHandler handler,
|
@NotNull ParameterInfoHandler handler,
|
||||||
@NotNull ShowParameterInfoHandler.BestLocationPointProvider provider) {
|
boolean showHint,
|
||||||
|
boolean requestFocus) {
|
||||||
myProject = project;
|
myProject = project;
|
||||||
myEditor = editor;
|
myEditor = editor;
|
||||||
myHandler = handler;
|
myHandler = handler;
|
||||||
myProvider = provider;
|
myProvider = new MyBestLocationPointProvider(editor);
|
||||||
myLbraceMarker = editor.getDocument().createRangeMarker(lbraceOffset, lbraceOffset);
|
myLbraceMarker = editor.getDocument().createRangeMarker(lbraceOffset, lbraceOffset);
|
||||||
myHint = hint;
|
myComponent = new ParameterInfoComponent(descriptors, editor, handler, requestFocus);
|
||||||
myComponent = (ParameterInfoComponent)myHint.getComponent();
|
myHint = new LightweightHint(myComponent);
|
||||||
|
myKeepOnHintHidden = !showHint;
|
||||||
|
|
||||||
|
myHint.setSelectingHint(true);
|
||||||
|
myComponent.setParameterOwner(parameterOwner);
|
||||||
|
myComponent.setHighlightedParameter(highlighted);
|
||||||
|
myComponent.update(); // to have correct preferred size
|
||||||
|
if (showHint) {
|
||||||
|
showHint(requestFocus);
|
||||||
|
}
|
||||||
|
|
||||||
List<ParameterInfoController> allControllers = getAllControllers(myEditor);
|
List<ParameterInfoController> allControllers = getAllControllers(myEditor);
|
||||||
allControllers.add(this);
|
allControllers.add(this);
|
||||||
@@ -175,12 +194,26 @@ public class ParameterInfoController implements Disposable {
|
|||||||
public void dispose(){
|
public void dispose(){
|
||||||
if (myDisposed) return;
|
if (myDisposed) return;
|
||||||
myDisposed = true;
|
myDisposed = true;
|
||||||
|
myHint.hide();
|
||||||
List<ParameterInfoController> allControllers = getAllControllers(myEditor);
|
List<ParameterInfoController> allControllers = getAllControllers(myEditor);
|
||||||
allControllers.remove(this);
|
allControllers.remove(this);
|
||||||
myEditor.getCaretModel().removeCaretListener(myEditorCaretListener);
|
myEditor.getCaretModel().removeCaretListener(myEditorCaretListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void showHint(boolean requestFocus) {
|
||||||
|
Pair<Point, Short> pos = myProvider.getBestPointPosition(myHint, myComponent.getParameterOwner(), myLbraceMarker.getStartOffset(), true, HintManager.UNDER);
|
||||||
|
HintHint hintHint = HintManagerImpl.createHintHint(myEditor, pos.getFirst(), myHint, pos.getSecond());
|
||||||
|
hintHint.setExplicitClose(true);
|
||||||
|
hintHint.setRequestFocus(requestFocus);
|
||||||
|
|
||||||
|
Editor editorToShow = myEditor instanceof EditorWindow ? ((EditorWindow)myEditor).getDelegate() : myEditor;
|
||||||
|
// is case of injection we need to calculate position for EditorWindow
|
||||||
|
// also we need to show the hint in the main editor because of intention bulb
|
||||||
|
HintManagerImpl.getInstanceImpl().showEditorHint(myHint, editorToShow, pos.getFirst(), HintManager.HIDE_BY_ESCAPE | HintManager.UPDATE_BY_SCROLLING, 0, false, hintHint);
|
||||||
|
|
||||||
|
updateComponent();
|
||||||
|
}
|
||||||
|
|
||||||
private void adjustPositionForLookup(@NotNull Lookup lookup) {
|
private void adjustPositionForLookup(@NotNull Lookup lookup) {
|
||||||
if (!myHint.isVisible() || myEditor.isDisposed()) {
|
if (!myHint.isVisible() || myEditor.isDisposed()) {
|
||||||
Disposer.dispose(this);
|
Disposer.dispose(this);
|
||||||
@@ -221,8 +254,34 @@ public class ParameterInfoController implements Disposable {
|
|||||||
myAlarm.addRequest(request, DELAY, ModalityState.stateForComponent(myEditor.getComponent()));
|
myAlarm.addRequest(request, DELAY, ModalityState.stateForComponent(myEditor.getComponent()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateComponent(){
|
public void updateComponent(){
|
||||||
if (!myHint.isVisible()){
|
if (myKeepOnHintHidden) {
|
||||||
|
boolean removeHints = true;
|
||||||
|
PsiElement owner = myComponent.getParameterOwner();
|
||||||
|
if (owner != null && owner.isValid()) {
|
||||||
|
int caretOffset = myEditor.getCaretModel().getOffset();
|
||||||
|
TextRange ownerTextRange = owner.getTextRange();
|
||||||
|
if (ownerTextRange != null) {
|
||||||
|
if (caretOffset > ownerTextRange.getStartOffset() && caretOffset < ownerTextRange.getEndOffset()) {
|
||||||
|
removeHints = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (PsiElement element : owner.getChildren()) {
|
||||||
|
if (element instanceof PsiErrorElement) {
|
||||||
|
removeHints = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (removeHints) {
|
||||||
|
Disposer.dispose(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!myHint.isVisible() && !myKeepOnHintHidden && !ApplicationManager.getApplication().isUnitTestMode()) {
|
||||||
Disposer.dispose(this);
|
Disposer.dispose(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -290,12 +349,26 @@ public class ParameterInfoController implements Disposable {
|
|||||||
PsiElement argsList = findArgumentList(file, offset, -1);
|
PsiElement argsList = findArgumentList(file, offset, -1);
|
||||||
if (argsList == null) return;
|
if (argsList == null) return;
|
||||||
|
|
||||||
myEditor.getCaretModel().moveToOffset(offset);
|
offset = adjustOffsetToInlay(offset);
|
||||||
|
|
||||||
|
myEditor.getCaretModel().moveToLogicalPosition(myEditor.offsetToLogicalPosition(offset).leanForward(true));
|
||||||
myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
|
myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
|
||||||
myEditor.getSelectionModel().removeSelection();
|
myEditor.getSelectionModel().removeSelection();
|
||||||
myHandler.updateParameterInfo(argsList, new MyUpdateParameterInfoContext(offset, file));
|
myHandler.updateParameterInfo(argsList, new MyUpdateParameterInfoContext(offset, file));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int adjustOffsetToInlay(int offset) {
|
||||||
|
CharSequence text = myEditor.getDocument().getImmutableCharSequence();
|
||||||
|
String whitespaceChars = " \t";
|
||||||
|
int whitespaceStart = CharArrayUtil.shiftBackward(text, offset, whitespaceChars) + 1;
|
||||||
|
int whitespaceEnd = CharArrayUtil.shiftForward(text, offset, whitespaceChars);
|
||||||
|
List<Inlay> inlays = myEditor.getInlayModel().getInlineElementsInRange(whitespaceStart, whitespaceEnd);
|
||||||
|
for (Inlay inlay : inlays) {
|
||||||
|
if (ParameterHintsPresentationManager.getInstance().isParameterHint(inlay)) return inlay.getOffset();
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
private int getPrevOrNextParameterOffset(boolean isNext) {
|
private int getPrevOrNextParameterOffset(boolean isNext) {
|
||||||
if (!(myHandler instanceof ParameterInfoHandlerWithTabActionSupport)) return -1;
|
if (!(myHandler instanceof ParameterInfoHandlerWithTabActionSupport)) return -1;
|
||||||
ParameterInfoHandlerWithTabActionSupport handler = (ParameterInfoHandlerWithTabActionSupport)myHandler;
|
ParameterInfoHandlerWithTabActionSupport handler = (ParameterInfoHandlerWithTabActionSupport)myHandler;
|
||||||
@@ -338,7 +411,93 @@ public class ParameterInfoController implements Disposable {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MyUpdateParameterInfoContext implements UpdateParameterInfoContext {
|
public Object[] getObjects() {
|
||||||
|
return myComponent.getObjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getHighlighted() {
|
||||||
|
return myComponent.getHighlighted();
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestOnly
|
||||||
|
public static void waitForDelayedActions(@NotNull Editor editor, long timeout, @NotNull TimeUnit unit) throws TimeoutException {
|
||||||
|
long deadline = System.currentTimeMillis() + unit.toMillis(timeout);
|
||||||
|
while (System.currentTimeMillis() < deadline) {
|
||||||
|
List<ParameterInfoController> controllers = getAllControllers(editor);
|
||||||
|
boolean hasPendingRequests = false;
|
||||||
|
for (ParameterInfoController controller : controllers) {
|
||||||
|
if (!controller.myAlarm.isEmpty()) {
|
||||||
|
hasPendingRequests = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasPendingRequests) UIUtil.dispatchAllInvocationEvents();
|
||||||
|
else return;
|
||||||
|
}
|
||||||
|
throw new TimeoutException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Point in layered pane coordinate system
|
||||||
|
*/
|
||||||
|
static Pair<Point, Short> chooseBestHintPosition(Project project,
|
||||||
|
Editor editor,
|
||||||
|
LogicalPosition pos,
|
||||||
|
LightweightHint hint,
|
||||||
|
boolean awtTooltip, short preferredPosition) {
|
||||||
|
if (ApplicationManager.getApplication().isUnitTestMode()) return Pair.pair(new Point(), HintManager.DEFAULT);
|
||||||
|
|
||||||
|
HintManagerImpl hintManager = HintManagerImpl.getInstanceImpl();
|
||||||
|
Dimension hintSize = hint.getComponent().getPreferredSize();
|
||||||
|
JComponent editorComponent = editor.getComponent();
|
||||||
|
JLayeredPane layeredPane = editorComponent.getRootPane().getLayeredPane();
|
||||||
|
|
||||||
|
Point p1;
|
||||||
|
Point p2;
|
||||||
|
boolean isLookupShown = LookupManager.getInstance(project).getActiveLookup() != null;
|
||||||
|
if (isLookupShown) {
|
||||||
|
p1 = hintManager.getHintPosition(hint, editor, HintManager.UNDER);
|
||||||
|
p2 = hintManager.getHintPosition(hint, editor, HintManager.ABOVE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
p1 = HintManagerImpl.getHintPosition(hint, editor, pos, HintManager.UNDER);
|
||||||
|
p2 = HintManagerImpl.getHintPosition(hint, editor, pos, HintManager.ABOVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!awtTooltip) {
|
||||||
|
p1.x = Math.min(p1.x, layeredPane.getWidth() - hintSize.width);
|
||||||
|
p1.x = Math.max(p1.x, 0);
|
||||||
|
p2.x = Math.min(p2.x, layeredPane.getWidth() - hintSize.width);
|
||||||
|
p2.x = Math.max(p2.x, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean p1Ok = p1.y + hintSize.height < layeredPane.getHeight();
|
||||||
|
boolean p2Ok = p2.y >= 0;
|
||||||
|
|
||||||
|
if (isLookupShown) {
|
||||||
|
if (p1Ok) return new Pair<>(p1, HintManager.UNDER);
|
||||||
|
if (p2Ok) return new Pair<>(p2, HintManager.ABOVE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (preferredPosition != HintManager.DEFAULT) {
|
||||||
|
if (preferredPosition == HintManager.ABOVE) {
|
||||||
|
if (p2Ok) return new Pair<>(p2, HintManager.ABOVE);
|
||||||
|
} else if (preferredPosition == HintManager.UNDER) {
|
||||||
|
if (p1Ok) return new Pair<>(p1, HintManager.UNDER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p1Ok) return new Pair<>(p1, HintManager.UNDER);
|
||||||
|
if (p2Ok) return new Pair<>(p2, HintManager.ABOVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int underSpace = layeredPane.getHeight() - p1.y;
|
||||||
|
int aboveSpace = p2.y;
|
||||||
|
return aboveSpace > underSpace ? new Pair<>(new Point(p2.x, 0), HintManager.UNDER) : new Pair<>(p1,
|
||||||
|
HintManager.ABOVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MyUpdateParameterInfoContext implements UpdateParameterInfoContext {
|
||||||
private final int myOffset;
|
private final int myOffset;
|
||||||
private final PsiFile myFile;
|
private final PsiFile myFile;
|
||||||
|
|
||||||
@@ -415,4 +574,46 @@ public class ParameterInfoController implements Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class MyBestLocationPointProvider {
|
||||||
|
private final Editor myEditor;
|
||||||
|
private int previousOffset = -1;
|
||||||
|
private Point previousBestPoint;
|
||||||
|
private Short previousBestPosition;
|
||||||
|
|
||||||
|
public MyBestLocationPointProvider(final Editor editor) {
|
||||||
|
myEditor = editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public Pair<Point, Short> getBestPointPosition(LightweightHint hint,
|
||||||
|
final PsiElement list,
|
||||||
|
int offset,
|
||||||
|
final boolean awtTooltip,
|
||||||
|
short preferredPosition) {
|
||||||
|
if (list != null) {
|
||||||
|
TextRange range = list.getTextRange();
|
||||||
|
if (!range.contains(offset)) {
|
||||||
|
offset = range.getStartOffset() + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (previousOffset == offset) return Pair.create(previousBestPoint, previousBestPosition);
|
||||||
|
|
||||||
|
final boolean isMultiline = list != null && StringUtil.containsAnyChar(list.getText(), "\n\r");
|
||||||
|
final LogicalPosition pos = myEditor.offsetToLogicalPosition(offset).leanForward(true);
|
||||||
|
Pair<Point, Short> position;
|
||||||
|
|
||||||
|
if (!isMultiline) {
|
||||||
|
position = chooseBestHintPosition(myEditor.getProject(), myEditor, pos, hint, awtTooltip, preferredPosition);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Point p = HintManagerImpl.getHintPosition(hint, myEditor, pos, HintManager.ABOVE);
|
||||||
|
position = new Pair<>(p, HintManager.ABOVE);
|
||||||
|
}
|
||||||
|
previousBestPoint = position.getFirst();
|
||||||
|
previousBestPosition = position.getSecond();
|
||||||
|
previousOffset = offset;
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2000-2009 JetBrains s.r.o.
|
* Copyright 2000-2017 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -40,7 +40,7 @@ public class PrevNextParameterHandler extends EditorActionHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isEnabledForCaret(@NotNull Editor editor, @NotNull Caret caret, DataContext dataContext) {
|
protected boolean isEnabledForCaret(@NotNull Editor editor, @NotNull Caret caret, DataContext dataContext) {
|
||||||
if (!ParameterInfoController.isShownForEditor(editor)) return false;
|
if (!ParameterInfoController.existsForEditor(editor)) return false;
|
||||||
|
|
||||||
Project project = CommonDataKeys.PROJECT.getData(dataContext);
|
Project project = CommonDataKeys.PROJECT.getData(dataContext);
|
||||||
if (project == null) return false;
|
if (project == null) return false;
|
||||||
@@ -49,7 +49,7 @@ public class PrevNextParameterHandler extends EditorActionHandler {
|
|||||||
if (exprList == null) return false;
|
if (exprList == null) return false;
|
||||||
|
|
||||||
int lbraceOffset = exprList.getTextRange().getStartOffset();
|
int lbraceOffset = exprList.getTextRange().getStartOffset();
|
||||||
return ParameterInfoController.isAlreadyShown(editor, lbraceOffset) &&
|
return ParameterInfoController.findControllerAtOffset(editor, lbraceOffset) != null &&
|
||||||
ParameterInfoController.hasPrevOrNextParameter(editor, lbraceOffset, myIsNextParameterHandler);
|
ParameterInfoController.hasPrevOrNextParameter(editor, lbraceOffset, myIsNextParameterHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-138
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2000-2014 JetBrains s.r.o.
|
* Copyright 2000-2017 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -15,29 +15,18 @@
|
|||||||
*/
|
*/
|
||||||
package com.intellij.codeInsight.hint;
|
package com.intellij.codeInsight.hint;
|
||||||
|
|
||||||
import com.intellij.codeInsight.lookup.LookupManager;
|
|
||||||
import com.intellij.injected.editor.EditorWindow;
|
|
||||||
import com.intellij.lang.parameterInfo.CreateParameterInfoContext;
|
import com.intellij.lang.parameterInfo.CreateParameterInfoContext;
|
||||||
import com.intellij.lang.parameterInfo.ParameterInfoHandler;
|
import com.intellij.lang.parameterInfo.ParameterInfoHandler;
|
||||||
import com.intellij.openapi.editor.Document;
|
import com.intellij.openapi.editor.Document;
|
||||||
import com.intellij.openapi.editor.Editor;
|
import com.intellij.openapi.editor.Editor;
|
||||||
import com.intellij.openapi.editor.LogicalPosition;
|
|
||||||
import com.intellij.openapi.project.DumbService;
|
import com.intellij.openapi.project.DumbService;
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
import com.intellij.openapi.util.Pair;
|
|
||||||
import com.intellij.openapi.util.TextRange;
|
|
||||||
import com.intellij.openapi.util.text.StringUtil;
|
|
||||||
import com.intellij.psi.PsiDocumentManager;
|
import com.intellij.psi.PsiDocumentManager;
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
import com.intellij.psi.PsiFile;
|
import com.intellij.psi.PsiFile;
|
||||||
import com.intellij.ui.HintHint;
|
|
||||||
import com.intellij.ui.LightweightHint;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author peter
|
* @author peter
|
||||||
*/
|
*/
|
||||||
@@ -131,20 +120,6 @@ public class ShowParameterInfoContext implements CreateParameterInfoContext {
|
|||||||
if (ParameterInfoController.isAlreadyShown(editor, elementStart)) return;
|
if (ParameterInfoController.isAlreadyShown(editor, elementStart)) return;
|
||||||
|
|
||||||
if (editor.isDisposed() || !editor.getComponent().isVisible()) return;
|
if (editor.isDisposed() || !editor.getComponent().isVisible()) return;
|
||||||
final ParameterInfoComponent component = new ParameterInfoComponent(descriptors, editor,handler,requestFocus);
|
|
||||||
component.setParameterOwner(element);
|
|
||||||
component.setRequestFocus(requestFocus);
|
|
||||||
if (highlighted != null) {
|
|
||||||
component.setHighlightedParameter(highlighted);
|
|
||||||
}
|
|
||||||
|
|
||||||
component.update(); // to have correct preferred size
|
|
||||||
|
|
||||||
final LightweightHint hint = new LightweightHint(component);
|
|
||||||
hint.setSelectingHint(true);
|
|
||||||
final HintManagerImpl hintManager = HintManagerImpl.getInstanceImpl();
|
|
||||||
final ShowParameterInfoHandler.BestLocationPointProvider provider = new MyBestLocationPointProvider(editor);
|
|
||||||
final Pair<Point, Short> pos = provider.getBestPointPosition(hint, element, elementStart, true, HintManager.UNDER);
|
|
||||||
|
|
||||||
PsiDocumentManager.getInstance(project).performLaterWhenAllCommitted(() -> {
|
PsiDocumentManager.getInstance(project).performLaterWhenAllCommitted(() -> {
|
||||||
if (editor.isDisposed() || DumbService.isDumb(project)) return;
|
if (editor.isDisposed() || DumbService.isDumb(project)) return;
|
||||||
@@ -152,15 +127,13 @@ public class ShowParameterInfoContext implements CreateParameterInfoContext {
|
|||||||
final Document document = editor.getDocument();
|
final Document document = editor.getDocument();
|
||||||
if (document.getTextLength() < elementStart) return;
|
if (document.getTextLength() < elementStart) return;
|
||||||
|
|
||||||
HintHint hintHint = HintManagerImpl.createHintHint(editor, pos.getFirst(), hint, pos.getSecond());
|
ParameterInfoController controller = ParameterInfoController.findControllerAtOffset(editor, elementStart);
|
||||||
hintHint.setExplicitClose(true);
|
if (controller == null) {
|
||||||
hintHint.setRequestFocus(requestFocus);
|
new ParameterInfoController(project, editor, elementStart, descriptors, highlighted, element, handler, true, requestFocus);
|
||||||
|
}
|
||||||
Editor editorToShow = editor instanceof EditorWindow ? ((EditorWindow)editor).getDelegate() : editor;
|
else {
|
||||||
// is case of injection we need to calculate position for EditorWindow
|
controller.showHint(requestFocus);
|
||||||
// also we need to show the hint in the main editor because of intention bulb
|
}
|
||||||
hintManager.showEditorHint(hint, editorToShow, pos.getFirst(), HintManager.HIDE_BY_ESCAPE | HintManager.UPDATE_BY_SCROLLING, 0, false, hintHint);
|
|
||||||
new ParameterInfoController(project, editor, elementStart, hint, handler, provider);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,66 +148,6 @@ public class ShowParameterInfoContext implements CreateParameterInfoContext {
|
|||||||
showParameterHint(list, editor, candidates, project, candidates.length > 1 ? highlighted : null, offset, handler, requestFocus);
|
showParameterHint(list, editor, candidates, project, candidates.length > 1 ? highlighted : null, offset, handler, requestFocus);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Point in layered pane coordinate system
|
|
||||||
*/
|
|
||||||
static Pair<Point, Short> chooseBestHintPosition(Project project,
|
|
||||||
Editor editor,
|
|
||||||
int line,
|
|
||||||
int col,
|
|
||||||
LightweightHint hint,
|
|
||||||
boolean awtTooltip, short preferredPosition) {
|
|
||||||
HintManagerImpl hintManager = HintManagerImpl.getInstanceImpl();
|
|
||||||
Dimension hintSize = hint.getComponent().getPreferredSize();
|
|
||||||
JComponent editorComponent = editor.getComponent();
|
|
||||||
JLayeredPane layeredPane = editorComponent.getRootPane().getLayeredPane();
|
|
||||||
|
|
||||||
Point p1;
|
|
||||||
Point p2;
|
|
||||||
boolean isLookupShown = LookupManager.getInstance(project).getActiveLookup() != null;
|
|
||||||
if (isLookupShown) {
|
|
||||||
p1 = hintManager.getHintPosition(hint, editor, HintManager.UNDER);
|
|
||||||
p2 = hintManager.getHintPosition(hint, editor, HintManager.ABOVE);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LogicalPosition pos = new LogicalPosition(line, col);
|
|
||||||
p1 = HintManagerImpl.getHintPosition(hint, editor, pos, HintManager.UNDER);
|
|
||||||
p2 = HintManagerImpl.getHintPosition(hint, editor, pos, HintManager.ABOVE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!awtTooltip) {
|
|
||||||
p1.x = Math.min(p1.x, layeredPane.getWidth() - hintSize.width);
|
|
||||||
p1.x = Math.max(p1.x, 0);
|
|
||||||
p2.x = Math.min(p2.x, layeredPane.getWidth() - hintSize.width);
|
|
||||||
p2.x = Math.max(p2.x, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean p1Ok = p1.y + hintSize.height < layeredPane.getHeight();
|
|
||||||
boolean p2Ok = p2.y >= 0;
|
|
||||||
|
|
||||||
if (isLookupShown) {
|
|
||||||
if (p1Ok) return new Pair<>(p1, HintManager.UNDER);
|
|
||||||
if (p2Ok) return new Pair<>(p2, HintManager.ABOVE);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (preferredPosition != HintManager.DEFAULT) {
|
|
||||||
if (preferredPosition == HintManager.ABOVE) {
|
|
||||||
if (p2Ok) return new Pair<>(p2, HintManager.ABOVE);
|
|
||||||
} else if (preferredPosition == HintManager.UNDER) {
|
|
||||||
if (p1Ok) return new Pair<>(p1, HintManager.UNDER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p1Ok) return new Pair<>(p1, HintManager.UNDER);
|
|
||||||
if (p2Ok) return new Pair<>(p2, HintManager.ABOVE);
|
|
||||||
}
|
|
||||||
|
|
||||||
int underSpace = layeredPane.getHeight() - p1.y;
|
|
||||||
int aboveSpace = p2.y;
|
|
||||||
return aboveSpace > underSpace ? new Pair<>(new Point(p2.x, 0), HintManager.UNDER) : new Pair<>(p1,
|
|
||||||
HintManager.ABOVE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRequestFocus(boolean requestFocus) {
|
public void setRequestFocus(boolean requestFocus) {
|
||||||
myRequestFocus = requestFocus;
|
myRequestFocus = requestFocus;
|
||||||
}
|
}
|
||||||
@@ -242,47 +155,4 @@ public class ShowParameterInfoContext implements CreateParameterInfoContext {
|
|||||||
public boolean isRequestFocus() {
|
public boolean isRequestFocus() {
|
||||||
return myRequestFocus;
|
return myRequestFocus;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class MyBestLocationPointProvider implements ShowParameterInfoHandler.BestLocationPointProvider {
|
|
||||||
private final Editor myEditor;
|
|
||||||
private int previousOffset = -1;
|
|
||||||
private Point previousBestPoint;
|
|
||||||
private Short previousBestPosition;
|
|
||||||
|
|
||||||
public MyBestLocationPointProvider(final Editor editor) {
|
|
||||||
myEditor = editor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@NotNull
|
|
||||||
public Pair<Point, Short> getBestPointPosition(LightweightHint hint,
|
|
||||||
final PsiElement list,
|
|
||||||
int offset,
|
|
||||||
final boolean awtTooltip,
|
|
||||||
short preferredPosition) {
|
|
||||||
if (list != null) {
|
|
||||||
TextRange range = list.getTextRange();
|
|
||||||
if (!range.contains(offset)) {
|
|
||||||
offset = range.getStartOffset() + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (previousOffset == offset) return Pair.create(previousBestPoint, previousBestPosition);
|
|
||||||
|
|
||||||
final boolean isMultiline = list != null && StringUtil.containsAnyChar(list.getText(), "\n\r");
|
|
||||||
final LogicalPosition pos = myEditor.offsetToLogicalPosition(offset);
|
|
||||||
Pair<Point, Short> position;
|
|
||||||
|
|
||||||
if (!isMultiline) {
|
|
||||||
position = chooseBestHintPosition(myEditor.getProject(), myEditor, pos.line, pos.column, hint, awtTooltip, preferredPosition);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Point p = HintManagerImpl.getHintPosition(hint, myEditor, pos, HintManager.ABOVE);
|
|
||||||
position = new Pair<>(p, HintManager.ABOVE);
|
|
||||||
}
|
|
||||||
previousBestPoint = position.getFirst();
|
|
||||||
previousBestPosition = position.getSecond();
|
|
||||||
previousOffset = offset;
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -142,7 +142,7 @@ public class ShowParameterInfoHandler implements CodeInsightActionHandler {
|
|||||||
final LightweightHint hint = new LightweightHint(component);
|
final LightweightHint hint = new LightweightHint(component);
|
||||||
hint.setSelectingHint(true);
|
hint.setSelectingHint(true);
|
||||||
final HintManagerImpl hintManager = HintManagerImpl.getInstanceImpl();
|
final HintManagerImpl hintManager = HintManagerImpl.getInstanceImpl();
|
||||||
final Pair<Point, Short> pos = ShowParameterInfoContext.chooseBestHintPosition(project, editor, -1, -1, hint, true, HintManager.DEFAULT);
|
final Pair<Point, Short> pos = ParameterInfoController.chooseBestHintPosition(project, editor, null, hint, true, HintManager.DEFAULT);
|
||||||
ApplicationManager.getApplication().invokeLater(() -> {
|
ApplicationManager.getApplication().invokeLater(() -> {
|
||||||
if (!editor.getComponent().isShowing()) return;
|
if (!editor.getComponent().isShowing()) return;
|
||||||
hintManager.showEditorHint(hint, editor, pos.getFirst(),
|
hintManager.showEditorHint(hint, editor, pos.getFirst(),
|
||||||
@@ -160,15 +160,5 @@ public class ShowParameterInfoHandler implements CodeInsightActionHandler {
|
|||||||
if (handlers.isEmpty()) return null;
|
if (handlers.isEmpty()) return null;
|
||||||
return handlers.toArray(new ParameterInfoHandler[handlers.size()]);
|
return handlers.toArray(new ParameterInfoHandler[handlers.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BestLocationPointProvider {
|
|
||||||
@NotNull
|
|
||||||
Pair<Point, Short> getBestPointPosition(LightweightHint hint,
|
|
||||||
final PsiElement list,
|
|
||||||
int offset,
|
|
||||||
final boolean awtTooltip,
|
|
||||||
short preferredPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -146,7 +146,7 @@ public class ParameterHintsPassFactory extends AbstractProjectComponent implemen
|
|||||||
String newText = myAnnotations.remove(offset);
|
String newText = myAnnotations.remove(offset);
|
||||||
String oldText = presentationManager.getHintText(inlay);
|
String oldText = presentationManager.getHintText(inlay);
|
||||||
|
|
||||||
if (delayRemoval(inlay, caretMap)) continue;
|
if (delayRemoval(inlay, caretMap) || presentationManager.isPinned(inlay)) continue;
|
||||||
if (!Objects.equals(newText, oldText)) {
|
if (!Objects.equals(newText, oldText)) {
|
||||||
if (newText == null) {
|
if (newText == null) {
|
||||||
removedHints.add(oldText);
|
removedHints.add(oldText);
|
||||||
@@ -160,7 +160,7 @@ public class ParameterHintsPassFactory extends AbstractProjectComponent implemen
|
|||||||
for (Map.Entry<Integer, String> e : myAnnotations.entrySet()) {
|
for (Map.Entry<Integer, String> e : myAnnotations.entrySet()) {
|
||||||
int offset = e.getKey();
|
int offset = e.getKey();
|
||||||
String text = e.getValue();
|
String text = e.getValue();
|
||||||
presentationManager.addHint(myEditor, offset, text, !firstTime && !removedHints.contains(text));
|
presentationManager.addHint(myEditor, offset, text, !firstTime && !removedHints.contains(text), false);
|
||||||
}
|
}
|
||||||
keeper.restoreOriginalLocation();
|
keeper.restoreOriginalLocation();
|
||||||
myEditor.putUserData(REPEATED_PASS, Boolean.TRUE);
|
myEditor.putUserData(REPEATED_PASS, Boolean.TRUE);
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -654,23 +654,19 @@ public class HintManagerImpl extends HintManager implements Disposable {
|
|||||||
@PositionFlags short constraint,
|
@PositionFlags short constraint,
|
||||||
boolean showByBalloon) {
|
boolean showByBalloon) {
|
||||||
Dimension hintSize = hint.getComponent().getPreferredSize();
|
Dimension hintSize = hint.getComponent().getPreferredSize();
|
||||||
int line1 = pos1.line;
|
|
||||||
int col1 = pos1.column;
|
|
||||||
int line2 = pos2.line;
|
|
||||||
int col2 = pos2.column;
|
|
||||||
|
|
||||||
Point location;
|
Point location;
|
||||||
JComponent externalComponent = getExternalComponent(editor);
|
JComponent externalComponent = getExternalComponent(editor);
|
||||||
JComponent internalComponent = editor.getContentComponent();
|
JComponent internalComponent = editor.getContentComponent();
|
||||||
if (constraint == RIGHT_UNDER) {
|
if (constraint == RIGHT_UNDER) {
|
||||||
Point p = editor.logicalPositionToXY(new LogicalPosition(line2, col2));
|
Point p = editor.logicalPositionToXY(pos2);
|
||||||
if (!showByBalloon) {
|
if (!showByBalloon) {
|
||||||
p.y += editor.getLineHeight();
|
p.y += editor.getLineHeight();
|
||||||
}
|
}
|
||||||
location = SwingUtilities.convertPoint(internalComponent, p, externalComponent);
|
location = SwingUtilities.convertPoint(internalComponent, p, externalComponent);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Point p = editor.logicalPositionToXY(new LogicalPosition(line1, col1));
|
Point p = editor.logicalPositionToXY(pos1);
|
||||||
if (constraint == UNDER) {
|
if (constraint == UNDER) {
|
||||||
p.y += editor.getLineHeight();
|
p.y += editor.getLineHeight();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,9 @@
|
|||||||
<action id="EditorLookupUp"><keyboard-shortcut first-keystroke="control UP"/></action>
|
<action id="EditorLookupUp"><keyboard-shortcut first-keystroke="control UP"/></action>
|
||||||
<action id="EditorLookupDown"><keyboard-shortcut first-keystroke="control DOWN"/></action>
|
<action id="EditorLookupDown"><keyboard-shortcut first-keystroke="control DOWN"/></action>
|
||||||
|
|
||||||
|
<action id="MethodOverloadSwitchUp"><keyboard-shortcut first-keystroke="control UP"/></action>
|
||||||
|
<action id="MethodOverloadSwitchDown"><keyboard-shortcut first-keystroke="control DOWN"/></action>
|
||||||
|
|
||||||
<action id="ReformatCode">
|
<action id="ReformatCode">
|
||||||
<keyboard-shortcut first-keystroke="control alt L"/>
|
<keyboard-shortcut first-keystroke="control alt L"/>
|
||||||
</action>
|
</action>
|
||||||
|
|||||||
+2
@@ -542,6 +542,8 @@ public interface CodeInsightTestFixture extends IdeaProjectTestFixture {
|
|||||||
|
|
||||||
void testInlays();
|
void testInlays();
|
||||||
|
|
||||||
|
void checkResultWithInlays(String text);
|
||||||
|
|
||||||
void assertPreferredCompletionItems(int selected, @NotNull String... expected);
|
void assertPreferredCompletionItems(int selected, @NotNull String... expected);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+10
@@ -27,6 +27,7 @@ import com.intellij.codeInsight.daemon.GutterMark;
|
|||||||
import com.intellij.codeInsight.daemon.impl.*;
|
import com.intellij.codeInsight.daemon.impl.*;
|
||||||
import com.intellij.codeInsight.folding.CodeFoldingManager;
|
import com.intellij.codeInsight.folding.CodeFoldingManager;
|
||||||
import com.intellij.codeInsight.highlighting.actions.HighlightUsagesAction;
|
import com.intellij.codeInsight.highlighting.actions.HighlightUsagesAction;
|
||||||
|
import com.intellij.codeInsight.hints.InlayInfo;
|
||||||
import com.intellij.codeInsight.intention.IntentionAction;
|
import com.intellij.codeInsight.intention.IntentionAction;
|
||||||
import com.intellij.codeInsight.intention.impl.IntentionListStep;
|
import com.intellij.codeInsight.intention.impl.IntentionListStep;
|
||||||
import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler;
|
import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler;
|
||||||
@@ -1705,6 +1706,15 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkResultWithInlays(String text) {
|
||||||
|
Document checkDocument = new DocumentImpl(text);
|
||||||
|
InlayHintsChecker checker = new InlayHintsChecker(this);
|
||||||
|
List<InlayInfo> inlayInfos = checker.extractInlays(checkDocument);
|
||||||
|
checkResult(checkDocument.getText());
|
||||||
|
checker.verifyInlays(inlayInfos, text);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void assertPreferredCompletionItems(final int selected, @NotNull final String... expected) {
|
public void assertPreferredCompletionItems(final int selected, @NotNull final String... expected) {
|
||||||
final LookupImpl lookup = getLookup();
|
final LookupImpl lookup = getLookup();
|
||||||
|
|||||||
+10
-5
@@ -17,7 +17,6 @@ package com.intellij.testFramework.utils.inlays
|
|||||||
|
|
||||||
import com.intellij.codeInsight.daemon.impl.ParameterHintsPresentationManager
|
import com.intellij.codeInsight.daemon.impl.ParameterHintsPresentationManager
|
||||||
import com.intellij.codeInsight.hints.InlayInfo
|
import com.intellij.codeInsight.hints.InlayInfo
|
||||||
import com.intellij.codeInsight.hints.InlayParameterHintsExtension
|
|
||||||
import com.intellij.codeInsight.hints.settings.ParameterNameHintsSettings
|
import com.intellij.codeInsight.hints.settings.ParameterNameHintsSettings
|
||||||
import com.intellij.openapi.command.WriteCommandAction
|
import com.intellij.openapi.command.WriteCommandAction
|
||||||
import com.intellij.openapi.editor.Document
|
import com.intellij.openapi.editor.Document
|
||||||
@@ -57,10 +56,17 @@ class InlayHintsChecker(private val myFixture: CodeInsightTestFixture) {
|
|||||||
val document = myFixture.getDocument(file)
|
val document = myFixture.getDocument(file)
|
||||||
val originalText = document.text
|
val originalText = document.text
|
||||||
val expectedInlays: List<InlayInfo> = extractInlays(document)
|
val expectedInlays: List<InlayInfo> = extractInlays(document)
|
||||||
|
myFixture.doHighlighting();
|
||||||
|
verifyInlays(expectedInlays, originalText)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyInlays(expectedInlays : List<InlayInfo>, originalText: String) {
|
||||||
|
val file = myFixture.file
|
||||||
|
val document = myFixture.getDocument(file)
|
||||||
val actual: List<Pair<Int, String>> = getActualInlays()
|
val actual: List<Pair<Int, String>> = getActualInlays()
|
||||||
|
|
||||||
val expected = expectedInlays.map { Pair(it.offset, it.text) }
|
val expected = expectedInlays.map { Pair(it.offset, it.text) }
|
||||||
|
|
||||||
if (expectedInlays.size != actual.size || actual.zip(expected).any { it.first != it.second }) {
|
if (expectedInlays.size != actual.size || actual.zip(expected).any { it.first != it.second }) {
|
||||||
val proposedText = StringBuilder(document.text)
|
val proposedText = StringBuilder(document.text)
|
||||||
actual.asReversed().forEach { proposedText.insert(it.first, "<hint text=\"${it.second}\" />") }
|
actual.asReversed().forEach { proposedText.insert(it.first, "<hint text=\"${it.second}\" />") }
|
||||||
@@ -70,9 +76,8 @@ class InlayHintsChecker(private val myFixture: CodeInsightTestFixture) {
|
|||||||
} ?: throw ComparisonFailure("Hints differ", originalText, proposedText.toString())
|
} ?: throw ComparisonFailure("Hints differ", originalText, proposedText.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getActualInlays(): List<Pair<Int, String>> {
|
private fun getActualInlays(): List<Pair<Int, String>> {
|
||||||
myFixture.doHighlighting()
|
|
||||||
val editor = myFixture.editor
|
val editor = myFixture.editor
|
||||||
val allInlays = editor.inlayModel.getInlineElementsInRange(0, editor.document.textLength)
|
val allInlays = editor.inlayModel.getInlineElementsInRange(0, editor.document.textLength)
|
||||||
|
|
||||||
|
|||||||
@@ -369,6 +369,9 @@ show.live.templates.in.completion.description=Show live templates in completion
|
|||||||
java.completion.make.outer.variables.final=true
|
java.completion.make.outer.variables.final=true
|
||||||
java.completion.make.outer.variables.final.description=Make variables accessed from inner class final automatically
|
java.completion.make.outer.variables.final.description=Make variables accessed from inner class final automatically
|
||||||
|
|
||||||
|
java.completion.argument.hints=false
|
||||||
|
java.completion.argument.hints.description=When completing a method call, show hints in place of all arguments
|
||||||
|
|
||||||
java.completion.argument.live.template=false
|
java.completion.argument.live.template=false
|
||||||
java.completion.argument.live.template.description=When completing a method call, start a live template with all arguments
|
java.completion.argument.live.template.description=When completing a method call, start a live template with all arguments
|
||||||
|
|
||||||
|
|||||||
@@ -1905,6 +1905,8 @@
|
|||||||
<java.refactoring.chainCallExtractor implementation="com.intellij.refactoring.chainCall.GuavaFluentIterableChainCallExtractor"/>
|
<java.refactoring.chainCallExtractor implementation="com.intellij.refactoring.chainCall.GuavaFluentIterableChainCallExtractor"/>
|
||||||
|
|
||||||
<diff.lang.DiffIgnoredRangeProvider implementation="com.intellij.diff.lang.JavaDiffIgnoredRangeProvider"/>
|
<diff.lang.DiffIgnoredRangeProvider implementation="com.intellij.diff.lang.JavaDiffIgnoredRangeProvider"/>
|
||||||
|
|
||||||
|
<actionPromoter implementation="com.intellij.codeInsight.editorActions.JavaMethodOverloadSwitchActionPromoter" />
|
||||||
</extensions>
|
</extensions>
|
||||||
|
|
||||||
<actions>
|
<actions>
|
||||||
@@ -1926,6 +1928,10 @@
|
|||||||
<action class="com.intellij.execution.testDiscovery.ConfigureTestDiscoveryAction" internal="true" id="TestDiscoveryIndexChooser" text="Choose external test discovery indices">
|
<action class="com.intellij.execution.testDiscovery.ConfigureTestDiscoveryAction" internal="true" id="TestDiscoveryIndexChooser" text="Choose external test discovery indices">
|
||||||
<add-to-group group-id="Internal" anchor="last"/>
|
<add-to-group group-id="Internal" anchor="last"/>
|
||||||
</action>
|
</action>
|
||||||
|
|
||||||
|
<action id="MethodOverloadSwitchUp" class="com.intellij.codeInsight.editorActions.JavaMethodOverloadSwitchUpAction"/>
|
||||||
|
<action id="MethodOverloadSwitchDown" class="com.intellij.codeInsight.editorActions.JavaMethodOverloadSwitchDownAction" />
|
||||||
|
|
||||||
</actions>
|
</actions>
|
||||||
|
|
||||||
</idea-plugin>
|
</idea-plugin>
|
||||||
|
|||||||
Reference in New Issue
Block a user