From 90f474a85a7492097d3c358b888ed93635f46814 Mon Sep 17 00:00:00 2001 From: Jack Zhou Date: Mon, 8 Apr 2013 11:27:26 -0400 Subject: [PATCH] Create from usage: Improved template performance by not re-analyzing file in expressions. --- .../quickfix/CreateMethodFromUsageFix.java | 142 +++++++++--------- 1 file changed, 75 insertions(+), 67 deletions(-) diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/CreateMethodFromUsageFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/CreateMethodFromUsageFix.java index 3bc5a4a0357..f11f6550c23 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/CreateMethodFromUsageFix.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/CreateMethodFromUsageFix.java @@ -144,14 +144,14 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { * Special Expression for parameter names based on its type. */ private static class ParameterNameExpression extends Expression { - private final TypeExpression typeExpression; private final String[] names; + private final Map parameterTypeToNamesMap; - public ParameterNameExpression(@NotNull String[] names, @NotNull TypeExpression typeExpression) { + public ParameterNameExpression(@NotNull String[] names, @NotNull Map parameterTypeToNamesMap) { for (String name : names) assert name != null && !name.isEmpty(); this.names = names; - this.typeExpression = typeExpression; + this.parameterTypeToNamesMap = parameterTypeToNamesMap; } @Nullable @@ -195,15 +195,9 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { if (parameter != null) { JetTypeReference parameterTypeRef = parameter.getTypeReference(); assert parameterTypeRef != null; - JetType selectedType = typeExpression.getTypeFromSelection(parameterTypeRef.getText()); - if (selectedType != null) { // user selected a type we suggested - Collections.addAll(names, JetNameSuggester.suggestNamesForType(selectedType, JetNameValidator.getEmptyValidator(project))); - } else { // user entered their own type - BindingContext bindingContext = AnalyzerFacadeWithCache.analyzeFileWithCache(jetFile).getBindingContext(); - JetType enteredType = bindingContext.get(BindingContext.TYPE, parameterTypeRef); - if (enteredType != null) { - Collections.addAll(names, JetNameSuggester.suggestNamesForType(enteredType, JetNameValidator.getEmptyValidator(project))); - } + String[] suggestedNamesBasedOnType = parameterTypeToNamesMap.get(parameterTypeRef.getText()); + if (suggestedNamesBasedOnType != null) { + Collections.addAll(names, suggestedNamesBasedOnType); } } @@ -235,13 +229,13 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { * An Expression for type references. */ private static class TypeExpression extends Expression { - private final Project project; - private final JetType[] types; - private LookupElement[] lastCachedLookupElements; + private final JetType[] options; + private final String[] optionStrings; - public TypeExpression(@NotNull Project project, @NotNull JetType[] types) { - this.project = project; - this.types = types; + public TypeExpression(@NotNull JetType[] options) { + //To change body of created methods use File | Settings | File Templates. + this.options = options; + optionStrings = renderTypes(options); } @Nullable @@ -262,22 +256,21 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { @NotNull @Override public LookupElement[] calculateLookupItems(ExpressionContext context) { - LookupElement[] lookupElements = new LookupElement[types.length]; - for (int i = 0; i < types.length; i++) { - lookupElements[i] = LookupElementBuilder.create(DescriptorRenderer.TEXT.renderType(types[i])); - // TODO: take into account imports and stuff; how to refer to a type with the simplest name with imports (currently uses fully qualified name)? + LookupElement[] lookupElements = new LookupElement[options.length]; + for (int i = 0; i < options.length; i++) { + lookupElements[i] = LookupElementBuilder.create(optionStrings[i]); } - return lastCachedLookupElements = lookupElements; + return lookupElements; } - @Nullable("can't be found") - public JetType getTypeFromSelection(String selection) { - for (int i = 0; i < lastCachedLookupElements.length; i++) { - if (lastCachedLookupElements[i].getLookupString().equals(selection)) { - return types[i]; - } - } - return null; + @NotNull + public JetType[] getOptions() { + return options; + } + + @NotNull + public String[] getOptionStrings() { + return optionStrings; } } @@ -286,14 +279,11 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { */ private static class TypeParameterListExpression extends Expression { private final String[] ownerTypeParameterNames; - private final Map typeParameterMap; - private final List parameterTypeExpressions; + private final Map typeParameterMap; - public TypeParameterListExpression(@NotNull String[] ownerTypeParameterNames, @NotNull Map typeParameterMap, - @NotNull List parameterTypeExpressions) { + public TypeParameterListExpression(@NotNull String[] ownerTypeParameterNames, @NotNull Map typeParameterMap) { this.ownerTypeParameterNames = ownerTypeParameterNames; this.typeParameterMap = typeParameterMap; - this.parameterTypeExpressions = parameterTypeExpressions; } @NotNull @@ -309,25 +299,17 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { PsiElement elementAt = file.findElementAt(offset); JetFunction func = PsiTreeUtil.getParentOfType(elementAt, JetFunction.class); assert func != null; - JetParameterList parameterList = func.getValueParameterList(); - assert parameterList != null; - List parameters = parameterList.getParameters(); - assert parameters.size() == parameterTypeExpressions.size(); + List parameters = func.getValueParameters(); List typeParameterNames = new ArrayList(); Collections.addAll(typeParameterNames, ownerTypeParameterNames); - int i = 0; for (JetParameter parameter : parameters) { - TypeExpression parameterTypeExpression = parameterTypeExpressions.get(i); JetTypeReference parameterTypeRef = parameter.getTypeReference(); assert parameterTypeRef != null; - JetType parameterType = parameterTypeExpression.getTypeFromSelection(parameterTypeRef.getText()); - if (parameterType != null) { // the user chose a given type - String[] names = typeParameterMap.get(parameterType); + String[] names = typeParameterMap.get(parameterTypeRef.getText()); + if (names != null) { Collections.addAll(typeParameterNames, names); } - - i++; } return typeParameterNames.isEmpty() @@ -399,13 +381,14 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { boolean isUnit = returnType.isType() && isUnit(returnType.getType()); String returnTypeString = isUnit ? "" : ": Any"; + String ownerTypeString; String methodText; JetNamedFunction func; PsiElement owner; boolean isExtension = !klass.isWritable(); if (isExtension) { // create as extension function - methodText = String.format("fun %s.%s(%s)%s { }", DescriptorRenderer.TEXT.renderType(ownerType), - methodName, parametersString, returnTypeString); + ownerTypeString = renderType(ownerType); + methodText = String.format("fun %s.%s(%s)%s { }", ownerTypeString, methodName, parametersString, returnTypeString); func = JetPsiFactory.createFunction(project, methodText); owner = file; func = (JetNamedFunction) file.add(func); @@ -438,24 +421,24 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { if (!isUnit) { JetTypeReference returnTypeRef = func.getReturnTypeRef(); assert returnTypeRef != null; - Expression returnTypeExpression = new TypeExpression(project, returnType.getPossibleTypes()); + Expression returnTypeExpression = new TypeExpression(returnType.getPossibleTypes()); builder.replaceElement(returnTypeRef, returnTypeExpression); } // add parameters to the template List jetParameters = parameterList.getParameters(); - List parameterTypeExpressions = new ArrayList(); assert jetParameters.size() == parameters.size(); + TypeExpression[] parameterTypeExpressions = new TypeExpression[parameters.size()]; for (int i = 0; i < parameters.size(); i++) { Parameter parameter = parameters.get(i); JetParameter jetParameter = jetParameters.get(i); // add parameter type to the template - TypeExpression parameterTypeExpression = new TypeExpression(project, parameter.getType().getPossibleTypes()); - parameterTypeExpressions.add(parameterTypeExpression); + JetType[] typeOptions = parameter.getType().getPossibleTypes(); + parameterTypeExpressions[i] = new TypeExpression(typeOptions); JetTypeReference parameterTypeRef = jetParameter.getTypeReference(); assert parameterTypeRef != null; - builder.replaceElement(parameterTypeRef, parameterTypeExpression); + builder.replaceElement(parameterTypeRef, parameterTypeExpressions[i]); // add parameter name to the template String[] possibleNamesFromExpression = parameter.getType().getPossibleNamesFromExpression(); @@ -468,7 +451,18 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { } else { possibleNames = possibleNamesFromExpression; } - Expression parameterNameExpression = new ParameterNameExpression(possibleNames, parameterTypeExpression); + + // figure out suggested names for each type option + Map parameterTypeToNamesMap = new HashMap(); + String[] typeOptionStrings = parameterTypeExpressions[i].getOptionStrings(); + assert typeOptions.length == typeOptionStrings.length; + for (int j = 0; j < typeOptions.length; j++) { + String[] suggestedNames = JetNameSuggester.suggestNamesForType(typeOptions[j], JetNameValidator.getEmptyValidator(project)); + parameterTypeToNamesMap.put(typeOptionStrings[j], suggestedNames); + } + + // add expression to builder + Expression parameterNameExpression = new ParameterNameExpression(possibleNames, parameterTypeToNamesMap); PsiElement parameterNameIdentifier = jetParameter.getNameIdentifier(); assert parameterNameIdentifier != null; builder.replaceElement(parameterNameIdentifier, parameterNameExpression); @@ -479,7 +473,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { // need to create the segment first and then hack the Expression into the template later. We use this template to update the type // parameter list as the user makes selections in the parameter types, and we need alwaysStopAt to be false so the user can't tab to // it. - Map typeParameterMap = new HashMap(); + Map typeParameterMap = new HashMap(); DeclarationDescriptor ownerDescriptor = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, owner); JetScope scope; if (ownerDescriptor instanceof NamespaceDescriptor) { @@ -490,20 +484,19 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { scope = classDescriptor.getMemberScope(classDescriptor.getDefaultType().getArguments()); } - String[] ownerTypeParameterNames = ArrayUtil.EMPTY_STRING_ARRAY; - if (isExtension) { - Set typeParameters = getTypeParametersInType(ownerType); - typeParameterMap.put(ownerType, ownerTypeParameterNames = getTypeParameterNamesNotInScope(typeParameters, scope)); - } - for (Parameter parameter : parameters) { - for (JetType type : parameter.getType().getPossibleTypes()) { - // get a list of type parameter candidates - Set typeParameters = getTypeParametersInType(type); - typeParameterMap.put(type, getTypeParameterNamesNotInScope(typeParameters, scope)); + Set ownerTypeParameters = getTypeParametersInType(ownerType); + String[] ownerTypeParameterNames = getTypeParameterNamesNotInScope(ownerTypeParameters, scope); + for (TypeExpression parameterTypeExpression : parameterTypeExpressions) { + JetType[] parameterTypeOptions = parameterTypeExpression.getOptions(); + String[] parameterTypeOptionStrings = parameterTypeExpression.getOptionStrings(); + assert parameterTypeOptions.length == parameterTypeOptionStrings.length; + for (int i = 0; i < parameterTypeOptions.length; i++) { + Set typeParameters = getTypeParametersInType(parameterTypeOptions[i]); + typeParameterMap.put(parameterTypeOptionStrings[i], getTypeParameterNamesNotInScope(typeParameters, scope)); } } - Expression expression = new TypeParameterListExpression(ownerTypeParameterNames, typeParameterMap, parameterTypeExpressions); + Expression expression = new TypeParameterListExpression(ownerTypeParameterNames, typeParameterMap); builder.replaceElement(func, TextRange.create(3, 3), TYPE_PARAMETER_LIST_VARIABLE_NAME, null, false); // ((3, 3) is after "fun") // the template built by TemplateBuilderImpl is ordered by element position, but we want types to be first, so hack it @@ -550,6 +543,21 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { return true; } + @NotNull + private static String renderType(JetType type) { + return DescriptorRenderer.TEXT.renderType(type); + // TODO: take into account imports and stuff; how to refer to a type with the simplest name with imports (currently uses fully qualified name)? + } + + @NotNull + private static String[] renderTypes(JetType[] types) { + String[] typeStrings = new String[types.length]; + for (int i = 0; i < types.length; i++) { + typeStrings[i] = renderType(types[i]); + } + return typeStrings; + } + @NotNull private static String[] getTypeParameterNamesNotInScope(Collection typeParameters, JetScope scope) { List typeParameterNames = new ArrayList();