method chain completion: remove sub lookup element (automatic argument insertion)

This commit is contained in:
Dmitry Batkovich
2017-05-15 16:27:16 +03:00
parent a56d2a511b
commit 18592664b7
8 changed files with 20 additions and 315 deletions
@@ -18,14 +18,13 @@ package com.intellij.compiler.chainsSearch;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.completion.JavaChainLookupElement;
import com.intellij.codeInsight.completion.JavaMethodCallElement;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.codeInsight.lookup.LookupElementDecorator;
import com.intellij.codeInsight.lookup.VariableLookupItem;
import com.intellij.compiler.chainsSearch.completion.lookup.ChainCompletionNewVariableLookupElement;
import com.intellij.compiler.chainsSearch.completion.lookup.WeightableChainLookupElement;
import com.intellij.compiler.chainsSearch.completion.lookup.sub.GetterLookupSubLookupElement;
import com.intellij.compiler.chainsSearch.completion.lookup.sub.SubLookupElement;
import com.intellij.compiler.chainsSearch.completion.lookup.sub.VariableSubLookupElement;
import com.intellij.compiler.chainsSearch.context.ChainCompletionContext;
import com.intellij.openapi.editor.Document;
import com.intellij.psi.*;
@@ -33,14 +32,12 @@ import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.TIntObjectHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.stream.Collectors;
import static com.intellij.compiler.chainsSearch.completion.lookup.ChainCompletionLookupElementUtil.createLookupElement;
import static com.intellij.psi.CommonClassNames.JAVA_LANG_STRING;
public class MethodsChainLookupRangingHelper {
@@ -152,25 +149,19 @@ public class MethodsChainLookupRangingHelper {
boolean hasCallingVariableInContext = false;
boolean introduceNewVariable = false;
PsiParameterList parameterList = method.getParameterList();
TIntObjectHashMap<SubLookupElement> parametersMap = new TIntObjectHashMap<>(parameterList.getParametersCount());
PsiParameter[] parameters = parameterList.getParameters();
for (int i = 0; i < parameters.length; i++) {
PsiParameter parameter = parameters[i];
for (PsiParameter parameter : parameters) {
PsiType type = parameter.getType();
if (type.equalsToText(JAVA_LANG_STRING)) {
PsiElement relevantStringElement = context.findRelevantStringInContext(parameter.getName());
if (relevantStringElement == null) {
notMatchedStringVars++;
}
else {
parametersMap.put(i, createSubLookup(relevantStringElement));
}
}
else if (!ChainCompletionContext.isWidelyUsed(type)) {
Collection<PsiElement> contextVariables = context.getQualifiers(type).collect(Collectors.toList());
PsiElement contextVariable = ContainerUtil.getFirstItem(contextVariables, null);
if (contextVariable != null) {
if (contextVariables.size() == 1) parametersMap.put(i, createSubLookup(contextVariable));
matchedParametersInContext++;
continue;
}
@@ -183,7 +174,7 @@ public class MethodsChainLookupRangingHelper {
if (isHeadMethod) {
if (method.hasModifierProperty(PsiModifier.STATIC)) {
hasCallingVariableInContext = true;
lookupElement = createLookupElement(method, parametersMap);
lookupElement = createMethodLookupElement(method);
}
else if (method.isConstructor()) {
return null;
@@ -196,7 +187,7 @@ public class MethodsChainLookupRangingHelper {
firstChainElement = new VariableLookupItem((PsiVariable)e);
}
else if (e instanceof PsiMethod) {
firstChainElement = createLookupElement((PsiMethod)e, null);
firstChainElement = createMethodLookupElement((PsiMethod)e);
}
else if (e instanceof LookupElement) {
firstChainElement = (LookupElement)e;
@@ -205,10 +196,10 @@ public class MethodsChainLookupRangingHelper {
throw new AssertionError();
}
hasCallingVariableInContext = true;
lookupElement = new JavaChainLookupElement(firstChainElement, createLookupElement(method, parametersMap));
lookupElement = new JavaChainLookupElement(firstChainElement, createMethodLookupElement(method));
}
else {
lookupElement = createLookupElement(method, parametersMap);
lookupElement = createMethodLookupElement(method);
if (!context.hasQualifier(qualifierClass)) {
introduceNewVariable = true;
}
@@ -216,7 +207,7 @@ public class MethodsChainLookupRangingHelper {
}
}
else {
lookupElement = createLookupElement(method, parametersMap);
lookupElement = createMethodLookupElement(method);
}
return new MethodProcResult(lookupElement,
unreachableParametersCount,
@@ -227,10 +218,17 @@ public class MethodsChainLookupRangingHelper {
}
@NotNull
private static SubLookupElement createSubLookup(PsiElement relevantStringElement) {
return relevantStringElement instanceof PsiMethod
? new GetterLookupSubLookupElement((PsiMethod)relevantStringElement)
: new VariableSubLookupElement((PsiVariable)relevantStringElement);
private static LookupElement createMethodLookupElement(@NotNull PsiMethod method) {
LookupElement result;
if (method.isConstructor()) {
//noinspection ConstantConditions
result = LookupElementBuilder.create(String.format("%s %s", PsiKeyword.NEW, method.getContainingClass().getName()));
} else if (method.hasModifierProperty(PsiModifier.STATIC)) {
result = new JavaMethodCallElement(method, false, true);
} else {
result = new JavaMethodCallElement(method);
}
return result;
}
private static class MethodProcResult {
@@ -1,68 +0,0 @@
/*
* 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.compiler.chainsSearch.completion.lookup;
import com.intellij.compiler.chainsSearch.completion.lookup.sub.SubLookupElement;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.psi.PsiKeyword;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiParameter;
import gnu.trove.TIntObjectHashMap;
import org.jetbrains.annotations.Nullable;
/**
* @author Dmitry Batkovich
*/
public final class ChainCompletionLookupElementUtil {
private ChainCompletionLookupElementUtil() {
}
public static LookupElement createLookupElement(final PsiMethod method,
final @Nullable TIntObjectHashMap<SubLookupElement> replaceElements) {
if (method.isConstructor()) {
//noinspection ConstantConditions
return LookupElementBuilder.create(String.format("%s %s", PsiKeyword.NEW, method.getContainingClass().getName()));
} else if (method.hasModifierProperty(PsiModifier.STATIC)) {
return new ChainCompletionMethodCallLookupElement(method, replaceElements, false, true);
} else {
return new ChainCompletionMethodCallLookupElement(method, replaceElements);
}
}
public static String fillMethodParameters(final PsiMethod method, @Nullable final TIntObjectHashMap<SubLookupElement> replaceElements) {
final TIntObjectHashMap<SubLookupElement> notNullReplaceElements = replaceElements == null ?
new TIntObjectHashMap<>(0) :
replaceElements;
final PsiParameter[] parameters = method.getParameterList().getParameters();
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < parameters.length; i++) {
if (i != 0) {
sb.append(", ");
}
final PsiParameter parameter = parameters[i];
final SubLookupElement replaceElement = notNullReplaceElements.get(i);
if (replaceElement != null) {
sb.append(replaceElement.getInsertString());
} else {
sb.append(parameter.getName());
}
}
return sb.toString();
}
}
@@ -1,99 +0,0 @@
/*
* 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.compiler.chainsSearch.completion.lookup;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.completion.JavaMethodCallElement;
import com.intellij.codeInsight.completion.StaticallyImportable;
import com.intellij.codeInsight.lookup.AutoCompletionPolicy;
import com.intellij.compiler.chainsSearch.completion.lookup.sub.SubLookupElement;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.PsiMethod;
import gnu.trove.TIntObjectHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Dmitry Batkovich
*/
public class ChainCompletionMethodCallLookupElement extends JavaMethodCallElement implements StaticallyImportable {
public static final String PROP_METHODS_CHAIN_COMPLETION_AUTO_COMPLETION = "methods.chain.completion.autoCompletion";
private final PsiMethod myMethod;
@Nullable
private final TIntObjectHashMap<SubLookupElement> myReplaceElements;
private final boolean myMergedOverloads;
public ChainCompletionMethodCallLookupElement(final PsiMethod method,
final @Nullable TIntObjectHashMap<SubLookupElement> replaceElements,
final boolean shouldImportStatic,
final boolean mergedOverloads) {
super(method, shouldImportStatic, mergedOverloads);
myMethod = method;
myReplaceElements = replaceElements;
myMergedOverloads = mergedOverloads;
configureAutoCompletionPolicy();
}
public ChainCompletionMethodCallLookupElement(final PsiMethod method,
final @Nullable TIntObjectHashMap<SubLookupElement> replaceElements) {
super(method);
myMethod = method;
myReplaceElements = replaceElements;
myMergedOverloads = true;
configureAutoCompletionPolicy();
}
private void configureAutoCompletionPolicy() {
if (ApplicationManager.getApplication().isUnitTestMode()) {
if (PropertiesComponent.getInstance(myMethod.getProject()).getBoolean(PROP_METHODS_CHAIN_COMPLETION_AUTO_COMPLETION)) {
setAutoCompletionPolicy(AutoCompletionPolicy.GIVE_CHANCE_TO_OVERWRITE);
}
}
}
@Override
public void handleInsert(final InsertionContext context) {
super.handleInsert(context);
if (!myMergedOverloads || isUniqueMethod(myMethod)) {
context.commitDocument();
context.getDocument()
.insertString(context.getTailOffset() - 1, ChainCompletionLookupElementUtil.fillMethodParameters(myMethod, myReplaceElements));
final PsiFile file = context.getFile();
assert file instanceof PsiJavaFile;
final PsiJavaFile javaFile = (PsiJavaFile)file;
if (myReplaceElements != null) {
myReplaceElements.forEachValue(subLookupElement -> {
subLookupElement.doImport(javaFile);
return true;
});
}
context.commitDocument();
context.getEditor().getCaretModel().moveToOffset(context.getTailOffset());
context.commitDocument();
}
}
private static boolean isUniqueMethod(@NotNull final PsiMethod method) {
final PsiClass containingClass = method.getContainingClass();
return containingClass == null || containingClass.findMethodsByName(method.getName(), true).length == 1;
}
}
@@ -1,51 +0,0 @@
/*
* 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.compiler.chainsSearch.completion.lookup.sub;
import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.PsiMethod;
import org.jetbrains.annotations.Nullable;
/**
* @author Dmitry Batkovich
*/
public class GetterLookupSubLookupElement implements SubLookupElement {
private final String myVariableName;
private final String myMethodName;
public GetterLookupSubLookupElement(final PsiMethod method) {
this(null, method.getName());
}
public GetterLookupSubLookupElement(@Nullable final String variableName, final String methodName) {
myVariableName = variableName;
myMethodName = methodName;
}
@Override
public void doImport(final PsiJavaFile javaFile) {
}
@Override
public String getInsertString() {
final StringBuilder sb = new StringBuilder();
if (myVariableName != null) {
sb.append(myVariableName).append(".");
}
sb.append(myMethodName).append("()");
return sb.toString();
}
}
@@ -1,28 +0,0 @@
/*
* 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.compiler.chainsSearch.completion.lookup.sub;
import com.intellij.psi.PsiJavaFile;
/**
* @author Dmitry Batkovich
*/
public interface SubLookupElement {
void doImport(final PsiJavaFile javaFile);
String getInsertString();
}
@@ -1,40 +0,0 @@
/*
* 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.compiler.chainsSearch.completion.lookup.sub;
import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.PsiVariable;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class VariableSubLookupElement implements SubLookupElement {
private final String myVarName;
public VariableSubLookupElement(final PsiVariable variable) {
myVarName = variable.getName();
}
@Override
public void doImport(final PsiJavaFile javaFile) {
}
@Override
public String getInsertString() {
return myVarName;
}
}
@@ -11,6 +11,6 @@ public class TestCompletion {
void m() {
String asd = "123";
PsiManager psiManager = null;
Project p = psiManager.getProject(asd, zxc)
Project p = psiManager.getProject()
}
}
@@ -20,9 +20,7 @@ import com.intellij.codeInsight.lookup.Lookup;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.compiler.chainsSearch.ChainRelevance;
import com.intellij.compiler.chainsSearch.completion.MethodChainCompletionContributor;
import com.intellij.compiler.chainsSearch.completion.lookup.ChainCompletionMethodCallLookupElement;
import com.intellij.compiler.chainsSearch.completion.lookup.WeightableChainLookupElement;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.testFramework.SkipSlowTestLocally;
import com.intellij.util.SmartList;
@@ -248,8 +246,6 @@ public class MethodChainsCompletionTest extends AbstractCompilerAwareTest {
}
private void doTestRendering() {
PropertiesComponent.getInstance(getProject())
.setValue(ChainCompletionMethodCallLookupElement.PROP_METHODS_CHAIN_COMPLETION_AUTO_COMPLETION, String.valueOf(true));
compileAndIndexData(TEST_INDEX_FILE_NAME);
myFixture.configureByFiles(getBeforeCompletionFilePath());
for (LookupElement element : myFixture.complete(CompletionType.BASIC)) {
@@ -259,9 +255,6 @@ public class MethodChainsCompletionTest extends AbstractCompilerAwareTest {
break;
}
}
PropertiesComponent.getInstance(getProject())
.setValue(ChainCompletionMethodCallLookupElement.PROP_METHODS_CHAIN_COMPLETION_AUTO_COMPLETION, String.valueOf(false));
myFixture.checkResultByFile(getAfterCompletionFilePath());
}