mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-13 21:55:01 +07:00
"initialize variable" & "initialize in constructor" suggests default constructors (with inheritor's if collection) (IDEA-145431)
This commit is contained in:
@@ -221,19 +221,26 @@ public class JavaInheritorsGetter extends CompletionProvider<CompletionParameter
|
||||
public static void processInheritors(final CompletionParameters parameters,
|
||||
final Collection<PsiClassType> expectedClassTypes,
|
||||
final PrefixMatcher matcher, final Consumer<PsiType> consumer) {
|
||||
processInheritors(parameters.getOriginalFile(), parameters.getPosition(), expectedClassTypes, matcher, consumer);
|
||||
}
|
||||
|
||||
public static void processInheritors(final PsiFile file,
|
||||
final PsiElement context,
|
||||
final Collection<PsiClassType> expectedClassTypes,
|
||||
final PrefixMatcher matcher, final Consumer<PsiType> consumer) {
|
||||
//quick
|
||||
if (!processMostProbableInheritors(parameters, expectedClassTypes, consumer)) return;
|
||||
if (!processMostProbableInheritors(file, context, expectedClassTypes, consumer)) return;
|
||||
|
||||
//long
|
||||
for (final PsiClassType type : expectedClassTypes) {
|
||||
CodeInsightUtil.processSubTypes(type, parameters.getPosition(), false, matcher, consumer);
|
||||
CodeInsightUtil.processSubTypes(type, context, false, matcher, consumer);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean processMostProbableInheritors(CompletionParameters parameters,
|
||||
private static boolean processMostProbableInheritors(PsiFile contextFile,
|
||||
PsiElement context,
|
||||
Collection<PsiClassType> expectedClassTypes,
|
||||
Consumer<PsiType> consumer) {
|
||||
PsiFile file = parameters.getOriginalFile();
|
||||
for (final PsiClassType type : expectedClassTypes) {
|
||||
consumer.consume(type);
|
||||
|
||||
@@ -243,15 +250,15 @@ public class JavaInheritorsGetter extends CompletionProvider<CompletionParameter
|
||||
|
||||
final PsiSubstitutor baseSubstitutor = baseResult.getSubstitutor();
|
||||
|
||||
final Processor<PsiClass> processor = CodeInsightUtil.createInheritorsProcessor(parameters.getPosition(), type, 0, false,
|
||||
final Processor<PsiClass> processor = CodeInsightUtil.createInheritorsProcessor(context, type, 0, false,
|
||||
consumer, baseClass, baseSubstitutor);
|
||||
final StatisticsInfo[] stats = StatisticsManager.getInstance().getAllValues(JavaStatisticsManager.getAfterNewKey(type));
|
||||
for (final StatisticsInfo statisticsInfo : stats) {
|
||||
final String value = statisticsInfo.getValue();
|
||||
if (value.startsWith(JavaStatisticsManager.CLASS_PREFIX)) {
|
||||
final String qname = value.substring(JavaStatisticsManager.CLASS_PREFIX.length());
|
||||
final PsiClass psiClass = JavaPsiFacade.getInstance(file.getProject()).findClass(qname, file.getResolveScope());
|
||||
if (psiClass != null && !PsiTreeUtil.isAncestor(file, psiClass, true) && !processor.process(psiClass)) break;
|
||||
final PsiClass psiClass = JavaPsiFacade.getInstance(contextFile.getProject()).findClass(qname, contextFile.getResolveScope());
|
||||
if (psiClass != null && !PsiTreeUtil.isAncestor(contextFile, psiClass, true) && !processor.process(psiClass)) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+133
-15
@@ -16,18 +16,33 @@
|
||||
package com.intellij.codeInsight.daemon.impl.quickfix;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightBundle;
|
||||
import com.intellij.codeInsight.CodeInsightUtilCore;
|
||||
import com.intellij.codeInsight.FileModificationService;
|
||||
import com.intellij.codeInsight.completion.*;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.codeInsight.lookup.*;
|
||||
import com.intellij.codeInsight.template.*;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.ScrollType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.refactoring.util.RefactoringUtil;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.util.containers.ContainerUtil.*;
|
||||
|
||||
public class AddVariableInitializerFix implements IntentionAction {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.AddReturnFix");
|
||||
@@ -62,9 +77,10 @@ public class AddVariableInitializerFix implements IntentionAction {
|
||||
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
|
||||
if (!FileModificationService.getInstance().prepareFileForWrite(myVariable.getContainingFile())) return;
|
||||
|
||||
String initializerText = suggestInitializer();
|
||||
PsiElementFactory factory = JavaPsiFacade.getInstance(myVariable.getProject()).getElementFactory();
|
||||
PsiExpression initializer = factory.createExpressionFromText(initializerText, myVariable);
|
||||
final LookupElement[] suggestedInitializers = suggestInitializer(myVariable);
|
||||
LOG.assertTrue(suggestedInitializers.length > 0);
|
||||
LOG.assertTrue(suggestedInitializers[0] instanceof ExpressionLookupItem);
|
||||
final PsiExpression initializer = (PsiExpression) suggestedInitializers[0].getObject();
|
||||
if (myVariable instanceof PsiLocalVariable) {
|
||||
((PsiLocalVariable)myVariable).setInitializer(initializer);
|
||||
}
|
||||
@@ -74,17 +90,119 @@ public class AddVariableInitializerFix implements IntentionAction {
|
||||
else {
|
||||
LOG.error("Unknown variable type: "+myVariable);
|
||||
}
|
||||
PsiVariable var = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(myVariable);
|
||||
TextRange range = var.getInitializer().getTextRange();
|
||||
int offset = range.getStartOffset();
|
||||
editor.getCaretModel().moveToOffset(offset);
|
||||
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
|
||||
editor.getSelectionModel().setSelection(range.getStartOffset(), range.getEndOffset());
|
||||
runAssignmentTemplate(Collections.singletonList(myVariable.getInitializer()), suggestedInitializers, editor);
|
||||
}
|
||||
|
||||
private String suggestInitializer() {
|
||||
PsiType type = myVariable.getType();
|
||||
return PsiTypesUtil.getDefaultValueOfType(type);
|
||||
public static void runAssignmentTemplate(@NotNull final List<PsiExpression> initializers,
|
||||
@NotNull final LookupElement[] suggestedInitializers,
|
||||
@Nullable Editor editor) {
|
||||
if (editor == null) return;
|
||||
LOG.assertTrue(!initializers.isEmpty());
|
||||
final PsiExpression initializer = ContainerUtil.getFirstItem(initializers);
|
||||
PsiElement context = initializers.size() == 1 ? initializer : PsiTreeUtil.findCommonParent(initializers);
|
||||
PsiDocumentManager.getInstance(initializer.getProject()).doPostponedOperationsAndUnblockDocument(editor.getDocument());
|
||||
final TemplateBuilderImpl builder = (TemplateBuilderImpl)TemplateBuilderFactory.getInstance().createTemplateBuilder(context);
|
||||
for (PsiExpression e : initializers) {
|
||||
builder.replaceElement(e, new Expression() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Result calculateResult(ExpressionContext context1) {
|
||||
return calculateQuickResult(context1);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Result calculateQuickResult(ExpressionContext context1) {
|
||||
return new PsiElementResult(suggestedInitializers[0].getPsiElement());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public LookupElement[] calculateLookupItems(ExpressionContext context1) {
|
||||
return suggestedInitializers;
|
||||
}
|
||||
});
|
||||
}
|
||||
builder.run(editor, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static LookupElement[] suggestInitializer(final PsiVariable variable) {
|
||||
PsiType type = variable.getType();
|
||||
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(variable.getProject());
|
||||
if (type instanceof PsiClassType) {
|
||||
final PsiClass aClass = PsiTypesUtil.getPsiClass(type);
|
||||
if (aClass != null) {
|
||||
final LookupElement nullLookupItem = new ExpressionLookupItem(elementFactory.createExpressionFromText(PsiKeyword.NULL, variable));
|
||||
if (InheritanceUtil.isInheritor(aClass, CommonClassNames.JAVA_LANG_ITERABLE) ||
|
||||
InheritanceUtil.isInheritor(aClass, CommonClassNames.JAVA_UTIL_MAP)) {
|
||||
final List<PsiType> suggestedTypes = new SmartList<PsiType>();
|
||||
JavaInheritorsGetter.processInheritors(variable.getContainingFile(), variable, Collections.singleton((PsiClassType) type), PrefixMatcher.ALWAYS_TRUE, new Consumer<PsiType>() {
|
||||
@Override
|
||||
public void consume(PsiType type) {
|
||||
LOG.assertTrue(type instanceof PsiClassType);
|
||||
final PsiClass psiClass = PsiTypesUtil.getPsiClass(type);
|
||||
if (psiClass != null &&
|
||||
!psiClass.isInterface() &&
|
||||
!psiClass.hasModifierProperty(PsiModifier.ABSTRACT) &&
|
||||
PsiUtil.hasDefaultConstructor(psiClass)) {
|
||||
suggestedTypes.add(type);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
List<LookupElement> sortedLookups = map(sorted(map(suggestedTypes, new Function<PsiType, LookupElement>() {
|
||||
@Override
|
||||
public LookupElement fun(PsiType type) {
|
||||
return PsiTypeLookupItem.createLookupItem(type, variable);
|
||||
}
|
||||
}), new Comparator<LookupElement>() {
|
||||
@Override
|
||||
public int compare(LookupElement o1, LookupElement o2) {
|
||||
final int count1 = StatisticsWeigher.getBaseStatisticsInfo(o1, null).getUseCount();
|
||||
final int count2 = StatisticsWeigher.getBaseStatisticsInfo(o2, null).getUseCount();
|
||||
return count2 - count1;
|
||||
}
|
||||
}), new Function<LookupElement, LookupElement>() {
|
||||
@Override
|
||||
public LookupElement fun(LookupElement element) {
|
||||
final LookupElementDecorator<LookupElement> constructorLookupElement =
|
||||
LookupElementDecorator.withInsertHandler(element, ConstructorInsertHandler.BASIC_INSTANCE);
|
||||
return new LookupElementDecorator<LookupElement>(constructorLookupElement) {
|
||||
@Override
|
||||
public void renderElement(LookupElementPresentation presentation) {
|
||||
super.renderElement(presentation);
|
||||
presentation.setTailText("");
|
||||
presentation.setItemText(PsiKeyword.NEW + " " + presentation.getItemText() + "()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleInsert(InsertionContext context) {
|
||||
super.handleInsert(context);
|
||||
context.getDocument().insertString(context.getStartOffset(), PsiKeyword.NEW + " ");
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
LookupElement[] result = new LookupElement[sortedLookups.size() + 1];
|
||||
result[0] = nullLookupItem;
|
||||
for (int i = 0; i < sortedLookups.size(); i++) {
|
||||
LookupElement lookup = sortedLookups.get(i);
|
||||
result[i + 1] = lookup;
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
if (PsiUtil.hasDefaultConstructor(aClass)) {
|
||||
final PsiExpression newExpression = elementFactory
|
||||
.createExpressionFromText(PsiKeyword.NEW + " " + type.getCanonicalText(false) + "()", variable);
|
||||
return new LookupElement[]{nullLookupItem, new ExpressionLookupItem(newExpression)};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
final String defaultValue = PsiTypesUtil.getDefaultValueOfType(type);
|
||||
final PsiExpression expression = elementFactory.createExpressionFromText(defaultValue, variable);
|
||||
return new LookupElement[] {new ExpressionLookupItem(expression)};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+18
-49
@@ -15,23 +15,20 @@
|
||||
*/
|
||||
package com.intellij.codeInsight.daemon.impl.quickfix;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightUtilCore;
|
||||
import com.intellij.codeInsight.FileModificationService;
|
||||
import com.intellij.codeInsight.daemon.QuickFixBundle;
|
||||
import com.intellij.codeInsight.generation.PsiMethodMember;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.ide.util.MemberChooser;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.ScrollType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -93,59 +90,34 @@ public class InitializeFinalFieldInConstructorFix implements IntentionAction {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final List<PsiExpressionStatement> statements = addFieldInitialization(constructors, myField, project);
|
||||
final PsiExpressionStatement highestStatement = getHighestElement(statements);
|
||||
if (highestStatement == null) return;
|
||||
|
||||
final PsiAssignmentExpression expression = (PsiAssignmentExpression)highestStatement.getExpression();
|
||||
final PsiElement rightExpression = expression.getRExpression();
|
||||
|
||||
final TextRange expressionRange = rightExpression.getTextRange();
|
||||
editor.getCaretModel().moveToOffset(expressionRange.getStartOffset());
|
||||
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
|
||||
editor.getSelectionModel().setSelection(expressionRange.getStartOffset(), expressionRange.getEndOffset());
|
||||
addFieldInitialization(constructors, myField, project, editor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static <T extends PsiElement> T getHighestElement(@NotNull List<T> elements) {
|
||||
T highest = null;
|
||||
int highestTextOffset = Integer.MAX_VALUE;
|
||||
for (T element : elements) {
|
||||
final T forcedElem = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(element);
|
||||
final int startOffset = forcedElem.getTextOffset();
|
||||
if (startOffset < highestTextOffset) {
|
||||
highest = forcedElem;
|
||||
highestTextOffset = startOffset;
|
||||
}
|
||||
}
|
||||
return highest;
|
||||
}
|
||||
private static void addFieldInitialization(@NotNull List<PsiMethod> constructors,
|
||||
@NotNull PsiField field,
|
||||
@NotNull Project project,
|
||||
@Nullable Editor editor) {
|
||||
final List<PsiExpression> rExpressions = new ArrayList<PsiExpression>(constructors.size());
|
||||
final LookupElement[] suggestedInitializers = AddVariableInitializerFix.suggestInitializer(field);
|
||||
|
||||
@NotNull
|
||||
private static List<PsiExpressionStatement> addFieldInitialization(@NotNull List<PsiMethod> constructors,
|
||||
@NotNull PsiField field,
|
||||
@NotNull Project project) {
|
||||
final List<PsiExpressionStatement> statements = new ArrayList<PsiExpressionStatement>();
|
||||
for (PsiMethod constructor : constructors) {
|
||||
final PsiExpressionStatement statement = addFieldInitialization(constructor, field, project);
|
||||
if (statement != null) {
|
||||
statements.add(statement);
|
||||
}
|
||||
rExpressions.add(addFieldInitialization(constructor, suggestedInitializers, field, project));
|
||||
}
|
||||
return statements;
|
||||
AddVariableInitializerFix.runAssignmentTemplate(rExpressions, suggestedInitializers, editor);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiExpressionStatement addFieldInitialization(@NotNull PsiMethod constructor,
|
||||
@NotNull PsiField field,
|
||||
@NotNull Project project) {
|
||||
private static PsiExpression addFieldInitialization(@NotNull PsiMethod constructor,
|
||||
@NotNull LookupElement[] suggestedInitializers,
|
||||
@NotNull PsiField field,
|
||||
@NotNull Project project) {
|
||||
PsiCodeBlock methodBody = constructor.getBody();
|
||||
if (methodBody == null) return null;
|
||||
|
||||
final String fieldName = field.getName();
|
||||
String stmtText = fieldName + " = " + suggestInitValue(field) + ";";
|
||||
String stmtText = fieldName + " = " + suggestedInitializers[0].getPsiElement().getText() + ";";
|
||||
if (methodContainsParameterWithName(constructor, fieldName)) {
|
||||
stmtText = "this." + stmtText;
|
||||
}
|
||||
@@ -154,7 +126,9 @@ public class InitializeFinalFieldInConstructorFix implements IntentionAction {
|
||||
final PsiElementFactory factory = JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory();
|
||||
final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
|
||||
|
||||
return (PsiExpressionStatement)methodBody.add(codeStyleManager.reformat(factory.createStatementFromText(stmtText, methodBody)));
|
||||
final PsiExpressionStatement addedStatement = (PsiExpressionStatement)methodBody.add(codeStyleManager
|
||||
.reformat(factory.createStatementFromText(stmtText, methodBody)));
|
||||
return ((PsiAssignmentExpression)addedStatement.getExpression()).getRExpression();
|
||||
}
|
||||
|
||||
private static boolean methodContainsParameterWithName(@NotNull PsiMethod constructor, @NotNull String name) {
|
||||
@@ -228,11 +202,6 @@ public class InitializeFinalFieldInConstructorFix implements IntentionAction {
|
||||
return result.toArray(new PsiMethod[result.size()]);
|
||||
}
|
||||
|
||||
private static String suggestInitValue(@NotNull PsiField field) {
|
||||
PsiType type = field.getType();
|
||||
return PsiTypesUtil.getDefaultValueOfType(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startInWriteAction() {
|
||||
return false;
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
class AClass
|
||||
{
|
||||
int f() {
|
||||
int i = <caret><selection>0</selection>;
|
||||
int i = 0<caret>;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,6 +2,6 @@
|
||||
|
||||
class AClass
|
||||
{
|
||||
final AClass f = <caret><selection>null</selection>;
|
||||
final AClass f = null<caret>;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -3,10 +3,10 @@ class A {
|
||||
private final int var;
|
||||
|
||||
private Main() {
|
||||
var = <caret><selection>0</selection>;
|
||||
var = 0;
|
||||
}
|
||||
|
||||
private Main(int var) {
|
||||
this.var = 0;
|
||||
}
|
||||
}
|
||||
}<caret>
|
||||
+1
-1
@@ -3,6 +3,6 @@ class A {
|
||||
private final int var;
|
||||
|
||||
private Main(int var) {
|
||||
this.var = <caret><selection>0</selection>;
|
||||
this.var = 0<caret>;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -3,6 +3,6 @@ class A {
|
||||
private final int var;
|
||||
|
||||
private Main() {
|
||||
var = <caret><selection>0</selection>;
|
||||
var = 0<caret>;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user