1 // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.codeInsight.hint.api.impls;
4 import com.intellij.codeInsight.AnnotationTargetUtil;
5 import com.intellij.codeInsight.AnnotationUtil;
6 import com.intellij.codeInsight.CodeInsightBundle;
7 import com.intellij.codeInsight.CodeInsightSettings;
8 import com.intellij.codeInsight.completion.CompletionMemory;
9 import com.intellij.codeInsight.completion.JavaCompletionUtil;
10 import com.intellij.codeInsight.completion.JavaMethodCallElement;
11 import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
12 import com.intellij.codeInsight.daemon.impl.ParameterHintsPresentationManager;
13 import com.intellij.codeInsight.hints.ParameterHintsPass;
14 import com.intellij.codeInsight.javadoc.JavaDocInfoGenerator;
15 import com.intellij.codeInsight.lookup.LookupElement;
16 import com.intellij.injected.editor.EditorWindow;
17 import com.intellij.lang.parameterInfo.*;
18 import com.intellij.openapi.editor.Document;
19 import com.intellij.openapi.editor.Editor;
20 import com.intellij.openapi.editor.Inlay;
21 import com.intellij.openapi.project.DumbAware;
22 import com.intellij.openapi.project.DumbService;
23 import com.intellij.openapi.util.Computable;
24 import com.intellij.openapi.util.Key;
25 import com.intellij.openapi.util.TextRange;
26 import com.intellij.openapi.util.UserDataHolder;
27 import com.intellij.openapi.util.registry.Registry;
28 import com.intellij.openapi.util.text.StringUtil;
29 import com.intellij.psi.*;
30 import com.intellij.psi.impl.PsiImplUtil;
31 import com.intellij.psi.impl.source.resolve.CompletionParameterTypeInferencePolicy;
32 import com.intellij.psi.infos.CandidateInfo;
33 import com.intellij.psi.infos.MethodCandidateInfo;
34 import com.intellij.psi.scope.MethodProcessorSetupFailedException;
35 import com.intellij.psi.scope.PsiConflictResolver;
36 import com.intellij.psi.scope.processor.MethodCandidatesProcessor;
37 import com.intellij.psi.scope.processor.MethodResolverProcessor;
38 import com.intellij.psi.scope.util.PsiScopesUtil;
39 import com.intellij.psi.tree.IElementType;
40 import com.intellij.psi.util.MethodSignatureUtil;
41 import com.intellij.util.DocumentUtil;
42 import com.intellij.util.ObjectUtils;
43 import com.intellij.util.containers.ContainerUtil;
44 import com.intellij.util.text.CharArrayUtil;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.annotations.Nullable;
50 import java.util.List;
53 * @author Maxim.Mossienko
55 public class MethodParameterInfoHandler implements ParameterInfoHandlerWithTabActionSupport<PsiExpressionList, Object, PsiExpression>, DumbAware {
56 private static final Set<Class> ourArgumentListAllowedParentClassesSet = ContainerUtil.newHashSet(
57 PsiMethodCallExpression.class, PsiNewExpression.class, PsiAnonymousClass.class, PsiEnumConstant.class);
58 private static final Set<? extends Class> ourStopSearch = Collections.singleton(PsiMethod.class);
59 private static final String WHITESPACE = " \t";
60 private static final Key<Inlay> CURRENT_HINT = Key.create("current.hint");
61 private static final Key<List<Inlay>> HIGHLIGHTED_HINTS = Key.create("highlighted.hints");
64 public Object[] getParametersForLookup(LookupElement item, ParameterInfoContext context) {
65 final List<? extends PsiElement> elements = JavaCompletionUtil.getAllPsiElements(item);
66 return elements != null && !elements.isEmpty() && elements.get(0) instanceof PsiMethod ? elements.toArray() : null;
70 public boolean couldShowInLookup() {
76 public PsiExpressionList findElementForParameterInfo(@NotNull final CreateParameterInfoContext context) {
77 PsiExpressionList argumentList = findArgumentList(context.getFile(), context.getOffset(), context.getParameterListStart(), true);
79 if (argumentList != null) {
80 return findMethodsForArgumentList(context, argumentList);
85 private PsiExpressionList findArgumentList(final PsiFile file, int offset, int parameterStart, boolean allowOuter) {
86 PsiExpressionList argumentList = ParameterInfoUtils.findArgumentList(file, offset, parameterStart, this, allowOuter);
87 if (argumentList == null && allowOuter) {
88 PsiCall call = ParameterInfoUtils.findParentOfTypeWithStopElements(file, offset, PsiMethodCallExpression.class, PsiMethod.class);
90 call = ParameterInfoUtils.findParentOfTypeWithStopElements(file, offset, PsiNewExpression.class, PsiMethod.class);
93 argumentList = call.getArgumentList();
99 private static PsiExpressionList findMethodsForArgumentList(final CreateParameterInfoContext context,
100 @NotNull final PsiExpressionList argumentList) {
102 CandidateInfo[] candidates = getMethods(argumentList);
103 if (candidates.length == 0) {
104 DaemonCodeAnalyzer.getInstance(context.getProject()).updateVisibleHighlighters(context.getEditor());
107 context.setItemsToShow(candidates);
112 public void showParameterInfo(@NotNull final PsiExpressionList element, @NotNull final CreateParameterInfoContext context) {
113 context.showHint(element, element.getTextRange().getStartOffset(), this);
117 public PsiExpressionList findElementForUpdatingParameterInfo(@NotNull final UpdateParameterInfoContext context) {
118 if (context.isPreservedOnHintHidden() && isOutsideOfCompletedInvocation(context)) {
119 context.setPreservedOnHintHidden(false);
122 PsiExpressionList expressionList = findArgumentList(context.getFile(), context.getOffset(), context.getParameterListStart(), false);
123 if (expressionList != null) {
124 Object[] candidates = context.getObjectsToView();
125 if (candidates != null && candidates.length != 0) {
126 Object currentMethodInfo = context.getHighlightedParameter();
127 if (currentMethodInfo == null) currentMethodInfo = candidates[0];
128 PsiElement element = currentMethodInfo instanceof CandidateInfo ? ((CandidateInfo)currentMethodInfo).getElement() :
129 currentMethodInfo instanceof PsiElement ? (PsiElement) currentMethodInfo :
131 if ((element instanceof PsiMethod)) {
132 PsiMethod method = (PsiMethod)element;
133 PsiElement parent = expressionList.getParent();
135 String originalMethodName = method.getName();
136 PsiQualifiedReference currentMethodReference = null;
137 if (parent instanceof PsiMethodCallExpression && !method.isConstructor()) {
138 currentMethodReference = ((PsiMethodCallExpression)parent).getMethodExpression();
140 else if (parent instanceof PsiNewExpression) {
141 currentMethodReference = ((PsiNewExpression)parent).getClassReference();
143 else if (parent instanceof PsiAnonymousClass) {
144 currentMethodReference = ((PsiAnonymousClass)parent).getBaseClassReference();
146 if (currentMethodReference == null || originalMethodName.equals(currentMethodReference.getReferenceName())) {
148 int currentNumberOfParameters = expressionList.getExpressionCount();
149 PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(context.getProject());
150 Document document = psiDocumentManager.getCachedDocument(context.getFile());
151 if (parent instanceof PsiCallExpression && JavaMethodCallElement.isCompletionMode((PsiCall)parent)) {
152 PsiMethod chosenMethod = CompletionMemory.getChosenMethod((PsiCall)parent);
153 if ((context.getHighlightedParameter() != null || candidates.length == 1) && chosenMethod != null &&
154 document != null && psiDocumentManager.isCommitted(document) &&
155 isIncompatibleParameterCount(chosenMethod, currentNumberOfParameters)) {
156 JavaMethodCallElement.setCompletionMode((PsiCall)parent, false);
157 highlightHints(context.getEditor(), null, -1, context.getCustomContext());
160 int index = ParameterInfoUtils.getCurrentParameterIndex(expressionList.getNode(),
161 context.getOffset(), JavaTokenType.COMMA);
162 TextRange textRange = expressionList.getTextRange();
163 if (context.getOffset() <= textRange.getStartOffset() || context.getOffset() >= textRange.getEndOffset()) index = -1;
164 highlightHints(context.getEditor(), expressionList, context.isInnermostContext() ? index : -1, context.getCustomContext());
168 return expressionList;
173 highlightHints(context.getEditor(), null, -1, context.getCustomContext());
177 private static boolean isOutsideOfCompletedInvocation(UpdateParameterInfoContext context) {
178 PsiElement owner = context.getParameterOwner();
179 if (owner != null && owner.isValid()) {
180 TextRange ownerTextRange = getRelatedRange(owner, context.getEditor());
181 int caretOffset = context.getOffset();
182 if (ownerTextRange != null) {
183 if (caretOffset >= ownerTextRange.getStartOffset() && caretOffset <= ownerTextRange.getEndOffset()) {
187 for (PsiElement element : owner.getChildren()) {
188 if (element instanceof PsiErrorElement) return false;
190 if (owner instanceof PsiExpressionList && ((PsiExpressionList)owner).isEmpty()) {
191 PsiElement parent = owner.getParent();
192 if (parent instanceof PsiCall) {
193 PsiMethod chosenMethod = CompletionMemory.getChosenMethod((PsiCall)parent);
194 if (chosenMethod != null) {
195 int parametersCount = chosenMethod.getParameterList().getParametersCount();
196 if ((parametersCount == 1 && !chosenMethod.isVarArgs() || parametersCount == 2 && chosenMethod.isVarArgs()) &&
197 !overloadWithNoParametersExists(chosenMethod, context.getObjectsToView())) return false;
207 private static TextRange getRelatedRange(PsiElement owner, Editor editor) {
208 TextRange range = owner.getTextRange();
209 if (range == null) return null;
210 Document document = editor.getDocument();
211 if (Registry.is("editor.keep.completion.hints.even.longer")) {
212 int startY = editor.visualPositionToXY(editor.offsetToVisualPosition(range.getStartOffset())).y;
213 int endY = editor.visualPositionToXY(editor.offsetToVisualPosition(range.getEndOffset())).y;
214 Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
215 return startY > visibleArea.getMaxY() || endY < visibleArea.getMinY() ? null : new TextRange(0, document.getTextLength());
217 if (!Registry.is("editor.keep.completion.hints.longer")) return range;
218 return new TextRange(DocumentUtil.getLineStartOffset(range.getStartOffset(), document),
219 DocumentUtil.getLineEndOffset(range.getEndOffset(), document));
222 private static boolean overloadWithNoParametersExists(PsiMethod method, Object[] candidates) {
223 String methodName = method.getName();
224 return ContainerUtil.find(candidates, c -> {
225 if (!(c instanceof CandidateInfo)) return false;
226 PsiElement e = ((CandidateInfo)c).getElement();
227 if (!(e instanceof PsiMethod)) return false;
228 PsiMethod m = (PsiMethod)e;
229 return m.getParameterList().isEmpty() && m.getName().equals(methodName);
233 private static boolean isIncompatibleParameterCount(@NotNull PsiMethod method, int numberOfParameters) {
234 int originalNumberOfParameters = method.getParameterList().getParametersCount();
235 return PsiImplUtil.isVarArgs(method)
236 ? originalNumberOfParameters > 2 && numberOfParameters < originalNumberOfParameters - 1
237 : originalNumberOfParameters != numberOfParameters && !(originalNumberOfParameters == 1 && numberOfParameters == 0);
241 public void updateParameterInfo(@NotNull final PsiExpressionList o, @NotNull final UpdateParameterInfoContext context) {
242 PsiElement parameterOwner = context.getParameterOwner();
243 if (parameterOwner != o) {
244 context.removeHint();
248 int offset = context.getOffset();
249 TextRange elRange = o.getTextRange();
250 int index = offset <= elRange.getStartOffset() || offset >= elRange.getEndOffset()
251 ? -1 : ParameterInfoUtils.getCurrentParameterIndex(o.getNode(), offset, JavaTokenType.COMMA);
252 context.setCurrentParameter(index);
254 Object[] candidates = context.getObjectsToView();
255 PsiExpression[] args = o.getExpressions();
256 PsiCall call = getCall(o);
257 PsiElement realResolve = call != null ? call.resolveMethod() : null;
259 PsiMethod chosenMethod = CompletionMemory.getChosenMethod(call);
260 CandidateInfo chosenInfo = null;
261 CandidateInfo completeMatch = null;
263 for (int i = 0; i < candidates.length; i++) {
264 CandidateInfo candidate = (CandidateInfo)candidates[i];
265 PsiMethod method = (PsiMethod)candidate.getElement();
266 if (!method.isValid()) continue;
267 if (candidate instanceof MethodCandidateInfo && !((MethodCandidateInfo)candidate).getSiteSubstitutor().isValid()) continue;
268 PsiSubstitutor substitutor = getCandidateInfoSubstitutor(o, candidate, method == realResolve);
269 assert substitutor != null;
271 if (!method.isValid() || !substitutor.isValid()) {
272 // this may sometimes happen e,g, when editing method call in field initializer candidates in the same file get invalidated
273 context.setUIComponentEnabled(i, false);
277 PsiParameter[] parms = method.getParameterList().getParameters();
278 boolean enabled = true;
279 if (parms.length <= index) {
280 if (parms.length > 0) {
281 if (method.isVarArgs()) {
282 for (int j = 0; j < parms.length - 1; j++) {
283 PsiType parmType = substitutor.substitute(parms[j].getType());
284 PsiType argType = args[j].getType();
285 if (argType != null && !parmType.isAssignableFrom(argType)) {
292 PsiArrayType lastParmType = (PsiArrayType)substitutor.substitute(parms[parms.length - 1].getType());
293 PsiType componentType = lastParmType.getComponentType();
295 if (parms.length == args.length) {
296 PsiType lastArgType = args[args.length - 1].getType();
297 if (lastArgType != null && !lastParmType.isAssignableFrom(lastArgType) &&
298 !componentType.isAssignableFrom(lastArgType)) {
303 for (int j = parms.length; j <= index && j < args.length; j++) {
304 PsiExpression arg = args[j];
305 PsiType argType = arg.getType();
306 if (argType != null && !componentType.isAssignableFrom(argType)) {
319 enabled = index == 0;
323 enabled = isAssignableParametersBeforeGivenIndex(parms, args, index, substitutor);
326 context.setUIComponentEnabled(i, enabled);
327 if (!enabled && context.getHighlightedParameter() == candidate) {
328 context.setHighlightedParameter(null);
330 if (candidates.length > 1 && enabled) {
331 if (PsiManager.getInstance(context.getProject()).areElementsEquivalent(chosenMethod, method)) {
332 chosenInfo = candidate;
335 if (parms.length == args.length && realResolve == method &&
336 isAssignableParametersBeforeGivenIndex(parms, args, args.length, substitutor)) {
337 completeMatch = candidate;
342 if (chosenInfo != null) {
343 context.setHighlightedParameter(chosenInfo);
345 else if (completeMatch != null) {
346 context.setHighlightedParameter(completeMatch);
349 Object highlightedCandidate = candidates.length == 1 ? candidates[0] : context.getHighlightedParameter();
350 if (highlightedCandidate != null) {
351 PsiMethod method = (PsiMethod)(highlightedCandidate instanceof CandidateInfo
352 ? ((CandidateInfo)highlightedCandidate).getElement() : highlightedCandidate);
353 if (!method.isVarArgs() && index >= method.getParameterList().getParametersCount()) context.setCurrentParameter(-1);
357 private static void highlightHints(@NotNull Editor editor, @Nullable PsiExpressionList expressionList, int currentHintIndex,
358 @NotNull UserDataHolder context) {
359 if (editor.isDisposed() || editor instanceof EditorWindow) return;
360 ParameterHintsPresentationManager presentationManager = ParameterHintsPresentationManager.getInstance();
361 Inlay currentHint = null;
362 List<Inlay> highlightedHints = null;
363 if (expressionList != null && expressionList.isValid()) {
364 int expressionCount = expressionList.getExpressionCount();
365 if (currentHintIndex == 0 || currentHintIndex > 0 && currentHintIndex < expressionCount) {
366 highlightedHints = new ArrayList<>(expressionCount);
367 ParameterHintsPass.syncUpdate(expressionList.getParent(), editor);
368 PsiElement prevDelimiter, nextDelimiter;
369 for (int i = 0; i < Math.max(expressionCount, currentHintIndex == 0 ? 1 : 0); i++) {
370 if (i < expressionCount) {
371 PsiExpression expression = expressionList.getExpressions()[i];
372 //noinspection StatementWithEmptyBody
373 for (prevDelimiter = expression;
374 prevDelimiter != null && !(prevDelimiter instanceof PsiJavaToken);
375 prevDelimiter = prevDelimiter.getPrevSibling())
377 //noinspection StatementWithEmptyBody
378 for (nextDelimiter = expression;
379 nextDelimiter != null && !(nextDelimiter instanceof PsiJavaToken);
380 nextDelimiter = nextDelimiter.getNextSibling())
384 prevDelimiter = expressionList.getFirstChild(); // left parenthesis
385 nextDelimiter = expressionList.getLastChild(); // right parenthesis
387 if (prevDelimiter != null && nextDelimiter != null) {
388 CharSequence text = editor.getDocument().getImmutableCharSequence();
389 int firstRangeStartOffset = prevDelimiter.getTextRange().getEndOffset();
390 int firstRangeEndOffset = CharArrayUtil.shiftForward(text, firstRangeStartOffset, WHITESPACE);
391 for (Inlay inlay : editor.getInlayModel().getInlineElementsInRange(firstRangeStartOffset, firstRangeEndOffset)) {
392 if (presentationManager.isParameterHint(inlay)) {
393 highlightedHints.add(inlay);
394 if (i == currentHintIndex && currentHint == null) currentHint = inlay;
397 int secondRangeEndOffset = nextDelimiter.getTextRange().getStartOffset();
398 if (secondRangeEndOffset > firstRangeEndOffset) {
399 int secondRangeStartOffset = CharArrayUtil.shiftBackward(text, secondRangeEndOffset - 1, WHITESPACE) + 1;
400 for (Inlay inlay : editor.getInlayModel().getInlineElementsInRange(secondRangeStartOffset, secondRangeEndOffset)) {
401 if (presentationManager.isParameterHint(inlay)) {
402 highlightedHints.add(inlay);
410 if (currentHint == context.getUserData(CURRENT_HINT) &&
411 Objects.equals(highlightedHints, context.getUserData(HIGHLIGHTED_HINTS))) return;
413 if (currentHint != null) {
414 presentationManager.setCurrent(currentHint, true);
415 context.putUserData(CURRENT_HINT, currentHint);
417 if (!ContainerUtil.isEmpty(highlightedHints)) {
418 for (Inlay highlightedHint : highlightedHints) {
419 presentationManager.setHighlighted(highlightedHint, true);
421 context.putUserData(HIGHLIGHTED_HINTS, highlightedHints);
425 private static void resetHints(@NotNull UserDataHolder context) {
426 ParameterHintsPresentationManager presentationManager = ParameterHintsPresentationManager.getInstance();
427 Inlay currentHint = context.getUserData(CURRENT_HINT);
428 if (currentHint != null) {
429 presentationManager.setCurrent(currentHint, false);
430 context.putUserData(CURRENT_HINT, null);
432 List<Inlay> highlightedHints = context.getUserData(HIGHLIGHTED_HINTS);
433 if (highlightedHints != null) {
434 for (Inlay hint : highlightedHints) {
435 presentationManager.setHighlighted(hint, false);
437 context.putUserData(HIGHLIGHTED_HINTS, null);
442 public void dispose(@NotNull DeleteParameterInfoContext context) {
443 Editor editor = context.getEditor();
444 if (!(editor instanceof EditorWindow)) {
445 resetHints(context.getCustomContext());
446 PsiElement parameterOwner = context.getParameterOwner();
447 if (!editor.isDisposed() && parameterOwner != null && parameterOwner.isValid()) {
448 ParameterHintsPass.syncUpdate(parameterOwner.getParent(), editor);
453 private static PsiSubstitutor getCandidateInfoSubstitutor(PsiElement argList, CandidateInfo candidate, boolean resolveResult) {
454 Computable<PsiSubstitutor> computeSubstitutor =
455 () -> candidate instanceof MethodCandidateInfo && ((MethodCandidateInfo)candidate).isInferencePossible()
456 ? ((MethodCandidateInfo)candidate).inferTypeArguments(CompletionParameterTypeInferencePolicy.INSTANCE, true)
457 : candidate.getSubstitutor();
458 if (resolveResult && candidate instanceof MethodCandidateInfo && ((MethodCandidateInfo)candidate).isInferencePossible()) {
459 return computeSubstitutor.compute();
461 return MethodCandidateInfo.ourOverloadGuard.doPreventingRecursion(ObjectUtils.notNull(argList, candidate.getElement()),
466 private static boolean isAssignableParametersBeforeGivenIndex(final PsiParameter[] parms,
467 final PsiExpression[] args,
469 PsiSubstitutor substitutor) {
470 for (int j = 0; j < length; j++) {
471 PsiParameter parm = parms[j];
472 PsiExpression arg = args[j];
473 assert parm.isValid();
474 assert arg.isValid();
475 PsiType parmType = parm.getType();
476 PsiType argType = arg.getType();
477 if (argType == null) continue;
478 if (parmType instanceof PsiEllipsisType ) {
479 parmType = ((PsiEllipsisType)parmType).getComponentType();
481 parmType = substitutor.substitute(parmType);
483 if (!parmType.isAssignableFrom(argType)) {
492 public Class<PsiExpressionList> getArgumentListClass() {
493 return PsiExpressionList.class;
498 public IElementType getActualParametersRBraceType() {
499 return JavaTokenType.RBRACE;
504 public Set<Class> getArgumentListAllowedParentClasses() {
505 return ourArgumentListAllowedParentClassesSet;
510 public Set<? extends Class> getArgListStopSearchClasses() {
511 return ourStopSearch;
516 public IElementType getActualParameterDelimiterType() {
517 return JavaTokenType.COMMA;
522 public PsiExpression[] getActualParameters(@NotNull PsiExpressionList psiExpressionList) {
523 return psiExpressionList.getExpressions();
526 private static PsiCall getCall(PsiExpressionList list) {
527 PsiElement listParent = list.getParent();
528 if (listParent instanceof PsiMethodCallExpression) {
529 return (PsiCall)listParent;
531 if (listParent instanceof PsiNewExpression) {
532 return (PsiCall)listParent;
534 if (listParent instanceof PsiAnonymousClass) {
535 return (PsiCall)listParent.getParent();
537 if (listParent instanceof PsiEnumConstant) {
538 return (PsiCall)listParent;
545 private static CandidateInfo[] getMethods(PsiExpressionList argList) {
546 final PsiCall call = getCall(argList);
547 PsiResolveHelper helper = JavaPsiFacade.getInstance(argList.getProject()).getResolveHelper();
549 if (call instanceof PsiCallExpression) {
550 CandidateInfo[] candidates = getCandidates((PsiCallExpression)call);
551 ArrayList<CandidateInfo> result = new ArrayList<>();
553 if (!(argList.getParent() instanceof PsiAnonymousClass)) {
555 for (CandidateInfo candidate : candidates) {
556 PsiMethod methodCandidate = (PsiMethod)candidate.getElement();
558 for (CandidateInfo info : result) {
559 if (MethodSignatureUtil.isSuperMethod(methodCandidate, (PsiMethod)info.getElement())) {
563 if (candidate.isStaticsScopeCorrect()) {
564 boolean accessible = candidate.isAccessible();
565 if (!accessible && methodCandidate.getModifierList().hasModifierProperty(PsiModifier.PRIVATE)) {
566 // privates are accessible within one file
567 accessible = JavaPsiFacade.getInstance(methodCandidate.getProject()).getResolveHelper()
568 .isAccessible(methodCandidate, methodCandidate.getModifierList(), call, null, null);
570 if (accessible) result.add(candidate);
575 PsiClass aClass = (PsiClass)argList.getParent();
576 for (CandidateInfo candidate : candidates) {
577 if (candidate.isStaticsScopeCorrect() && helper.isAccessible((PsiMethod)candidate.getElement(), argList, aClass)) {
578 result.add(candidate);
582 return result.isEmpty() ? candidates : result.toArray(CandidateInfo.EMPTY_ARRAY);
585 assert call instanceof PsiEnumConstant;
586 //We are inside our own enum, no isAccessible check needed
587 PsiMethod[] constructors = ((PsiEnumConstant)call).getContainingClass().getConstructors();
588 CandidateInfo[] result = new CandidateInfo[constructors.length];
590 for (int i = 0; i < constructors.length; i++) {
591 result[i] = new CandidateInfo(constructors[i], PsiSubstitutor.EMPTY);
597 private static CandidateInfo[] getCandidates(PsiCallExpression call) {
598 final MethodCandidatesProcessor processor = new MethodResolverProcessor(call, call.getContainingFile(), new PsiConflictResolver[0]) {
600 protected boolean acceptVarargs() {
606 PsiScopesUtil.setupAndRunProcessor(processor, call, true);
608 catch (MethodProcessorSetupFailedException e) {
609 return CandidateInfo.EMPTY_ARRAY;
611 final List<CandidateInfo> results = processor.getResults();
612 return results.toArray(CandidateInfo.EMPTY_ARRAY);
615 public static String updateMethodPresentation(@NotNull PsiMethod method, @Nullable PsiSubstitutor substitutor, @NotNull ParameterInfoUIContext context) {
616 CodeInsightSettings settings = CodeInsightSettings.getInstance();
618 if (!method.isValid() || substitutor != null && !substitutor.isValid()) {
619 context.setUIComponentEnabled(false);
623 StringBuilder buffer = new StringBuilder();
625 if (settings.SHOW_FULL_SIGNATURES_IN_PARAMETER_INFO && !context.isSingleParameterInfo()) {
626 if (!method.isConstructor()) {
627 PsiType returnType = method.getReturnType();
628 if (substitutor != null) {
629 returnType = substitutor.substitute(returnType);
631 assert returnType != null : method;
633 appendModifierList(buffer, method);
634 buffer.append(returnType.getPresentableText(true));
637 buffer.append(method.getName());
641 int currentParameter = context.getCurrentParameterIndex();
643 PsiParameter[] parms = method.getParameterList().getParameters();
644 int numParams = parms.length;
645 int highlightStartOffset = -1;
646 int highlightEndOffset = -1;
648 if (context.isSingleParameterInfo() && method.isVarArgs() && currentParameter >= numParams) currentParameter = numParams - 1;
650 for (int j = 0; j < numParams; j++) {
651 if (context.isSingleParameterInfo() && j != currentParameter) continue;
653 PsiParameter param = parms[j];
655 int startOffset = buffer.length();
657 if (param.isValid()) {
658 PsiType paramType = param.getType();
659 assert paramType.isValid();
660 if (substitutor != null) {
661 assert substitutor.isValid();
662 paramType = substitutor.substitute(paramType);
664 if (context.isSingleParameterInfo()) buffer.append("<b>");
665 appendModifierList(buffer, param);
666 String type = paramType.getPresentableText(true);
667 buffer.append(context.isSingleParameterInfo() ? StringUtil.escapeXml(type) : type);
668 String name = param.getName();
669 if (name != null && !context.isSingleParameterInfo()) {
673 if (context.isSingleParameterInfo()) buffer.append("</b>");
676 if (context.isSingleParameterInfo()) {
677 String javaDoc = new JavaDocInfoGenerator(param.getProject(), param).generateMethodParameterJavaDoc();
678 if (javaDoc != null) {
679 javaDoc = removeHyperlinks(javaDoc);
680 if (javaDoc.length() < 100) {
681 buffer.append(" <i>").append(javaDoc).append("</i>");
684 buffer.insert(0, "<table><tr><td valign='top'>")
685 .append("</td><td style='width:400px'> <i>").append(javaDoc).append("</i></td></tr></table>");
690 int endOffset = buffer.length();
692 if (j < numParams - 1) {
696 if (context.isUIComponentEnabled() &&
697 (j == currentParameter || j == numParams - 1 && param.isVarArgs() && currentParameter >= numParams)) {
698 highlightStartOffset = startOffset;
699 highlightEndOffset = endOffset;
705 buffer.append(CodeInsightBundle.message("parameter.info.no.parameters"));
708 if (settings.SHOW_FULL_SIGNATURES_IN_PARAMETER_INFO && !context.isSingleParameterInfo()) {
712 String text = buffer.toString();
713 if (context.isSingleParameterInfo()) {
714 context.setupRawUIComponentPresentation(text);
718 return context.setupUIComponentPresentation(
720 highlightStartOffset,
722 !context.isUIComponentEnabled(),
723 method.isDeprecated() && !context.isSingleParameterInfo() && !context.isSingleOverload(),
725 context.getDefaultParameterColor()
730 private static String removeHyperlinks(String html) {
731 return html.replaceAll("<a.*?>", "").replaceAll("</a>", "");
734 private static void appendModifierList(@NotNull StringBuilder buffer, @NotNull PsiModifierListOwner owner) {
735 int lastSize = buffer.length();
736 Set<String> shownAnnotations = ContainerUtil.newHashSet();
737 for (PsiAnnotation annotation : AnnotationUtil.getAllAnnotations(owner, false, null, !DumbService.isDumb(owner.getProject()))) {
738 final PsiJavaCodeReferenceElement element = annotation.getNameReferenceElement();
739 if (element != null) {
740 final PsiElement resolved = element.resolve();
741 if (resolved instanceof PsiClass &&
742 (!JavaDocInfoGenerator.isDocumentedAnnotationType((PsiClass)resolved) ||
743 AnnotationTargetUtil.findAnnotationTarget((PsiClass)resolved, PsiAnnotation.TargetType.TYPE_USE) != null)) {
747 String referenceName = element.getReferenceName();
748 if (shownAnnotations.add(referenceName) || JavaDocInfoGenerator.isRepeatableAnnotationType(resolved)) {
749 if (lastSize != buffer.length()) buffer.append(' ');
750 buffer.append('@').append(referenceName);
754 if (lastSize != buffer.length()) buffer.append(' ');
758 public void updateUI(final Object p, @NotNull final ParameterInfoUIContext context) {
759 if (p instanceof CandidateInfo) {
760 CandidateInfo info = (CandidateInfo)p;
761 PsiMethod method = (PsiMethod)info.getElement();
762 if (!method.isValid() || info instanceof MethodCandidateInfo && !((MethodCandidateInfo)info).getSiteSubstitutor().isValid()) {
763 context.setUIComponentEnabled(false);
767 updateMethodPresentation(method, getCandidateInfoSubstitutor(context.getParameterOwner(), info, false), context);
770 updateMethodPresentation((PsiMethod)p, null, context);
775 public boolean supportsOverloadSwitching() {
776 return CodeInsightSettings.getInstance().SHOW_PARAMETER_NAME_HINTS_ON_COMPLETION;