<commit_before>// 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.
package com.intellij.codeInsight.hint.api.impls;

import com.intellij.codeInsight.AnnotationTargetUtil;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.completion.CompletionMemory;
import com.intellij.codeInsight.completion.JavaCompletionUtil;
import com.intellij.codeInsight.completion.JavaMethodCallElement;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.codeInsight.daemon.impl.ParameterHintsPresentationManager;
import com.intellij.codeInsight.hint.ParameterInfoController;
import com.intellij.codeInsight.hints.ParameterHintsPass;
import com.intellij.codeInsight.javadoc.JavaDocInfoGenerator;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.injected.editor.EditorWindow;
import com.intellij.lang.parameterInfo.*;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.Inlay;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.UserDataHolder;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.source.resolve.CompletionParameterTypeInferencePolicy;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.scope.MethodProcessorSetupFailedException;
import com.intellij.psi.scope.PsiConflictResolver;
import com.intellij.psi.scope.processor.MethodCandidatesProcessor;
import com.intellij.psi.scope.processor.MethodResolverProcessor;
import com.intellij.psi.scope.util.PsiScopesUtil;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.MethodSignatureUtil;
import com.intellij.util.DocumentUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.awt.*;
import java.util.*;
import java.util.List;

/**
 * @author Maxim.Mossienko
 */
public class MethodParameterInfoHandler implements ParameterInfoHandlerWithTabActionSupport<PsiExpressionList, Object, PsiExpression>, DumbAware {
  private static final Set<Class> ourArgumentListAllowedParentClassesSet = ContainerUtil.newHashSet(
    PsiMethodCallExpression.class, PsiNewExpression.class, PsiAnonymousClass.class, PsiEnumConstant.class);
  private static final Set<Class> ourStopSearch = Collections.singleton(PsiMethod.class);
  private static final String WHITESPACE = " \t";
  private static final Key<Inlay> CURRENT_HINT = Key.create("current.hint");
  private static final Key<List<Inlay>> HIGHLIGHTED_HINTS = Key.create("highlighted.hints");

  @Override
  public Object[] getParametersForLookup(LookupElement item, ParameterInfoContext context) {
    final List<? extends PsiElement> elements = JavaCompletionUtil.getAllPsiElements(item);
    return elements != null && !elements.isEmpty() && elements.get(0) instanceof PsiMethod ? elements.toArray() : null;
  }

  @Override
  public boolean couldShowInLookup() {
    return true;
  }

  @Override
  @Nullable
  public PsiExpressionList findElementForParameterInfo(@NotNull final CreateParameterInfoContext context) {
    PsiExpressionList argumentList = findArgumentList(context.getFile(), context.getOffset(), context.getParameterListStart(), true);

    if (argumentList != null) {
      return findMethodsForArgumentList(context, argumentList);
    }
    return null;
  }

  private PsiExpressionList findArgumentList(final PsiFile file, int offset, int parameterStart, boolean allowOuter) {
    PsiExpressionList argumentList = ParameterInfoUtils.findArgumentList(file, offset, parameterStart, this, allowOuter);
    if (argumentList == null && allowOuter) {
      PsiCall call = ParameterInfoUtils.findParentOfTypeWithStopElements(file, offset, PsiMethodCallExpression.class, PsiMethod.class);
      if (call == null) {
        call = ParameterInfoUtils.findParentOfTypeWithStopElements(file, offset, PsiNewExpression.class, PsiMethod.class);
      }
      if (call != null) {
        argumentList = call.getArgumentList();
      }
    }
    return argumentList;
  }

  private static PsiExpressionList findMethodsForArgumentList(final CreateParameterInfoContext context,
                                                              @NotNull final PsiExpressionList argumentList) {

    CandidateInfo[] candidates = getMethods(argumentList);
    if (candidates.length == 0) {
      DaemonCodeAnalyzer.getInstance(context.getProject()).updateVisibleHighlighters(context.getEditor());
      return null;
    }
    context.setItemsToShow(candidates);
    return argumentList;
  }

