relevant method chain completion: introduce new field if completion for field initializer is called (EA-103019)

This commit is contained in:
Dmitry Batkovich
2017-06-14 16:53:21 +03:00
parent e8cc55d2c3
commit b1d40b518b
8 changed files with 107 additions and 14 deletions
@@ -105,7 +105,7 @@ public class MethodChainLookupRangingHelper {
if (method.hasModifierProperty(PsiModifier.STATIC)) return null;
PsiNamedElement element = context.getQualifiers(qualifierClass).findFirst().orElse(null);
if (element == null) {
return new ChainCompletionNewVariableLookupElement(qualifierClass);
return new ChainCompletionNewVariableLookupElement(qualifierClass, context);
} else {
if (element instanceof PsiVariable) {
return new VariableLookupItem((PsiVariable)element);
@@ -125,7 +125,13 @@ public class MethodChainCompletionContributor extends CompletionContributor {
if (parent instanceof PsiLocalVariable) {
return extractContextFromVariable((PsiLocalVariable)parent, parameters);
}
PsiMethod method = ((PsiMethodCallExpression)parent).resolveMethod();
return extractContextFromMethodCall((PsiMethodCallExpression)parent, parameters);
}
@Nullable
private static ChainCompletionContext extractContextFromMethodCall(PsiMethodCallExpression parent,
CompletionParameters parameters) {
PsiMethod method = parent.resolveMethod();
if (method == null) return null;
PsiExpression expression = PsiTreeUtil.getParentOfType(parameters.getPosition(), PsiExpression.class);
PsiExpressionList expressionList = PsiTreeUtil.getParentOfType(parameters.getPosition(), PsiExpressionList.class);
@@ -19,11 +19,13 @@ import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.completion.PreferByKindWeigher;
import com.intellij.codeInsight.lookup.AutoCompletionPolicy;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.compiler.chainsSearch.context.ChainCompletionContext;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
@@ -35,13 +37,17 @@ public class ChainCompletionNewVariableLookupElement extends LookupElement {
private final PsiClass myQualifierClass;
@NotNull
private final String myNewVarName;
private final boolean myField;
public ChainCompletionNewVariableLookupElement(@NotNull final PsiClass qualifierClass) {
public ChainCompletionNewVariableLookupElement(@NotNull PsiClass qualifierClass,
@NotNull ChainCompletionContext context) {
Project project = qualifierClass.getProject();
JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project);
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
myField = PsiTreeUtil.getParentOfType(context.getContextPsi(), PsiStatement.class, false) == null;
VariableKind variableKind = myField ? VariableKind.FIELD : VariableKind.LOCAL_VARIABLE;
myNewVarName = Stream
.of(codeStyleManager.suggestVariableName(VariableKind.LOCAL_VARIABLE, null, null, elementFactory.createType(qualifierClass)).names)
.of(codeStyleManager.suggestVariableName(variableKind, null, null, elementFactory.createType(qualifierClass)).names)
.sorted(Comparator.comparing(String::length).reversed())
.findFirst()
.orElseThrow(IllegalStateException::new);
@@ -58,18 +64,31 @@ public class ChainCompletionNewVariableLookupElement extends LookupElement {
public void handleInsert(final InsertionContext context) {
final PsiFile file = context.getFile();
final PsiElement caretElement = ObjectUtils.notNull(file.findElementAt(context.getEditor().getCaretModel().getOffset()));
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(context.getProject());
final PsiStatement statement = PsiTreeUtil.getParentOfType(caretElement.getPrevSibling(), PsiStatement.class, false);
final Project project = context.getProject();
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
context.commitDocument();
final PsiStatement newVarDeclarationTemplate = elementFactory.createVariableDeclarationStatement(myNewVarName,
elementFactory.createType(myQualifierClass),
elementFactory.createExpressionFromText(PsiKeyword.NULL, null));
PsiElement varDeclaration = statement.getParent().addBefore(newVarDeclarationTemplate, statement);
PsiElement newVariablePlacementAnchor;
PsiElement newVarDeclarationTemplate;
if (myField) {
PsiField field = ObjectUtils.notNull(PsiTreeUtil.getParentOfType(caretElement.getPrevSibling(), PsiField.class, false));
newVariablePlacementAnchor = field;
PsiField newField = elementFactory.createField(myNewVarName, elementFactory.createType(myQualifierClass));
if (field.hasModifierProperty(PsiModifier.STATIC)) {
PsiUtil.setModifierProperty(newField, PsiModifier.STATIC, true);
}
newVarDeclarationTemplate = newField;
} else {
newVariablePlacementAnchor = ObjectUtils.notNull(PsiTreeUtil.getParentOfType(caretElement.getPrevSibling(), PsiStatement.class, false));
newVarDeclarationTemplate = elementFactory.createVariableDeclarationStatement(myNewVarName,
elementFactory.createType(myQualifierClass),
elementFactory.createExpressionFromText(PsiKeyword.NULL, null));
}
PsiElement varDeclaration = newVariablePlacementAnchor.getParent().addBefore(newVarDeclarationTemplate, newVariablePlacementAnchor);
JavaCodeStyleManager.getInstance(context.getProject()).shortenClassReferences(varDeclaration);
}
@NotNull
@Override
public String getLookupString() {
@@ -113,6 +113,11 @@ public class ChainCompletionContext {
return false;
}
@NotNull
public PsiElement getContextPsi() {
return myContext;
}
public PsiFile getContextFile() {
return myContext.getContainingFile();
}
@@ -0,0 +1,18 @@
import java.jang.String;
interface PsiClass {
PsiMethod getMethod();
}
interface PsiMethod {}
public class TestCompletion {
private static PsiClass psiClass;
static Object f = get(psiClass.getMethod());
static Object get(PsiMethod m) {
return null;
}
}
@@ -0,0 +1,17 @@
import java.jang.String;
interface PsiClass {
PsiMethod getMethod();
}
interface PsiMethod {}
public class TestCompletion {
static Object f = get(<caret>);
static Object get(PsiMethod m) {
return null;
}
}
@@ -0,0 +1,23 @@
public class TestIndex {
Object o;
public void statMethod(PsiClass c) {
c.getMethod();
c.getMethod();
c.getMethod();
c.getMethod();
c.getMethod();
c.getMethod();
c.getMethod();
c.getMethod();
c.getMethod();
c.getMethod();
}
}
interface PsiClass {
PsiMethod getMethod();
}
interface PsiMethod {}
@@ -226,6 +226,10 @@ public class MethodChainsCompletionTest extends AbstractCompilerAwareTest {
assertEquals("f.createClass", element.getLookupString());
}
public void testMethodCallInFieldInitializer() {
doTestRendering();
}
public void assertAdvisorLookupElementEquals(String lookupText,
int unreachableParametersCount,
int chainSize,
@@ -255,10 +259,11 @@ public class MethodChainsCompletionTest extends AbstractCompilerAwareTest {
if (element instanceof JavaRelevantChainLookupElement) {
myFixture.getLookup().setCurrentItem(element);
myFixture.finishLookup(Lookup.AUTO_INSERT_SELECT_CHAR);
break;
myFixture.checkResultByFile(getAfterCompletionFilePath());
return;
}
}
myFixture.checkResultByFile(getAfterCompletionFilePath());
fail("relevant method chain isn't foun");
}
private List<JavaRelevantChainLookupElement> doCompletion() {