[name hints] option to show hints for multiple parameters with the same type

This commit is contained in:
Yaroslav Lepenkin
2016-11-30 19:42:53 +03:00
parent 5d1bc53527
commit 63b66f3964
6 changed files with 91 additions and 32 deletions
@@ -34,7 +34,7 @@ class JavaInlayParameterHintsProvider : InlayParameterHintsProvider {
override fun getParameterHints(element: PsiElement): List<InlayInfo> {
if (element is PsiCallExpression) {
return JavaParameterHintManager(element).descriptors
return JavaParameterHintManager(element).descriptors.toList()
}
return emptyList()
}
@@ -16,32 +16,39 @@
package com.intellij.codeInsight.hints;
import com.intellij.codeInsight.hints.settings.ParameterNameHintsSettings;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.containers.ContainerUtil;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class JavaParameterHintManager {
@NotNull
private final List<InlayInfo> myDescriptors;
private final Set<InlayInfo> myDescriptors;
public JavaParameterHintManager(@NotNull PsiCallExpression callExpression) {
PsiExpression[] callArguments = getArguments(callExpression);
JavaResolveResult resolveResult = callExpression.resolveMethodGenerics();
if (!(resolveResult.getElement() instanceof PsiMethod)) {
myDescriptors = Collections.emptySet();
return;
}
List<InlayInfo> descriptors = Collections.emptyList();
if (resolveResult.getElement() instanceof PsiMethod
&& isMethodToShowParams(callExpression, resolveResult)
&& hasUnclearExpressions(callArguments))
{
PsiMethod method = (PsiMethod)resolveResult.getElement();
final PsiMethod method = (PsiMethod)resolveResult.getElement();
Set<InlayInfo> descriptors = Collections.emptySet();
if (isMethodToShowParams(callExpression, method)) {
PsiParameter[] parameters = method.getParameterList().getParameters();
descriptors = buildDescriptorsForLiteralArguments(callArguments, parameters, resolveResult);
}
@@ -49,11 +56,7 @@ public class JavaParameterHintManager {
myDescriptors = descriptors;
}
private static boolean isMethodToShowParams(@NotNull PsiCallExpression callExpression, @NotNull JavaResolveResult resolveResult) {
PsiElement element = resolveResult.getElement();
if (!(element instanceof PsiMethod)) return false;
PsiMethod method = (PsiMethod)element;
private static boolean isMethodToShowParams(@NotNull PsiCallExpression callExpression, @NotNull PsiMethod method) {
if (isSetter(method)) return false;
PsiParameter[] parameters = method.getParameterList().getParameters();
@@ -70,6 +73,16 @@ public class JavaParameterHintManager {
return true;
}
private static Set<String> getDuplicatedParamTypes(PsiParameter[] parameters) {
List<String> duplicatedTypes = Arrays
.stream(parameters)
.map((p) -> p.getType().getCanonicalText())
.collect(Collectors.toList());
ContainerUtil.newHashSet(duplicatedTypes).forEach((e) -> duplicatedTypes.remove(e));
return ContainerUtil.newHashSet(duplicatedTypes);
}
private static boolean isParamNameContainedInMethodName(@NotNull PsiParameter parameter, @NotNull PsiMethod method) {
String parameterName = parameter.getName();
if (parameterName != null && parameterName.length() > 1) {
@@ -137,42 +150,71 @@ public class JavaParameterHintManager {
}
@NotNull
public List<InlayInfo> getDescriptors() {
public Set<InlayInfo> getDescriptors() {
return myDescriptors;
}
@NotNull
private static List<InlayInfo> buildDescriptorsForLiteralArguments(@NotNull PsiExpression[] callArguments,
@NotNull PsiParameter[] parameters,
@NotNull JavaResolveResult resolveResult) {
private static Set<InlayInfo> buildDescriptorsForLiteralArguments(@NotNull PsiExpression[] callArguments,
@NotNull PsiParameter[] parameters,
@NotNull JavaResolveResult resolveResult) {
final Set<InlayInfo> descriptors = ContainerUtil.newHashSet();
final PsiSubstitutor substitutor = resolveResult.getSubstitutor();
if (ParameterNameHintsSettings.getInstance().isShowForParamsWithSameType()) {
final List<InlayInfo> hintsForNonLiterals = getHintsForParamsWithSameType(callArguments, parameters, substitutor);
descriptors.addAll(hintsForNonLiterals);
}
List<InlayInfo> descriptors = ContainerUtil.newArrayList();
for (int i = 0; i < Math.min(callArguments.length, parameters.length); i++) {
PsiExpression arg = callArguments[i];
PsiParameter param = parameters[i];
if (isVarargParam(param.getType(), arg.getType()) && hasUnclearExpressionStartingFrom(i, callArguments)
|| shouldInlineParameterName(arg, param, resolveResult)) {
|| isUnclearExpression(arg) && isAssignable(arg, param, substitutor)) {
descriptors.add(createInlayInfo(arg, param));
}
}
return descriptors;
}
private static List<InlayInfo> getHintsForParamsWithSameType(@NotNull PsiExpression[] callArguments,
@NotNull PsiParameter[] parameters,
@NotNull PsiSubstitutor substitutor)
{
final Set<String> duplicatedTypes = getDuplicatedParamTypes(parameters);
final int minArraySize = Math.min(callArguments.length, parameters.length);
List<PsiExpression> args = ContainerUtil.list(callArguments).subList(0, minArraySize);
List<PsiParameter> params = ContainerUtil.list(parameters).subList(0, minArraySize);
return StreamEx
.zip(args, params, (a, b) -> Pair.create(a, b))
.filter((pair) -> {
PsiParameter param = pair.getSecond();
return duplicatedTypes.contains(param.getType().getCanonicalText());
})
.filter((pair) -> isAssignable(pair.first, pair.second, substitutor))
.map((pair) -> createInlayInfo(pair.first, pair.second))
.collect(Collectors.toList());
}
@NotNull
private static InlayInfo createInlayInfo(@NotNull PsiExpression callArgument, @NotNull PsiParameter methodParam) {
String paramName = ((methodParam.getType() instanceof PsiEllipsisType) ? "..." : "") + methodParam.getName();
return new InlayInfo(paramName, callArgument.getTextRange().getStartOffset());
}
private static boolean shouldInlineParameterName(@NotNull PsiExpression argument,
@NotNull PsiParameter parameter,
@NotNull JavaResolveResult resolveResult) {
private static boolean isAssignable(@NotNull PsiExpression argument,
@NotNull PsiParameter parameter,
@NotNull PsiSubstitutor substitutor) {
PsiType argType = argument.getType();
PsiType paramType = parameter.getType();
if (argType != null && isUnclearExpression(argument)) {
PsiType parameterType = resolveResult.getSubstitutor().substitute(paramType);
if (argType != null) {
PsiType parameterType = substitutor.substitute(paramType);
return TypeConversionUtil.isAssignable(parameterType, argType);
}
@@ -789,4 +789,21 @@ class Test {
.assertInlays("timeout->1000", "message->\"xxx\"")
}
fun `test show same params`() {
ParameterNameHintsSettings.getInstance().isShowForParamsWithSameType = true
setup("""
class Test {
void main() {
String c = "c";
String d = "d";
test(c, d);
}
void test(String parent, String child) {
}
}
""")
onLineStartingWith("test").assertInlays("parent->c", "child->d")
}
}
@@ -15,7 +15,7 @@
*/
package com.intellij.codeInsight.hints
class InlayInfo(val text: String, val offset: Int)
data class InlayInfo(val text: String, val offset: Int)
open class MethodInfo(val fullyQualifiedName: String, val paramNames: List<String>) {
@@ -73,7 +73,7 @@ public class ParameterNameHintsConfigurable extends DialogWrapper {
setTitle("Configure Parameter Name Hints Blacklist");
init();
myOptionsPanel.setVisible(false);
myOptionsPanel.setVisible(true);
}
private void updateOkEnabled() {
@@ -103,7 +103,7 @@ public class ParameterNameHintsConfigurable extends DialogWrapper {
ParameterNameHintsSettings settings = ParameterNameHintsSettings.getInstance();
settings.setShowParamNameContainedInMethodName(myShowIfParamNameContainedInMethod.isSelected());
settings.setShowWhenMultipleParamsWithSameType(myShowWhenMultipleParamsWithSameType.isSelected());
settings.setShowForParamsWithSameType(myShowWhenMultipleParamsWithSameType.isSelected());
}
private static void storeBlackListDiff(@NotNull Language language, @NotNull String text) {
@@ -147,7 +147,7 @@ public class ParameterNameHintsConfigurable extends DialogWrapper {
ParameterNameHintsSettings settings = ParameterNameHintsSettings.getInstance();
myShowIfParamNameContainedInMethod.setSelected(settings.isShowParamNameContainedInMethodName());
myShowWhenMultipleParamsWithSameType.setSelected(settings.getShowWhenMultipleParamsWithSameType());
myShowWhenMultipleParamsWithSameType.setSelected(settings.isShowForParamsWithSameType());
initLanguageCombo(languages, selected);
}
@@ -50,7 +50,7 @@ class ParameterNameHintsSettings : PersistentStateComponent<Element> {
private val myAddedPatterns = hashMapOf<String, Set<String>>()
var isShowParamNameContainedInMethodName: Boolean = false
var showWhenMultipleParamsWithSameType: Boolean = false
var isShowForParamsWithSameType: Boolean = false
fun addIgnorePattern(language: Language, pattern: String) {
val patternsBefore = getAddedPatterns(language)
@@ -85,7 +85,7 @@ class ParameterNameHintsSettings : PersistentStateComponent<Element> {
.setAttribute("value", isShowParamNameContainedInMethodName.toString())
root.getOrCreateChild(XmlTagHelper.SHOW_WHEN_MULTIPLE_PARAMS_WITH_SAME_TYPE)
.setAttribute("value", showWhenMultipleParamsWithSameType.toString())
.setAttribute("value", isShowForParamsWithSameType.toString())
return root
}
@@ -95,7 +95,7 @@ class ParameterNameHintsSettings : PersistentStateComponent<Element> {
myRemovedPatterns.clear()
isShowParamNameContainedInMethodName = false
showWhenMultipleParamsWithSameType = false
isShowForParamsWithSameType = false
val allBlackLists = state
.getChild(XmlTagHelper.BLACKLISTS)
@@ -108,7 +108,7 @@ class ParameterNameHintsSettings : PersistentStateComponent<Element> {
}
isShowParamNameContainedInMethodName = state.getBooleanValue(XmlTagHelper.SHOW_IF_PARAM_NAME_CONTAINED, true)
showWhenMultipleParamsWithSameType = state.getBooleanValue(XmlTagHelper.SHOW_WHEN_MULTIPLE_PARAMS_WITH_SAME_TYPE, false)
isShowForParamsWithSameType = state.getBooleanValue(XmlTagHelper.SHOW_WHEN_MULTIPLE_PARAMS_WITH_SAME_TYPE, false)
}
private fun Element.getBooleanValue(childName: String, defaultValue: Boolean): Boolean {