  @Override
  public void showParameterInfo(@NotNull final PsiExpressionList element, @NotNull final CreateParameterInfoContext context) {
    int offset = element.getTextRange().getStartOffset();
    if (CodeInsightSettings.getInstance().SHOW_PARAMETER_NAME_HINTS_ON_COMPLETION) {
      ParameterInfoController controller = ParameterInfoController.findControllerAtOffset(context.getEditor(), offset);
      PsiElement parent = element.getParent();
      if (parent instanceof PsiCall && controller != null && controller.isHintShown(false)) {
        Object highlighted = controller.getHighlighted();
        Object[] objects = controller.getObjects();
        if (objects != null && objects.length > 0 && (highlighted != null || objects.length == 1)) {
          PsiCall methodCall = (PsiCall)parent;
          JavaMethodCallElement.setCompletionModeIfNotSet(methodCall, controller);
          PsiMethod targetMethod = (PsiMethod)((CandidateInfo)(highlighted == null ? objects[0] : highlighted)).getElement();
          CompletionMemory.registerChosenMethod(targetMethod, methodCall);
          controller.setPreservedOnHintHidden(true);
          ParameterHintsPass.syncUpdate(methodCall, context.getEditor());
        }
      }
    }
    context.showHint(element, offset, this);
  }

  @Override
  public PsiExpressionList findElementForUpdatingParameterInfo(@NotNull final UpdateParameterInfoContext context) {
    if (context.isPreservedOnHintHidden() && isOutsideOfCompletedInvocation(context)) {
      context.setPreservedOnHintHidden(false);
      return null;
    }
    PsiExpressionList expressionList = findArgumentList(context.getFile(), context.getOffset(), context.getParameterListStart(), false);
    if (expressionList != null) {
      Object[] candidates = context.getObjectsToView();
      if (candidates != null && candidates.length != 0) {
        Object currentMethodInfo = context.getHighlightedParameter();
        if (currentMethodInfo == null) currentMethodInfo = candidates[0];
        PsiElement element = currentMethodInfo instanceof CandidateInfo ? ((CandidateInfo)currentMethodInfo).getElement() : 
                             currentMethodInfo instanceof PsiElement ? (PsiElement) currentMethodInfo : 
                             null;
        if ((element instanceof PsiMethod)) {
          PsiMethod method = (PsiMethod)element;
          PsiElement parent = expressionList.getParent();

          String originalMethodName = method.getName();
          PsiQualifiedReference currentMethodReference = null;
          if (parent instanceof PsiMethodCallExpression && !method.isConstructor()) {
            currentMethodReference = ((PsiMethodCallExpression)parent).getMethodExpression();
          }
          else if (parent instanceof PsiNewExpression) {
            currentMethodReference = ((PsiNewExpression)parent).getClassReference();
          }
          else if (parent instanceof PsiAnonymousClass) {
            currentMethodReference = ((PsiAnonymousClass)parent).getBaseClassReference();
          }
          if (currentMethodReference == null || originalMethodName.equals(currentMethodReference.getReferenceName())) {

            int currentNumberOfParameters = expressionList.getExpressionCount();
            PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(context.getProject());
            Document document = psiDocumentManager.getCachedDocument(context.getFile());
            if (parent instanceof PsiCallExpression && JavaMethodCallElement.isCompletionMode((PsiCall)parent)) {
              PsiMethod chosenMethod = CompletionMemory.getChosenMethod((PsiCall)parent);
              if ((context.getHighlightedParameter() != null || candidates.length == 1) && chosenMethod != null &&
                  document != null && psiDocumentManager.isCommitted(document) &&
                  isIncompatibleParameterCount(chosenMethod, currentNumberOfParameters)) {
                JavaMethodCallElement.setCompletionMode((PsiCall)parent, false);
                highlightHints(context.getEditor(), null, -1, context.getCustomContext());
              }
              else {
                int index = ParameterInfoUtils.getCurrentParameterIndex(expressionList.getNode(), 
                                                                        context.getOffset(), JavaTokenType.COMMA);
                TextRange textRange = expressionList.getTextRange();
                if (context.getOffset() <= textRange.getStartOffset() || context.getOffset() >= textRange.getEndOffset()) index = -1;
                highlightHints(context.getEditor(), expressionList, context.isInnermostContext() ? index : -1, context.getCustomContext());
              }
            }

            return expressionList;
          }
        }
      }
    }
    highlightHints(context.getEditor(), null, -1, context.getCustomContext());
    return null;
  }

  private static boolean isOutsideOfCompletedInvocation(UpdateParameterInfoContext context) {
    PsiElement owner = context.getParameterOwner();<commit_msg>fix blinking CompletionHintsTest<commit_after>// 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.
package com.intellij.codeInsight.hint.api.impls;

import com.intellij.codeInsight.AnnotationTargetUtil;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.completion.CompletionMemory;
import com.intellij.codeInsight.completion.JavaCompletionUtil;
import com.intellij.codeInsight.completion.JavaMethodCallElement;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.codeInsight.daemon.impl.ParameterHintsPresentationManager;
import com.intellij.codeInsight.hint.ParameterInfoController;
import com.intellij.codeInsight.hints.ParameterHintsPass;
import com.intellij.codeInsight.javadoc.JavaDocInfoGenerator;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.injected.editor.EditorWindow;
import com.intellij.lang.parameterInfo.*;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.Inlay;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.UserDataHolder;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.source.resolve.CompletionParameterTypeInferencePolicy;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.scope.MethodProcessorSetupFailedException;
import com.intellij.psi.scope.PsiConflictResolver;
import com.intellij.psi.scope.processor.MethodCandidatesProcessor;
import com.intellij.psi.scope.processor.MethodResolverProcessor;
import com.intellij.psi.scope.util.PsiScopesUtil;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.MethodSignatureUtil;
import com.intellij.util.DocumentUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.awt.*;
import java.util.*;
import java.util.List;

/**
 * @author Maxim.Mossienko
 */
public class MethodParameterInfoHandler implements ParameterInfoHandlerWithTabActionSupport<PsiExpressionList, Object, PsiExpression>, DumbAware {
  private static final Set<Class> ourArgumentListAllowedParentClassesSet = ContainerUtil.newHashSet(
    PsiMethodCallExpression.class, PsiNewExpression.class, PsiAnonymousClass.class, PsiEnumConstant.class);
  private static final Set<Class> ourStopSearch = Collections.singleton(PsiMethod.class);
  private static final String WHITESPACE = " \t";
  private static final Key<Inlay> CURRENT_HINT = Key.create("current.hint");
  private static final Key<List<Inlay>> HIGHLIGHTED_HINTS = Key.create("highlighted.hints");

  @Override
  public Object[] getParametersForLookup(LookupElement item, ParameterInfoContext context) {
    final List<? extends PsiElement> elements = JavaCompletionUtil.getAllPsiElements(item);
    return elements != null && !elements.isEmpty() && elements.get(0) instanceof PsiMethod ? elements.toArray() : null;
  }

  @Override
  public boolean couldShowInLookup() {
    return true;
  }

  @Override
  @Nullable
  public PsiExpressionList findElementForParameterInfo(@NotNull final CreateParameterInfoContext context) {
    PsiExpressionList argumentList = findArgumentList(context.getFile(), context.getOffset(), context.getParameterListStart(), true);

    if (argumentList != null) {
      return findMethodsForArgumentList(context, argumentList);
    }
    return null;
  }

  private PsiExpressionList findArgumentList(final PsiFile file, int offset, int parameterStart, boolean allowOuter) {
    PsiExpressionList argumentList = ParameterInfoUtils.findArgumentList(file, offset, parameterStart, this, allowOuter);
    if (argumentList == null && allowOuter) {
      PsiCall call = ParameterInfoUtils.findParentOfTypeWithStopElements(file, offset, PsiMethodCallExpression.class, PsiMethod.class);
      if (call == null) {
        call = ParameterInfoUtils.findParentOfTypeWithStopElements(file, offset, PsiNewExpression.class, PsiMethod.class);
      }
      if (call != null) {
        argumentList = call.getArgumentList();
      }
    }
    return argumentList;
  }

  private static PsiExpressionList findMethodsForArgumentList(final CreateParameterInfoContext context,
                                                              @NotNull final PsiExpressionList argumentList) {

    CandidateInfo[] candidates = getMethods(argumentList);
    if (candidates.length == 0) {
      DaemonCodeAnalyzer.getInstance(context.getProject()).updateVisibleHighlighters(context.getEditor());
      return null;
    }
    context.setItemsToShow(candidates);
    return argumentList;
  }

  @Override
  public void showParameterInfo(@NotNull final PsiExpressionList element, @NotNull final CreateParameterInfoContext context) {
    int offset = element.getTextRange().getStartOffset();
    if (CodeInsightSettings.getInstance().SHOW_PARAMETER_NAME_HINTS_ON_COMPLETION) {
      ParameterInfoController controller = ParameterInfoController.findControllerAtOffset(context.getEditor(), offset);
      PsiElement parent = element.getParent();
      if (parent instanceof PsiCall && controller != null && controller.isHintShown(false)) {
        Object highlighted = controller.getHighlighted();
        Object[] objects = controller.getObjects();
        if (objects != null && objects.length > 0 && (highlighted != null || objects.length == 1)) {
          PsiCall methodCall = (PsiCall)parent;
          JavaMethodCallElement.setCompletionModeIfNotSet(methodCall, controller);
          PsiMethod targetMethod = (PsiMethod)((CandidateInfo)(highlighted == null ? objects[0] : highlighted)).getElement();
          CompletionMemory.registerChosenMethod(targetMethod, methodCall);
          controller.setPreservedOnHintHidden(true);
          ParameterHintsPass.syncUpdate(methodCall, context.getEditor());
        }
      }
    }
    context.showHint(element, offset, this);
  }

  @Override
  public PsiExpressionList findElementForUpdatingParameterInfo(@NotNull final UpdateParameterInfoContext context) {
    if (context.isPreservedOnHintHidden() && isOutsideOfCompletedInvocation(context)) {
      context.setPreservedOnHintHidden(false);
      return null;
    }
    PsiExpressionList expressionList = findArgumentList(context.getFile(), context.getOffset(), context.getParameterListStart(), false);
    if (expressionList != null) {
      Object[] candidates = context.getObjectsToView();
      if (candidates != null && candidates.length != 0) {
        Object currentMethodInfo = context.getHighlightedParameter();
        if (currentMethodInfo == null) currentMethodInfo = candidates[0];
        PsiElement element = currentMethodInfo instanceof CandidateInfo ? ((CandidateInfo)currentMethodInfo).getElement() : 
                             currentMethodInfo instanceof PsiElement ? (PsiElement) currentMethodInfo : 
                             null;
        if ((element instanceof PsiMethod)) {
          PsiMethod method = (PsiMethod)element;
          PsiElement parent = expressionList.getParent();

          String originalMethodName = method.getName();
          PsiQualifiedReference currentMethodReference = null;
          if (parent instanceof PsiMethodCallExpression && !method.isConstructor()) {
            currentMethodReference = ((PsiMethodCallExpression)parent).getMethodExpression();
          }
          else if (parent instanceof PsiNewExpression) {
            currentMethodReference = ((PsiNewExpression)parent).getClassReference();
          }
          else if (parent instanceof PsiAnonymousClass) {
            currentMethodReference = ((PsiAnonymousClass)parent).getBaseClassReference();
          }
          if (currentMethodReference == null || originalMethodName.equals(currentMethodReference.getReferenceName())) {

            int currentNumberOfParameters = expressionList.getExpressionCount();
            PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(context.getProject());
            Document document = psiDocumentManager.getCachedDocument(context.getFile());
            if (parent instanceof PsiCallExpression && JavaMethodCallElement.isCompletionMode((PsiCall)parent)) {
              PsiMethod chosenMethod = CompletionMemory.getChosenMethod((PsiCall)parent);
              if ((context.getHighlightedParameter() != null || candidates.length == 1) && chosenMethod != null &&
                  document != null && psiDocumentManager.isCommitted(document) &&
                  isIncompatibleParameterCount(chosenMethod, currentNumberOfParameters)) {
                JavaMethodCallElement.setCompletionMode((PsiCall)parent, false);
                ParameterHintsPass.syncUpdate(parent, context.getEditor()); // make sure the statement above takes effect
                highlightHints(context.getEditor(), null, -1, context.getCustomContext());
              }
              else {
                int index = ParameterInfoUtils.getCurrentParameterIndex(expressionList.getNode(), 
                                                                        context.getOffset(), JavaTokenType.COMMA);
                TextRange textRange = expressionList.getTextRange();
                if (context.getOffset() <= textRange.getStartOffset() || context.getOffset() >= textRange.getEndOffset()) index = -1;
                highlightHints(context.getEditor(), expressionList, context.isInnermostContext() ? index : -1, context.getCustomContext());
              }
            }

            return expressionList;
          }
        }
      }
    }
    highlightHints(context.getEditor(), null, -1, context.getCustomContext());
    return null;
  }

  private static boolean isOutsideOfCompletedInvocation(UpdateParameterInfoContext context) {
    PsiElement owner = context.getParameterOwner();
{'fraction_non_alphanumeric': 0.09415850658434229, 'fraction_numerical': 0.0021224253533355846, 'mean_word_length': 4.102633522028058, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2566439', 'n_tokens_mistral': 5498, 'n_tokens_neox': 5360, 'n_words': 1026}