Merge remote-tracking branch 'origin/master' into amakeev/remote-debug_linux

This commit is contained in:
Anton Makeev
2016-06-25 13:33:38 +02:00
325 changed files with 3472 additions and 3281 deletions
@@ -30,6 +30,7 @@ import com.intellij.refactoring.extractMethod.InputVariables;
import com.intellij.refactoring.util.RefactoringChangeUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.IntArrayList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -518,6 +519,8 @@ public class DuplicatesFinder {
final PsiJavaCodeReferenceElement candidateQualifier = ((PsiSuperExpression)candidate).getQualifier();
return contextClass == (candidateQualifier != null ? candidateQualifier.resolve() : PsiTreeUtil.getContextOfType(candidate, PsiClass.class));
}
} else if (pattern instanceof PsiModifierList) {
return candidate instanceof PsiModifierList && matchModifierList((PsiModifierList)pattern, (PsiModifierList)candidate);
}
PsiElement[] children1 = getFilteredChildren(pattern);
@@ -541,6 +544,40 @@ public class DuplicatesFinder {
return true;
}
private static boolean matchModifierList(PsiModifierList modifierList1, PsiModifierList modifierList2) {
if (!(modifierList1.getParent() instanceof PsiLocalVariable)) {
// local variables can only have a final modifier, and are considered equivalent with or without it.
for (String modifier : PsiModifier.MODIFIERS) {
if (modifierList1.hasModifierProperty(modifier)) {
if (!modifierList2.hasModifierProperty(modifier)) {
return false;
}
}
else if (modifierList2.hasModifierProperty(modifier)) {
return false;
}
}
}
final List<PsiAnnotation> annotations1 = ContainerUtil.newArrayList(modifierList1.getAnnotations());
final List<PsiAnnotation> annotations2 = ContainerUtil.newArrayList(modifierList2.getAnnotations());
annotations1.removeIf(a -> CommonClassNames.JAVA_LANG_OVERRIDE.equals(a.getQualifiedName()));
annotations2.removeIf(a -> CommonClassNames.JAVA_LANG_OVERRIDE.equals(a.getQualifiedName()));
if (annotations1.size() != annotations2.size()) {
return false;
}
for (final Iterator<PsiAnnotation> iterator = annotations1.iterator(); iterator.hasNext(); ) {
final PsiAnnotation annotation1 = iterator.next();
for (final Iterator<PsiAnnotation> iterator2 = annotations2.iterator(); iterator2.hasNext(); ) {
final PsiAnnotation annotation2 = iterator2.next();
if (PsiEquivalenceUtil.areElementsEquivalent(annotation1, annotation2)) {
iterator.remove();
iterator2.remove();
}
}
}
return true;
}
private static boolean checkParameterModification(PsiExpression expression,
final IElementType sign,
PsiExpression candidate) {
@@ -58,7 +58,7 @@ public class ConstructorInsertHandler implements InsertHandler<LookupElementDeco
boolean isAbstract = psiClass.hasModifierProperty(PsiModifier.ABSTRACT);
if (Lookup.REPLACE_SELECT_CHAR == context.getCompletionChar()) {
if (Lookup.REPLACE_SELECT_CHAR == context.getCompletionChar() && context.getOffsetMap().containsOffset(PARAM_LIST_START)) {
final int plStart = context.getOffset(PARAM_LIST_START);
final int plEnd = context.getOffset(PARAM_LIST_END);
if (plStart >= 0 && plEnd >= 0) {
@@ -15,24 +15,62 @@
*/
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.generation.ClassMember;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.codeInsight.intention.LowPriorityAction;
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
import com.intellij.codeInsight.intention.impl.ParameterClassMember;
import com.intellij.codeInsight.template.Template;
import com.intellij.codeInsight.template.TemplateBuilderImpl;
import com.intellij.codeInsight.template.impl.TextExpression;
import com.intellij.icons.AllIcons;
import com.intellij.ide.util.MemberChooser;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Iconable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
/**
* User: anna
* Date: 8/2/12
*/
public class DefineParamsDefaultValueAction extends DelegateWithDefaultParamValueIntentionAction {
public class DefineParamsDefaultValueAction extends PsiElementBaseIntentionAction implements Iconable, LowPriorityAction {
private static final Logger LOG = Logger.getInstance(DefineParamsDefaultValueAction.class);
@Override
public boolean startInWriteAction() {
return false;
}
@NotNull
@Override
public String getFamilyName() {
return "Generate overloaded method with default parameter values";
}
@Override
public Icon getIcon(int flags) {
return AllIcons.Actions.RefactoringBulb;
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
@@ -56,20 +94,106 @@ public class DefineParamsDefaultValueAction extends DelegateWithDefaultParamValu
return true;
}
@Nullable
@Override
public void invoke(@NotNull final Project project, final Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
final PsiParameter[] parameters = getParams(element);
if (parameters == null || parameters.length == 0) return;
final PsiMethod method = (PsiMethod)parameters[0].getDeclarationScope();
final PsiMethod methodPrototype = generateMethodPrototype(method, parameters);
final PsiClass containingClass = method.getContainingClass();
if (containingClass == null) return;
final PsiMethod existingMethod = containingClass.findMethodBySignature(methodPrototype, false);
if (existingMethod != null) {
editor.getCaretModel().moveToOffset(existingMethod.getTextOffset());
HintManager.getInstance().showErrorHint(editor, (existingMethod.isConstructor() ? "Constructor" : "Method") +
" with the chosen signature already exists");
return;
}
if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return;
Runnable runnable = () -> {
final PsiMethod prototype = (PsiMethod)containingClass.addBefore(methodPrototype, method);
RefactoringUtil.fixJavadocsForParams(prototype, new HashSet<PsiParameter>(Arrays.asList(prototype.getParameterList().getParameters())));
TemplateBuilderImpl builder = new TemplateBuilderImpl(prototype);
PsiCodeBlock body = prototype.getBody();
final String callArgs =
"(" + StringUtil.join(method.getParameterList().getParameters(), psiParameter -> {
if (ArrayUtil.find(parameters, psiParameter) > -1) return "IntelliJIDEARulezzz";
return psiParameter.getName();
}, ",") + ");";
final String methodCall;
if (method.getReturnType() == null) {
methodCall = "this";
} else if (!PsiType.VOID.equals(method.getReturnType())) {
methodCall = "return " + method.getName();
} else {
methodCall = method.getName();
}
LOG.assertTrue(body != null);
body.add(JavaPsiFacade.getElementFactory(project).createStatementFromText(methodCall + callArgs, method));
body = (PsiCodeBlock)CodeStyleManager.getInstance(project).reformat(body);
final PsiStatement stmt = body.getStatements()[0];
PsiExpression expr = null;
if (stmt instanceof PsiReturnStatement) {
expr = ((PsiReturnStatement)stmt).getReturnValue();
} else if (stmt instanceof PsiExpressionStatement) {
expr = ((PsiExpressionStatement)stmt).getExpression();
}
if (expr instanceof PsiMethodCallExpression) {
PsiMethodCallExpression methodCallExp = (PsiMethodCallExpression)expr;
RangeMarker rangeMarker = editor.getDocument().createRangeMarker(prototype.getTextRange());
for (PsiParameter parameter : parameters) {
final PsiExpression exprToBeDefault =
methodCallExp.getArgumentList().getExpressions()[method.getParameterList().getParameterIndex(parameter)];
builder.replaceElement(exprToBeDefault, new TextExpression(""));
}
Template template = builder.buildTemplate();
editor.getCaretModel().moveToOffset(rangeMarker.getStartOffset());
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument());
editor.getDocument().deleteString(rangeMarker.getStartOffset(), rangeMarker.getEndOffset());
rangeMarker.dispose();
CreateFromUsageBaseFix.startTemplate(editor, template, project);
}
};
if (startInWriteAction()) {
runnable.run();
} else {
ApplicationManager.getApplication().runWriteAction(runnable);
}
}
@Nullable
protected PsiParameter[] getParams(PsiElement element) {
final PsiMethod method = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
assert method != null;
final PsiParameter[] parameters = method.getParameterList().getParameters();
if (parameters.length == 1) {
return parameters;
}
final ParameterClassMember[] members = new ParameterClassMember[parameters.length];
for (int i = 0; i < members.length; i++) {
members[i] = new ParameterClassMember(parameters[i]);
}
final PsiParameter selectedParam = PsiTreeUtil.getParentOfType(element, PsiParameter.class);
final int idx = selectedParam != null ? ArrayUtil.find(parameters, selectedParam) : -1;
if (ApplicationManager.getApplication().isUnitTestMode()) {
return idx >= 0 ? new PsiParameter[] {selectedParam} : null;
}
final MemberChooser<ParameterClassMember> chooser =
new MemberChooser<ParameterClassMember>(members, false, true, element.getProject());
chooser.selectElements(members);
if (idx >= 0) {
chooser.selectElements(new ClassMember[] {members[idx]});
}
else {
chooser.selectElements(members);
}
chooser.setTitle("Choose Default Value Parameters");
chooser.setCopyJavadocVisible(false);
if (chooser.showAndGet()) {
final List<ParameterClassMember> elements = chooser.getSelectedElements();
if (elements != null) {
@@ -83,14 +207,34 @@ public class DefineParamsDefaultValueAction extends DelegateWithDefaultParamValu
return null;
}
@Override
public boolean startInWriteAction() {
return false;
}
private static PsiMethod generateMethodPrototype(PsiMethod method, PsiParameter... params) {
final PsiMethod prototype = (PsiMethod)method.copy();
final PsiCodeBlock body = prototype.getBody();
final PsiCodeBlock emptyBody = JavaPsiFacade.getElementFactory(method.getProject()).createMethodFromText("void foo(){}", prototype).getBody();
assert emptyBody != null;
if (body != null) {
body.replace(emptyBody);
} else {
prototype.getModifierList().setModifierProperty(PsiModifier.ABSTRACT, false);
prototype.addBefore(emptyBody, null);
}
@NotNull
@Override
public String getFamilyName() {
return "Generate overloaded method with default parameter values";
final PsiClass aClass = method.getContainingClass();
if (aClass != null && aClass.isInterface() && !method.hasModifierProperty(PsiModifier.STATIC)) {
prototype.getModifierList().setModifierProperty(PsiModifier.DEFAULT, true);
}
final PsiParameterList parameterList = method.getParameterList();
Arrays.sort(params, (p1, p2) -> {
final int parameterIndex1 = parameterList.getParameterIndex(p1);
final int parameterIndex2 = parameterList.getParameterIndex(p2);
return parameterIndex1 > parameterIndex2 ? -1 : 1;
});
for (PsiParameter param : params) {
final int parameterIndex = parameterList.getParameterIndex(param);
prototype.getParameterList().getParameters()[parameterIndex].delete();
}
return prototype;
}
}
@@ -1,191 +0,0 @@
/*
* Copyright 2000-2015 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.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.codeInsight.intention.LowPriorityAction;
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
import com.intellij.codeInsight.template.Template;
import com.intellij.codeInsight.template.TemplateBuilderImpl;
import com.intellij.codeInsight.template.impl.TextExpression;
import com.intellij.icons.AllIcons;
import com.intellij.lang.StdLanguages;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Iconable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
/**
* User: anna
*/
public class DelegateWithDefaultParamValueIntentionAction extends PsiElementBaseIntentionAction implements Iconable, LowPriorityAction {
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
final PsiParameter parameter = PsiTreeUtil.getParentOfType(element, PsiParameter.class);
if (parameter != null) {
if (!parameter.getLanguage().isKindOf(StdLanguages.JAVA)) return false;
final PsiElement declarationScope = parameter.getDeclarationScope();
if (declarationScope instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)declarationScope;
final PsiClass containingClass = method.getContainingClass();
if (containingClass != null && (!containingClass.isInterface() || PsiUtil.isLanguageLevel8OrHigher(method))) {
if (containingClass.findMethodBySignature(generateMethodPrototype(method, parameter), false) != null) {
return false;
}
setText("Generate overloaded " + (method.isConstructor() ? "constructor" : "method") + " with default parameter value");
return true;
}
}
}
return false;
}
@Override
public Icon getIcon(int flags) {
return AllIcons.Actions.RefactoringBulb;
}
private static PsiMethod generateMethodPrototype(PsiMethod method, PsiParameter... params) {
final PsiMethod prototype = (PsiMethod)method.copy();
final PsiCodeBlock body = prototype.getBody();
final PsiCodeBlock emptyBody = JavaPsiFacade.getElementFactory(method.getProject()).createMethodFromText("void foo(){}", prototype).getBody();
assert emptyBody != null;
if (body != null) {
body.replace(emptyBody);
} else {
prototype.getModifierList().setModifierProperty(PsiModifier.ABSTRACT, false);
prototype.addBefore(emptyBody, null);
}
final PsiClass aClass = method.getContainingClass();
if (aClass != null && aClass.isInterface() && !method.hasModifierProperty(PsiModifier.STATIC)) {
prototype.getModifierList().setModifierProperty(PsiModifier.DEFAULT, true);
}
final PsiParameterList parameterList = method.getParameterList();
Arrays.sort(params, (p1, p2) -> {
final int parameterIndex1 = parameterList.getParameterIndex(p1);
final int parameterIndex2 = parameterList.getParameterIndex(p2);
return parameterIndex1 > parameterIndex2 ? -1 : 1;
});
for (PsiParameter param : params) {
final int parameterIndex = parameterList.getParameterIndex(param);
prototype.getParameterList().getParameters()[parameterIndex].delete();
}
return prototype;
}
@Override
public void invoke(@NotNull final Project project, final Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
final PsiParameter[] parameters = getParams(element);
if (parameters == null || parameters.length == 0) return;
final PsiMethod method = (PsiMethod)parameters[0].getDeclarationScope();
final PsiMethod methodPrototype = generateMethodPrototype(method, parameters);
final PsiMethod existingMethod = method.getContainingClass().findMethodBySignature(methodPrototype, false);
if (existingMethod != null) {
editor.getCaretModel().moveToOffset(existingMethod.getTextOffset());
HintManager.getInstance().showErrorHint(editor, (existingMethod.isConstructor() ? "Constructor" : "Method") +
" with the chosen signature already exists");
return;
}
if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return;
Runnable runnable = () -> {
final PsiMethod prototype = (PsiMethod)method.getContainingClass().addBefore(methodPrototype, method);
RefactoringUtil.fixJavadocsForParams(prototype, new HashSet<PsiParameter>(Arrays.asList(prototype.getParameterList().getParameters())));
TemplateBuilderImpl builder = new TemplateBuilderImpl(prototype);
PsiCodeBlock body = prototype.getBody();
final String callArgs =
"(" + StringUtil.join(method.getParameterList().getParameters(), psiParameter -> {
if (ArrayUtil.find(parameters, psiParameter) > -1) return "IntelliJIDEARulezzz";
return psiParameter.getName();
}, ",") + ");";
final String methodCall;
if (method.getReturnType() == null) {
methodCall = "this";
} else if (!PsiType.VOID.equals(method.getReturnType())) {
methodCall = "return " + method.getName();
} else {
methodCall = method.getName();
}
body.add(JavaPsiFacade.getElementFactory(project).createStatementFromText(methodCall + callArgs, method));
body = (PsiCodeBlock)CodeStyleManager.getInstance(project).reformat(body);
final PsiStatement stmt = body.getStatements()[0];
PsiExpression expr = null;
if (stmt instanceof PsiReturnStatement) {
expr = ((PsiReturnStatement)stmt).getReturnValue();
} else if (stmt instanceof PsiExpressionStatement) {
expr = ((PsiExpressionStatement)stmt).getExpression();
}
if (expr instanceof PsiMethodCallExpression) {
PsiMethodCallExpression methodCallExp = (PsiMethodCallExpression)expr;
RangeMarker rangeMarker = editor.getDocument().createRangeMarker(prototype.getTextRange());
for (PsiParameter parameter : parameters) {
final PsiExpression exprToBeDefault =
methodCallExp.getArgumentList().getExpressions()[method.getParameterList().getParameterIndex(parameter)];
builder.replaceElement(exprToBeDefault, new TextExpression(""));
}
Template template = builder.buildTemplate();
editor.getCaretModel().moveToOffset(rangeMarker.getStartOffset());
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument());
editor.getDocument().deleteString(rangeMarker.getStartOffset(), rangeMarker.getEndOffset());
rangeMarker.dispose();
CreateFromUsageBaseFix.startTemplate(editor, template, project);
}
};
if (startInWriteAction()) {
runnable.run();
} else {
ApplicationManager.getApplication().runWriteAction(runnable);
}
}
@Nullable
protected PsiParameter[] getParams(PsiElement element) {
return new PsiParameter[]{PsiTreeUtil.getParentOfType(element, PsiParameter.class)};
}
@NotNull
@Override
public String getFamilyName() {
return "Generate overloaded method with default parameter value";
}
}
@@ -1,109 +0,0 @@
/*
* Copyright 2000-2009 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.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.BitUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author ven
*/
public class RemoveRedundantElseAction extends PsiElementBaseIntentionAction {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.RemoveRedundantElseAction");
@Override
@NotNull
public String getText() {
return QuickFixBundle.message("remove.redundant.else.fix");
}
@Override
@NotNull
public String getFamilyName() {
return QuickFixBundle.message("remove.redundant.else.fix");
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
if (element instanceof PsiKeyword &&
element.getParent() instanceof PsiIfStatement &&
PsiKeyword.ELSE.equals(element.getText())) {
PsiIfStatement ifStatement = (PsiIfStatement)element.getParent();
if (ifStatement.getElseBranch() == null) return false;
PsiStatement thenBranch = ifStatement.getThenBranch();
if (thenBranch == null) return false;
PsiElement block = PsiTreeUtil.getParentOfType(ifStatement, PsiCodeBlock.class);
if (block != null) {
while (cantCompleteNormally(thenBranch, block)) {
thenBranch = getPrevThenBranch(thenBranch);
if (thenBranch == null) return true;
}
return false;
}
}
return false;
}
@Nullable
private static PsiStatement getPrevThenBranch(@NotNull PsiElement thenBranch) {
final PsiElement ifStatement = thenBranch.getParent();
final PsiElement parent = ifStatement.getParent();
if (parent instanceof PsiIfStatement && ((PsiIfStatement)parent).getElseBranch() == ifStatement) {
return ((PsiIfStatement)parent).getThenBranch();
}
return null;
}
private static boolean cantCompleteNormally(@NotNull PsiStatement thenBranch, PsiElement block) {
try {
ControlFlow controlFlow = ControlFlowFactory.getInstance(thenBranch.getProject()).getControlFlow(block, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance());
int startOffset = controlFlow.getStartOffset(thenBranch);
int endOffset = controlFlow.getEndOffset(thenBranch);
return startOffset != -1 && endOffset != -1 && !BitUtil.isSet(ControlFlowUtil.getCompletionReasons(controlFlow, startOffset, endOffset), ControlFlowUtil.NORMAL_COMPLETION_REASON);
}
catch (AnalysisCanceledException e) {
return false;
}
}
@Override
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return;
PsiIfStatement ifStatement = (PsiIfStatement)element.getParent();
LOG.assertTrue(ifStatement != null && ifStatement.getElseBranch() != null);
PsiStatement elseBranch = ifStatement.getElseBranch();
if (elseBranch instanceof PsiBlockStatement) {
PsiElement[] statements = ((PsiBlockStatement)elseBranch).getCodeBlock().getStatements();
if (statements.length > 0) {
ifStatement.getParent().addRangeAfter(statements[0], statements[statements.length-1], ifStatement);
}
} else {
ifStatement.getParent().addAfter(elseBranch, ifStatement);
}
ifStatement.getElseBranch().delete();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2016 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.
@@ -13,44 +13,43 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.daemon.quickFix;
package com.intellij.codeInspection.lambda;
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
/**
* User: anna
*/
public class RedundantLambdaParameterTypeIntention extends PsiElementBaseIntentionAction {
public static final Logger LOG = Logger.getInstance("#" + RedundantLambdaParameterTypeIntention.class.getName());
public class RedundantLambdaParameterTypeInspection extends BaseJavaBatchLocalInspectionTool {
public static final Logger LOG = Logger.getInstance("#" + RedundantLambdaParameterTypeInspection.class.getName());
@NotNull
@Override
public String getFamilyName() {
return "Remove redundant types";
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
return new JavaElementVisitor() {
@Override
public void visitParameterList(PsiParameterList parameterList) {
super.visitParameterList(parameterList);
if (isApplicable(parameterList)) {
holder.registerProblem(parameterList, "Remove redundant types", new LambdaParametersFix());
}
}
};
}
@NotNull
@Override
public String getText() {
return getFamilyName();
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
final PsiParameterList parameterList = PsiTreeUtil.getParentOfType(element, PsiParameterList.class);
if (parameterList == null) return false;
private static boolean isApplicable(@NotNull PsiParameterList parameterList) {
final PsiElement parent = parameterList.getParent();
if (!(parent instanceof PsiLambdaExpression)) return false;
final PsiLambdaExpression expression = (PsiLambdaExpression)parent;
@@ -73,9 +72,9 @@ public class RedundantLambdaParameterTypeIntention extends PsiElementBaseIntenti
final PsiTypeParameter[] typeParameters = method.getTypeParameters();
final PsiExpression[] arguments = ((PsiExpressionList)lambdaParent).getExpressions();
final JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(project);
final JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(parameterList.getProject());
arguments[idx] = javaPsiFacade.getElementFactory().createExpressionFromText(
"(" + StringUtil.join(expression.getParameterList().getParameters(), parameter -> parameter.getName(), ", ") + ") -> {}", expression);
"(" + StringUtil.join(expression.getParameterList().getParameters(), PsiParameter::getName, ", ") + ") -> {}", expression);
final PsiParameter[] methodParams = method.getParameterList().getParameters();
final PsiSubstitutor substitutor = javaPsiFacade.getResolveHelper()
.inferTypeArguments(typeParameters, methodParams, arguments, ((MethodCandidateInfo)resolveResult).getSiteSubstitutor(),
@@ -85,8 +84,7 @@ public class RedundantLambdaParameterTypeIntention extends PsiElementBaseIntenti
final PsiType psiType = substitutor.substitute(parameter);
if (psiType == null || dependsOnTypeParams(psiType, expression, parameter)) return false;
}
final PsiType paramType;
if (idx < methodParams.length) {
paramType = methodParams[idx].getType();
@@ -107,12 +105,6 @@ public class RedundantLambdaParameterTypeIntention extends PsiElementBaseIntenti
return false;
}
@Override
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(element, PsiLambdaExpression.class);
removeTypes(lambdaExpression);
}
private static void removeTypes(PsiLambdaExpression lambdaExpression) {
if (lambdaExpression != null) {
final PsiParameter[] parameters = lambdaExpression.getParameterList().getParameters();
@@ -121,7 +113,7 @@ public class RedundantLambdaParameterTypeIntention extends PsiElementBaseIntenti
text = parameters[0].getName();
}
else {
text = "(" + StringUtil.join(parameters, parameter -> parameter.getName(), ", ") + ")";
text = "(" + StringUtil.join(parameters, PsiParameter::getName, ", ") + ")";
}
final PsiLambdaExpression expression = (PsiLambdaExpression)JavaPsiFacade.getElementFactory(lambdaExpression.getProject())
.createExpressionFromText(text + "->{}", lambdaExpression);
@@ -135,4 +127,29 @@ public class RedundantLambdaParameterTypeIntention extends PsiElementBaseIntenti
return LambdaUtil.depends(type, new LambdaUtil.TypeParamsChecker(expr, PsiUtil
.resolveGenericsClassInType(LambdaUtil.getFunctionalInterfaceType(expr, false)).getElement()), param2Check);
}
private static class LambdaParametersFix implements LocalQuickFix {
@Nls
@NotNull
@Override
public String getName() {
return getFamilyName();
}
@Nls
@NotNull
@Override
public String getFamilyName() {
return "Remove redundant types";
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiElement element = descriptor.getPsiElement();
final PsiElement parent = element.getParent();
if (parent instanceof PsiLambdaExpression) {
removeTypes((PsiLambdaExpression)parent);
}
}
}
}
@@ -19,13 +19,10 @@ import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressIndicatorProvider;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.VirtualFileWithId;
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree;
import com.intellij.psi.search.DelegatingGlobalSearchScope;
import com.intellij.psi.search.EverythingGlobalScope;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.stubsHierarchy.HierarchyService;
import com.intellij.psi.util.CachedValue;
import com.intellij.psi.util.CachedValueProvider;
@@ -33,17 +30,18 @@ import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.util.CachedValueBase;
import com.intellij.util.indexing.FileBasedIndex;
import com.intellij.util.indexing.FileBasedIndexImpl;
import org.jetbrains.annotations.NotNull;
import java.util.BitSet;
public class HierarchyServiceImpl extends HierarchyService {
private static final SingleClassHierarchy EMPTY_HIERARCHY = new SingleClassHierarchy(Symbol.ClassSymbol.EMPTY_ARRAY);
private final Project myProject;
private final ProjectFileIndex myFileIndex;
private final CachedValue<SingleClassHierarchy> myHierarchy;
public HierarchyServiceImpl(Project project) {
myProject = project;
myFileIndex = ProjectFileIndex.SERVICE.getInstance(project);
myHierarchy = CachedValuesManager.getManager(project).createCachedValue(
() -> CachedValueProvider.Result.create(buildHierarchy(), PsiModificationTracker.MODIFICATION_COUNT),
false);
@@ -69,38 +67,56 @@ public class HierarchyServiceImpl extends HierarchyService {
private SingleClassHierarchy buildHierarchy() {
Symbols symbols = new Symbols();
StubEnter stubEnter = new StubEnter(symbols);
IdSets idSets = IdSets.getIdSets(myProject);
loadUnits(false, symbols.myNameEnvironment, stubEnter);
loadUnits(idSets.libraryFiles, StubHierarchyIndex.BINARY_FILES, symbols.myNameEnvironment, stubEnter);
stubEnter.connect1();
loadUnits(true, symbols.myNameEnvironment, stubEnter);
loadUnits(idSets.sourceFiles, StubHierarchyIndex.SOURCE_FILES, symbols.myNameEnvironment, stubEnter);
stubEnter.connect2();
return symbols.createHierarchy();
}
private void loadUnits(boolean sourceMode, NameEnvironment names, StubEnter stubEnter) {
GlobalSearchScope scope = new DelegatingGlobalSearchScope(new EverythingGlobalScope(myProject)) {
@Override
public boolean contains(@NotNull VirtualFile file) {
return sourceMode ? myFileIndex.isInSourceContent(file) : myFileIndex.isInLibraryClasses(file);
}
};
private void loadUnits(BitSet files, int indexKey, NameEnvironment names, StubEnter stubEnter) {
ProgressIndicator indicator = ProgressIndicatorProvider.getGlobalProgressIndicator();
FileBasedIndex index = FileBasedIndex.getInstance();
for (String packageName : index.getAllKeys(StubHierarchyIndex.INDEX_ID, myProject)) {
QualifiedName pkg = StringUtil.isEmpty(packageName) ? null : names.fromString(packageName, true);
index.processValues(StubHierarchyIndex.INDEX_ID, packageName, null, new FileBasedIndex.ValueProcessor<IndexTree.Unit>() {
int count = 0;
@Override
public boolean process(VirtualFile file, IndexTree.Unit unit) {
if (indicator != null && ++count % 128 == 0) indicator.checkCanceled();
stubEnter.unitEnter(Translator.internNames(names, unit, ((VirtualFileWithId)file).getId(), pkg));
return true;
FileBasedIndexImpl index = (FileBasedIndexImpl)FileBasedIndex.getInstance();
index.processAllValues(StubHierarchyIndex.INDEX_ID, indexKey, myProject, new FileBasedIndexImpl.IdValueProcessor<IndexTree.Unit>() {
int count = 0;
@Override
public boolean process(int fileId, IndexTree.Unit unit) {
if (indicator != null && ++count % 128 == 0) indicator.checkCanceled();
if (files.get(fileId)) {
QualifiedName pkg = unit.myPackageName.length == 0 ? null : names.myNamesEnumerator.getFullName(unit.myPackageName, true);
stubEnter.unitEnter(Translator.internNames(names, unit, fileId, pkg));
}
}, scope);
}
return true;
}
});
}
private static class IdSets {
final BitSet sourceFiles = new BitSet();
final BitSet libraryFiles = new BitSet();
static IdSets getIdSets(@NotNull Project project) {
return CachedValuesManager.getManager(project).getCachedValue(project, () -> {
IdSets answer = new IdSets();
ProjectFileIndex index = ProjectFileIndex.SERVICE.getInstance(project);
FileBasedIndex.getInstance().iterateIndexableFiles(file -> {
if (!file.isDirectory() && file instanceof VirtualFileWithId) {
if (index.isInSourceContent(file)) {
answer.sourceFiles.set(((VirtualFileWithId) file).getId());
}
else if (index.isInLibraryClasses(file)) {
answer.libraryFiles.set(((VirtualFileWithId) file).getId());
}
}
return true;
}, project, ProgressIndicatorProvider.getGlobalProgressIndicator());
return CachedValueProvider.Result.create(answer, ProjectRootManager.getInstance(project), VirtualFileManager.VFS_STRUCTURE_MODIFICATIONS);
});
}
}
}
@@ -18,7 +18,6 @@ package com.intellij.psi.stubsHierarchy.impl;
import com.intellij.ide.highlighter.JavaClassFileType;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiNameHelper;
@@ -39,7 +38,10 @@ import com.intellij.util.indexing.FileContent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class JavaStubIndexer extends StubHierarchyIndexer {
@@ -61,7 +63,7 @@ public class JavaStubIndexer extends StubHierarchyIndexer {
@Nullable
@Override
public List<Pair<String, Unit>> indexFile(@NotNull FileContent content) {
public Unit indexFile(@NotNull FileContent content) {
Stub stubTree = StubTreeBuilder.buildStubTree(content);
if (!(stubTree instanceof PsiJavaFileStub)) return null;
@@ -87,7 +89,7 @@ public class JavaStubIndexer extends StubHierarchyIndexer {
ClassDecl[] classes = classList.isEmpty() ? ClassDecl.EMPTY_ARRAY : classList.toArray(new ClassDecl[classList.size()]);
Import[] imports = importList.isEmpty() ? Import.EMPTY_ARRAY : importList.toArray(new Import[importList.size()]);
byte type = javaFileStub.isCompiled() ? IndexTree.BYTECODE : IndexTree.JAVA;
return Collections.singletonList(Pair.create(javaFileStub.getPackageName(), new Unit(type, imports, classes)));
return new Unit(javaFileStub.getPackageName(), type, imports, classes);
}
@Nullable
@@ -16,14 +16,12 @@
package com.intellij.psi.stubsHierarchy.impl;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
public class NameEnvironment extends UserDataHolderBase {
public final QualifiedName empty;
public final QualifiedName java_lang_Object;
public final QualifiedName java_lang_Enum;
@@ -40,22 +38,7 @@ public class NameEnvironment extends UserDataHolderBase {
@Nullable
public QualifiedName fromString(String s, boolean create) {
List<String> comps = StringUtil.split(s, ".");
int[] ids = new int[comps.size()];
for (int i = 0; i < comps.size(); i++) {
int name = simpleName(comps.get(i), create);
if (name == NamesEnumerator.NO_NAME) {
return null;
}
ids[i] = name;
}
return myNamesEnumerator.getFullName(ids, create);
}
public int simpleName(String s, boolean create) {
if (s == null)
return NamesEnumerator.NO_NAME;
return myNamesEnumerator.getSimpleName(s, create);
return myNamesEnumerator.getFullName(IndexTree.hashQualifiedName(s), create);
}
public QualifiedName prefix(QualifiedName name) {
@@ -15,31 +15,15 @@
*/
package com.intellij.psi.stubsHierarchy.impl;
import com.intellij.util.ArrayUtil;
import gnu.trove.TObjectHashingStrategy;
import gnu.trove.TObjectIntHashMap;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
public class NamesEnumerator {
final static int NO_NAME = 0;
private TObjectIntHashMap<byte[]> myAsciiMap = new TObjectIntHashMap<byte[]>(new TObjectHashingStrategy<byte[]>() {
@Override
public int computeHashCode(byte[] object) {
return Arrays.hashCode(object);
}
@Override
public boolean equals(byte[] o1, byte[] o2) {
return Arrays.equals(o1, o2);
}
});
private TObjectIntHashMap<String> myNonAsciiMap = new TObjectIntHashMap<String>();
TObjectIntHashMap<int[]> fullNameMap = new TObjectIntHashMap<int[]>(new TObjectHashingStrategy<int[]>() {
private final TObjectIntHashMap<int[]> myFullNameMap = new TObjectIntHashMap<int[]>(new TObjectHashingStrategy<int[]>() {
@Override
public int computeHashCode(int[] object) {
return Arrays.hashCode(object);
@@ -50,38 +34,17 @@ public class NamesEnumerator {
return Arrays.equals(o1, o2);
}
});
private QualifiedName[] myQualifiedNames = new QualifiedName[0x8000];
QualifiedName qualifiedName(int id) {
return myQualifiedNames[id];
}
public int getSimpleName(String s, boolean create) {
byte[] bytes = convertToBytesIfAsciiString(s);
if (bytes != null) {
int id = myAsciiMap.get(bytes);
if (id == 0 && create) {
id = myAsciiMap.size() + myNonAsciiMap.size() + 1;
myAsciiMap.put(bytes, id);
}
return id;
}
else {
int id = myNonAsciiMap.get(s);
if (id == 0 && create) {
id = myAsciiMap.size() + myNonAsciiMap.size() + 1;
myNonAsciiMap.put(s, id);
}
return id;
}
}
public QualifiedName getFullName(int[] ids, boolean create) {
int id = fullNameMap.get(ids);
int id = myFullNameMap.get(ids);
if (id == 0 && create) {
id = fullNameMap.size() + 1;
fullNameMap.put(ids, id);
id = myFullNameMap.size() + 1;
myFullNameMap.put(ids, id);
ensureFullCapacity(id);
myQualifiedNames[id] = new QualifiedName(id, ids);
}
@@ -104,20 +67,4 @@ public class NamesEnumerator {
return currentLength;
}
@Nullable
public static byte[] convertToBytesIfAsciiString(CharSequence name) {
int length = name.length();
if (length == 0) return ArrayUtil.EMPTY_BYTE_ARRAY;
byte[] bytes = new byte[length];
for (int i = 0; i < length; i++) {
char c = name.charAt(i);
if (c >= 128) {
return null;
}
bytes[i] = (byte)c;
}
return bytes;
}
}
@@ -88,7 +88,7 @@ public class StubEnter {
supers = myNameEnvironment.annotation;
}
ClassSymbol classSymbol = mySymbols.enterClass(tree.myClassAnchor, flags, tree.myName, owner, info, supers, myStubHierarchyConnector);
ClassSymbol classSymbol = mySymbols.enterClass(tree.myClassAnchor, flags, tree.myName, owner, info, supers);
if (uncompleted != null) {
uncompleted.add(classSymbol);
@@ -100,14 +100,14 @@ public class StubEnter {
public void connect1() {
for (ClassSymbol classSymbol : uncompleted) {
classSymbol.connect();
classSymbol.connect(myStubHierarchyConnector);
}
uncompleted = new ArrayList<ClassSymbol>();
}
public void connect2() {
for (ClassSymbol classSymbol : uncompleted) {
classSymbol.connect();
classSymbol.connect(myStubHierarchyConnector);
}
uncompleted = null;
}
@@ -26,14 +26,14 @@ public class StubHierarchyConnector {
protected StubHierarchyConnector(NameEnvironment nameEnvironment, Symbols symbols) {
this.myNameEnvironment = nameEnvironment;
myResolve = new StubResolver(symbols);
myResolve = new StubResolver(symbols, this);
}
void connect(Symbol sym) {
Symbol.ClassSymbol c = (Symbol.ClassSymbol) sym;
if (c.myOwner instanceof Symbol.ClassSymbol) {
((Symbol.ClassSymbol)c.myOwner).connect();
((Symbol.ClassSymbol)c.myOwner).connect(this);
}
// Determine supertype.
@@ -67,6 +67,7 @@ public class StubHierarchyConnector {
c.mySuperClasses = Symbol.ClassSymbol.EMPTY_ARRAY;
}
else {
//noinspection SuspiciousToArrayCall
c.mySuperClasses = supertypes.toArray(new Symbol.ClassSymbol[supertypes.size()]);
}
}
@@ -18,47 +18,42 @@ package com.intellij.psi.stubsHierarchy.impl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree;
import com.intellij.psi.impl.java.stubs.index.JavaUnitDescriptor;
import com.intellij.psi.stubsHierarchy.StubHierarchyIndexer;
import com.intellij.util.indexing.*;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.util.io.EnumeratorIntegerDescriptor;
import com.intellij.util.io.KeyDescriptor;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.Arrays;
import java.util.Collections;
/**
* @author peter
*/
public class StubHierarchyIndex extends FileBasedIndexExtension<String, IndexTree.Unit> implements PsiDependentIndex {
static final ID<String, IndexTree.Unit> INDEX_ID = ID.create("jvm.hierarchy");
public class StubHierarchyIndex extends FileBasedIndexExtension<Integer, IndexTree.Unit> implements PsiDependentIndex {
public static final int BINARY_FILES = 0;
public static final int SOURCE_FILES = 1;
static final ID<Integer, IndexTree.Unit> INDEX_ID = ID.create("jvm.hierarchy");
private static final StubHierarchyIndexer[] ourIndexers = StubHierarchyIndexer.EP_NAME.getExtensions();
@NotNull
@Override
public ID<String, IndexTree.Unit> getName() {
public ID<Integer, IndexTree.Unit> getName() {
return INDEX_ID;
}
@NotNull
@Override
public DataIndexer<String, IndexTree.Unit, FileContent> getIndexer() {
public DataIndexer<Integer, IndexTree.Unit, FileContent> getIndexer() {
return inputData -> {
for (StubHierarchyIndexer indexer : ourIndexers) {
List<Pair<String, IndexTree.Unit>> pairs = indexer.handlesFile(inputData.getFile()) ? indexer.indexFile(inputData) : null;
if (pairs != null && !pairs.isEmpty()) {
Map<String, IndexTree.Unit> answer = new HashMap<>();
for (Pair<String, IndexTree.Unit> entry : pairs) {
if (entry.second.myDecls.length > 0) {
answer.put(StringUtil.notNullize(entry.first), entry.second);
}
}
return answer;
IndexTree.Unit unit = indexer.handlesFile(inputData.getFile()) ? indexer.indexFile(inputData) : null;
if (unit != null && unit.myDecls.length > 0) {
return Collections.singletonMap(inputData.getFile().getFileType().isBinary() ? BINARY_FILES : SOURCE_FILES, unit);
}
}
return Collections.emptyMap();
@@ -67,8 +62,8 @@ public class StubHierarchyIndex extends FileBasedIndexExtension<String, IndexTre
@NotNull
@Override
public KeyDescriptor<String> getKeyDescriptor() {
return EnumeratorStringDescriptor.INSTANCE;
public KeyDescriptor<Integer> getKeyDescriptor() {
return EnumeratorIntegerDescriptor.INSTANCE;
}
@NotNull
@@ -79,7 +74,7 @@ public class StubHierarchyIndex extends FileBasedIndexExtension<String, IndexTre
@Override
public int getVersion() {
return IndexTree.STUB_HIERARCHY_ENABLED ? 2 + Arrays.stream(ourIndexers).mapToInt(StubHierarchyIndexer::getVersion).sum() : 0;
return IndexTree.STUB_HIERARCHY_ENABLED ? 4 + Arrays.stream(ourIndexers).mapToInt(StubHierarchyIndexer::getVersion).sum() : 0;
}
@NotNull
@@ -28,10 +28,12 @@ import java.util.Set;
public class StubResolver {
private final Symbols mySymbols;
private final NameEnvironment myNameEnvironment;
private final StubHierarchyConnector myConnector;
public StubResolver(Symbols symbols) {
public StubResolver(Symbols symbols, StubHierarchyConnector connector) {
this.mySymbols = symbols;
this.myNameEnvironment = symbols.myNameEnvironment;
myConnector = connector;
}
// resolve class `sym` extends/implements `baseId`
@@ -97,7 +99,7 @@ public class StubResolver {
}
}
private static void findMemberType(Symbol s, int name, Set<Symbol> symbols, Set<Symbol> processed) throws IncompleteHierarchyException {
private void findMemberType(Symbol s, int name, Set<Symbol> symbols, Set<Symbol> processed) throws IncompleteHierarchyException {
if (!processed.add(s)) {
return;
}
@@ -130,9 +132,9 @@ public class StubResolver {
}
}
private static void findInheritedMemberType(Symbol.ClassSymbol c, int name, Set<Symbol> symbols, Set<Symbol> processed)
private void findInheritedMemberType(Symbol.ClassSymbol c, int name, Set<Symbol> symbols, Set<Symbol> processed)
throws IncompleteHierarchyException {
for (Symbol.ClassSymbol st : c.getSuperClasses())
for (Symbol.ClassSymbol st : c.getSuperClasses(myConnector))
findMemberType(st, name, symbols, processed);
}
@@ -194,13 +196,13 @@ public class StubResolver {
}
// handling of import static `tsym.name` as
private static void importNamedStatic(final Symbol.ClassSymbol tsym, final int name, final Set<Symbol> symbols) throws IncompleteHierarchyException {
private void importNamedStatic(final Symbol.ClassSymbol tsym, final int name, final Set<Symbol> symbols) throws IncompleteHierarchyException {
new Object() {
Set<Symbol> processed = new HashSet<Symbol>();
void importFrom(Symbol.ClassSymbol cs) throws IncompleteHierarchyException {
if (cs == null || !processed.add(cs))
return;
for (Symbol.ClassSymbol c : cs.getSuperClasses())
for (Symbol.ClassSymbol c : cs.getSuperClasses(myConnector))
importFrom(c);
importMember(cs.members(), name, symbols, true);
}
@@ -86,28 +86,27 @@ public abstract class Symbol {
/** A class for class symbols
*/
public static class ClassSymbol extends Symbol {
private static final int HIERARCHY_INCOMPLETE = 1 << 20;
private static final int CONNECT_STARTED = 1 << 21;
public static final ClassSymbol[] EMPTY_ARRAY = new ClassSymbol[0];
public final StubClassAnchor myClassAnchor;
public ClassSymbol[] mySuperClasses;
public UnitInfo myUnitInfo;
public QualifiedName[] mySuperNames;
private ClassSymbol[] myMembers;
private StubHierarchyConnector myConnector;
private boolean myHierarchyIncomplete;
public ClassSymbol(StubClassAnchor classAnchor,
final StubClassAnchor myClassAnchor;
ClassSymbol[] mySuperClasses;
UnitInfo myUnitInfo;
QualifiedName[] mySuperNames;
private ClassSymbol[] myMembers;
ClassSymbol(StubClassAnchor classAnchor,
int flags,
Symbol owner,
QualifiedName fullname,
int name,
UnitInfo unitInfo,
QualifiedName[] supers,
StubHierarchyConnector connector) {
QualifiedName[] supers) {
super(flags | IndexTree.CLASS, owner, fullname, name);
this.myClassAnchor = classAnchor;
this.mySuperNames = supers;
this.myUnitInfo = unitInfo;
this.myConnector = connector;
}
@Override
@@ -115,18 +114,21 @@ public abstract class Symbol {
return myClassAnchor.toString();
}
public void connect() {
if (myConnector != null) {
StubHierarchyConnector c = myConnector;
myConnector = null;
c.connect(this);
void connect(StubHierarchyConnector connector) {
if (!isConnectStarted()) {
myFlags = BitUtil.set(myFlags, CONNECT_STARTED, true);
connector.connect(this);
}
}
private boolean isConnectStarted() {
return BitUtil.isSet(myFlags, CONNECT_STARTED);
}
@NotNull
public ClassSymbol[] getSuperClasses() throws IncompleteHierarchyException {
connect();
if (myHierarchyIncomplete) {
ClassSymbol[] getSuperClasses(StubHierarchyConnector connector) throws IncompleteHierarchyException {
connect(connector);
if (isHierarchyIncomplete()) {
throw IncompleteHierarchyException.INSTANCE;
}
return rawSuperClasses();
@@ -134,11 +136,11 @@ public abstract class Symbol {
@NotNull
ClassSymbol[] rawSuperClasses() {
assert myConnector == null;
assert isConnectStarted();
return mySuperClasses == null ? EMPTY_ARRAY : mySuperClasses;
}
public boolean isCompiled() {
boolean isCompiled() {
return BitUtil.isSet(myFlags, IndexTree.COMPILED);
}
@@ -154,11 +156,11 @@ public abstract class Symbol {
mySuperClasses = EMPTY_ARRAY;
mySuperNames = null;
myUnitInfo = null;
myHierarchyIncomplete = true;
myFlags = BitUtil.set(myFlags, HIERARCHY_INCOMPLETE, true);
}
boolean isHierarchyIncomplete() {
return myHierarchyIncomplete;
return BitUtil.isSet(myFlags, HIERARCHY_INCOMPLETE);
}
boolean hasAmbiguousSupers() {
@@ -63,10 +63,10 @@ public class Symbols {
return (ClassSymbol[])cs;
}
public ClassSymbol enterClass(ClassAnchor classAnchor, int flags, int shortName, Symbol owner, UnitInfo info, QualifiedName[] supers, StubHierarchyConnector connector) {
public ClassSymbol enterClass(ClassAnchor classAnchor, int flags, int shortName, Symbol owner, UnitInfo info, QualifiedName[] supers) {
QualifiedName qualifiedName = myNameEnvironment.qualifiedName(owner, shortName);
StubClassAnchor stubClassAnchor = new StubClassAnchor(myClassSymbols.size(), classAnchor);
ClassSymbol c = new ClassSymbol(stubClassAnchor, flags, owner, qualifiedName, shortName, info, supers, connector);
ClassSymbol c = new ClassSymbol(stubClassAnchor, flags, owner, qualifiedName, shortName, info, supers);
myClassSymbols.add(c);
putClassByName(c);
return c;
@@ -16,15 +16,12 @@
package com.intellij.psi.stubsHierarchy.impl;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiNameHelper;
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree;
import com.intellij.psi.stubsHierarchy.stubs.*;
import com.intellij.util.BitUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class Translator {
private static final Key<long[]> DEFAULT_JAVA_IMPORTS_KEY = Key.create("java_imports");
@@ -91,11 +88,10 @@ public class Translator {
}
private static ClassDeclaration processClassDecl(NameEnvironment nameEnvironment, int fileId, IndexTree.ClassDecl def) {
String stubName = def.myName;
int name = stubName == null ? 0 : nameEnvironment.simpleName(stubName, true);
int name = def.myName;
ArrayList<QualifiedName> superList = new ArrayList<QualifiedName>();
for (String aSuper : def.mySupers) {
superList.add(id(nameEnvironment, aSuper));
for (int[] aSuper : def.mySupers) {
superList.add(nameEnvironment.concat(aSuper, true));
}
if (BitUtil.isSet(def.myMods, IndexTree.ENUM)) {
superList.add(nameEnvironment.java_lang_Enum);
@@ -130,21 +126,8 @@ public class Translator {
}
private static long processImport(NameEnvironment nameEnvironment, IndexTree.Import anImport) {
QualifiedName fullname = nameEnvironment.fromString(anImport.myFullname, true);
int aliasName = anImport.myAlias == null ? 0 : nameEnvironment.simpleName(anImport.myAlias, true);
return Import.mkImport(fullname, anImport.myStaticImport, anImport.myOnDemand, aliasName);
QualifiedName fullname = nameEnvironment.myNamesEnumerator.getFullName(anImport.myFullname, true);
return Import.mkImport(fullname, anImport.myStaticImport, anImport.myOnDemand, anImport.myAlias);
}
private static QualifiedName id(NameEnvironment nameEnvironment, String s) {
s = PsiNameHelper.getQualifiedClassName(s, true);
List<String> ids = StringUtil.split(s, ".");
int[] comps = new int[ids.size()];
int i = 0;
for (String id : ids) {
int name = nameEnvironment.simpleName(id, true);
comps[i] = name;
i++;
}
return nameEnvironment.concat(comps, true);
}
}
@@ -63,7 +63,7 @@ public class ExtractClassHandler implements ElementsHandler {
if (cannotRefactorMessage != null) {
CommonRefactoringUtil.showErrorHint(project, editor,
RefactorJBundle.message("cannot.perform.the.refactoring") + cannotRefactorMessage,
null, getHelpID());
ExtractClassProcessor.REFACTORING_NAME, getHelpID());
return;
}
new ExtractClassDialog(containingClass, selectedMember).show();
@@ -66,6 +66,7 @@ import java.util.*;
public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
private static final Logger logger = Logger.getInstance("com.siyeh.rpp.extractclass.ExtractClassProcessor");
@NonNls public static final String REFACTORING_NAME = "Extract Delegate";
private final PsiClass sourceClass;
private final List<PsiField> fields;
@@ -16,6 +16,7 @@
package com.intellij.psi.impl.java.stubs.index;
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree;
import com.intellij.util.BitUtil;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.DataInputOutputUtil;
import org.jetbrains.annotations.NotNull;
@@ -24,18 +25,34 @@ import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import static com.intellij.psi.impl.java.stubs.index.JavaUnitDescriptor.ImportFlags.*;
public class JavaUnitDescriptor implements DataExternalizer<IndexTree.Unit> {
public static final JavaUnitDescriptor INSTANCE = new JavaUnitDescriptor();
private static void writeIntArray(DataOutput out, int[] array) throws IOException {
DataInputOutputUtil.writeINT(out, array.length);
for (int i : array) {
out.writeInt(i);
}
}
private static int[] readIntArray(DataInput in) throws IOException {
int length = DataInputOutputUtil.readINT(in);
int[] result = new int[length];
for (int i = 0; i < length; i++) {
result[i] = in.readInt();
}
return result;
}
@Override
public void save(@NotNull DataOutput out, IndexTree.Unit value) throws IOException {
writeIntArray(out, value.myPackageName);
out.writeByte(value.myUnitType);
if (value.myUnitType != IndexTree.BYTECODE) {
DataInputOutputUtil.writeINT(out, value.imports.length);
for (IndexTree.Import anImport : value.imports) {
out.writeUTF(anImport.myFullname);
out.writeBoolean(anImport.myStaticImport);
out.writeBoolean(anImport.myOnDemand);
writeImport(out, anImport);
}
}
// class Declaration
@@ -46,12 +63,12 @@ public class JavaUnitDescriptor implements DataExternalizer<IndexTree.Unit> {
}
private void saveClassDecl(@NotNull DataOutput out, IndexTree.ClassDecl value) throws IOException {
out.writeInt(value.myStubId);
DataInputOutputUtil.writeINT(out, value.myStubId);
DataInputOutputUtil.writeINT(out, value.myMods);
out.writeUTF(value.myName == null ? "" : value.myName);
out.writeInt(value.myName);
DataInputOutputUtil.writeINT(out, value.mySupers.length);
for (String aSuper : value.mySupers) {
out.writeUTF(aSuper);
for (int[] aSuper : value.mySupers) {
writeIntArray(out, aSuper);
}
DataInputOutputUtil.writeINT(out, value.myDecls.length);
for (IndexTree.Decl def : value.myDecls) {
@@ -75,31 +92,29 @@ public class JavaUnitDescriptor implements DataExternalizer<IndexTree.Unit> {
@Override
public IndexTree.Unit read(@NotNull DataInput in) throws IOException {
int[] pid = readIntArray(in);
byte type = in.readByte();
IndexTree.Import[] imports = IndexTree.Import.EMPTY_ARRAY;
if (type != IndexTree.BYTECODE) {
imports = new IndexTree.Import[DataInputOutputUtil.readINT(in)];
for (int i = 0; i < imports.length; i++) {
imports[i] = new IndexTree.Import(in.readUTF(), in.readBoolean(), in.readBoolean(), null);
imports[i] = readImport(in);
}
}
IndexTree.ClassDecl[] classes = new IndexTree.ClassDecl[DataInputOutputUtil.readINT(in)];
for (int i = 0; i < classes.length; i++) {
classes[i] = readClassDecl(in);
}
return new IndexTree.Unit(type, imports, classes);
return new IndexTree.Unit(pid, type, imports, classes);
}
private IndexTree.ClassDecl readClassDecl(DataInput in) throws IOException {
int stubId = in.readInt();
int stubId = DataInputOutputUtil.readINT(in);
int mods = DataInputOutputUtil.readINT(in);
String name = in.readUTF();
if (name.isEmpty()) {
name = null;
}
String[] supers = new String[DataInputOutputUtil.readINT(in)];
int name = in.readInt();
int[][] supers = new int[DataInputOutputUtil.readINT(in)][];
for (int i = 0; i < supers.length; i++) {
supers[i] = in.readUTF();
supers[i] = readIntArray(in);
}
IndexTree.Decl[] decls = new IndexTree.Decl[DataInputOutputUtil.readINT(in)];
for (int i = 0; i < decls.length; i++) {
@@ -122,4 +137,32 @@ public class JavaUnitDescriptor implements DataExternalizer<IndexTree.Unit> {
}
}
interface ImportFlags {
int IS_STATIC = 1;
int IS_ON_DEMAND = 2;
int HAS_ALIAS = 4;
}
private static void writeImport(@NotNull DataOutput out, IndexTree.Import anImport) throws IOException {
writeIntArray(out, anImport.myFullname);
boolean hasAlias = anImport.myAlias != 0;
int flags = 0;
flags = BitUtil.set(flags, IS_STATIC, anImport.myStaticImport);
flags = BitUtil.set(flags, IS_ON_DEMAND, anImport.myOnDemand);
flags = BitUtil.set(flags, HAS_ALIAS, hasAlias);
out.writeByte(flags);
if (hasAlias) {
out.writeInt(anImport.myAlias);
}
}
@NotNull
private static IndexTree.Import readImport(@NotNull DataInput in) throws IOException {
int[] fullname = readIntArray(in);
int flags = in.readByte();
return new IndexTree.Import(fullname,
BitUtil.isSet(flags, IS_STATIC), BitUtil.isSet(flags, IS_ON_DEMAND),
BitUtil.isSet(flags, HAS_ALIAS) ? in.readInt() : 0);
}
}
@@ -16,15 +16,12 @@
package com.intellij.psi.stubsHierarchy;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree;
import com.intellij.util.indexing.FileContent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author peter
*/
@@ -37,7 +34,7 @@ public abstract class StubHierarchyIndexer {
* @return a list of pairs <packageName, compilation unit> for a specified file content
*/
@Nullable
public abstract List<Pair<String, IndexTree.Unit>> indexFile(@NotNull FileContent content);
public abstract IndexTree.Unit indexFile(@NotNull FileContent content);
public abstract boolean handlesFile(@NotNull VirtualFile file);
@@ -15,11 +15,15 @@
*/
package com.intellij.psi.util;
import com.intellij.openapi.roots.FileIndexFacade;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.containers.HashSet;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.LinkedHashSet;
import java.util.List;
@@ -111,6 +115,7 @@ public class PsiSuperMethodUtil {
public static Map<MethodSignature, Set<PsiMethod>> collectOverrideEquivalents(@NotNull PsiClass aClass) {
final Map<MethodSignature, Set<PsiMethod>> overrideEquivalent =
new THashMap<MethodSignature, Set<PsiMethod>>(MethodSignatureUtil.METHOD_PARAMETERS_ERASURE_EQUALITY);
final GlobalSearchScope resolveScope = aClass.getResolveScope();
PsiClass[] supers = aClass.getSupers();
for (int i = 0; i < supers.length; i++) {
PsiClass superClass = supers[i];
@@ -122,10 +127,12 @@ public class PsiSuperMethodUtil {
if (subType) continue;
final PsiSubstitutor superClassSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(superClass, aClass, PsiSubstitutor.EMPTY);
for (HierarchicalMethodSignature hms : superClass.getVisibleSignatures()) {
final PsiMethod method = hms.getMethod();
PsiMethod method = hms.getMethod();
if (MethodSignatureUtil.findMethodBySignature(aClass, method.getSignature(superClassSubstitutor), false) != null) continue;
final PsiClass containingClass = method.getContainingClass();
final PsiClass containingClass = correctClassByScope(method.getContainingClass(), resolveScope);
if (containingClass == null) continue;
method = containingClass.findMethodBySignature(method, false);
if (method == null) continue;
final PsiSubstitutor containingClassSubstitutor = TypeConversionUtil.getClassSubstitutor(containingClass, aClass, PsiSubstitutor.EMPTY);
if (containingClassSubstitutor == null) continue;
final PsiSubstitutor finalSubstitutor =
@@ -141,4 +148,30 @@ public class PsiSuperMethodUtil {
}
return overrideEquivalent;
}
@Nullable
public static PsiClass correctClassByScope(PsiClass psiClass, final GlobalSearchScope resolveScope) {
if (psiClass == null) return null;
String qualifiedName = psiClass.getQualifiedName();
if (qualifiedName == null) {
return psiClass;
}
PsiFile file = psiClass.getContainingFile();
if (file == null || !file.getViewProvider().isPhysical()) {
return psiClass;
}
final VirtualFile vFile = file.getVirtualFile();
if (vFile == null) {
return psiClass;
}
final FileIndexFacade index = FileIndexFacade.getInstance(file.getProject());
if (!index.isInSource(vFile) && !index.isInLibrarySource(vFile) && !index.isInLibraryClasses(vFile)) {
return psiClass;
}
return JavaPsiFacade.getInstance(psiClass.getProject()).findClass(qualifiedName, resolveScope);
}
}
@@ -15,11 +15,10 @@
*/
package com.intellij.psi.impl;
import com.intellij.openapi.roots.FileIndexFacade;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiSuperMethodUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
@@ -56,7 +55,7 @@ class TypeCorrector extends PsiTypeMapper {
final PsiClassType.ClassResolveResult classResolveResult = classType.resolveGenerics();
final PsiClass psiClass = classResolveResult.getElement();
if (psiClass != null && classResolveResult.getSubstitutor() == PsiSubstitutor.EMPTY) {
final PsiClass mappedClass = mapClass(psiClass);
final PsiClass mappedClass = PsiSuperMethodUtil.correctClassByScope(psiClass, myResolveScope);
if (mappedClass == null || mappedClass == psiClass) return (T) classType;
}
}
@@ -79,7 +78,7 @@ class TypeCorrector extends PsiTypeMapper {
PsiUtilCore.ensureValid(psiClass);
final PsiClass mappedClass = mapClass(psiClass);
final PsiClass mappedClass = PsiSuperMethodUtil.correctClassByScope(psiClass, myResolveScope);
if (mappedClass == null) return classType;
PsiClassType mappedType = new PsiCorrectedClassType(classType.getLanguageLevel(),
@@ -89,31 +88,6 @@ class TypeCorrector extends PsiTypeMapper {
return mappedType;
}
@Nullable
private PsiClass mapClass(@NotNull PsiClass psiClass) {
String qualifiedName = psiClass.getQualifiedName();
if (qualifiedName == null) {
return psiClass;
}
PsiFile file = psiClass.getContainingFile();
if (file == null || !file.getViewProvider().isPhysical()) {
return psiClass;
}
final VirtualFile vFile = file.getVirtualFile();
if (vFile == null) {
return psiClass;
}
final FileIndexFacade index = FileIndexFacade.getInstance(file.getProject());
if (!index.isInSource(vFile) && !index.isInLibrarySource(vFile) && !index.isInLibraryClasses(vFile)) {
return psiClass;
}
return JavaPsiFacade.getInstance(psiClass.getProject()).findClass(qualifiedName, myResolveScope);
}
@NotNull
private PsiSubstitutor mapSubstitutor(PsiClass originalClass, PsiClass mappedClass, PsiSubstitutor substitutor) {
PsiTypeParameter[] typeParameters = mappedClass.getTypeParameters();
@@ -17,12 +17,19 @@ package com.intellij.psi.impl.java.stubs.hierarchy;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiNameHelper;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
public class IndexTree {
public static final boolean STUB_HIERARCHY_ENABLED = Registry.is("java.hierarchy.service");
@SuppressWarnings("PointlessBitwiseExpression")
public final static int PACKAGE = 1 << 0;
public final static int CLASS = 1 << 1;
public static final int STATIC = 1 << 3;
@@ -36,15 +43,52 @@ public class IndexTree {
public static final byte JAVA = 1;
public static final byte GROOVY = 2;
private static int hashIdentifier(@Nullable String s) {
if (s == null) return 0;
// not using String.hashCode because this way there's less collisions for short package names like 'com'
int hash = 0;
for (int i = 0; i < s.length(); i++) {
hash = hash * 239 + s.charAt(i);
}
return hash;
}
public static int[] hashQualifiedName(@NotNull String qName) {
qName = PsiNameHelper.getQualifiedClassName(qName, true);
if (qName.isEmpty()) return ArrayUtil.EMPTY_INT_ARRAY;
List<String> components = StringUtil.split(qName, ".");
int[] result = new int[components.size()];
for (int i = 0; i < components.size(); i++) {
result[i] = hashIdentifier(components.get(i));
}
return result;
}
private static int[][] hashQualifiedNameArray(String[] supers) {
int[][] superHashes = new int[supers.length][];
for (int i = 0; i < supers.length; i++) {
superHashes[i] = hashQualifiedName(supers[i]);
}
return superHashes;
}
public static class Unit {
@NotNull public final int[] myPackageName;
public final byte myUnitType;
public final Import[] imports;
public final ClassDecl[] myDecls;
public Unit(byte unitType, Import[] imports, ClassDecl[] decls) {
this.myUnitType = unitType;
public Unit(@Nullable String packageName, byte unitType, Import[] imports, ClassDecl[] decls) {
this(hashQualifiedName(StringUtil.notNullize(packageName)), unitType, imports, decls);
}
public Unit(@NotNull int[] packageName, byte unitType, Import[] imports, ClassDecl[] decls) {
myPackageName = packageName;
myUnitType = unitType;
this.imports = imports;
this.myDecls = decls;
myDecls = decls;
}
@Override
@@ -54,6 +98,8 @@ public class IndexTree {
Unit unit = (Unit)o;
if (myUnitType != unit.myUnitType) return false;
if (!Arrays.equals(myPackageName, unit.myPackageName)) return false;
if (!Arrays.equals(imports, unit.imports)) return false;
if (!Arrays.equals(myDecls, unit.myDecls)) return false;
@@ -62,22 +108,33 @@ public class IndexTree {
@Override
public int hashCode() {
return Arrays.hashCode(myDecls);
int hash = myUnitType * 31 + Arrays.hashCode(myPackageName);
for (ClassDecl decl : myDecls) {
int name = decl.myName;
if (name != 0) {
return hash * 31 + name;
}
}
return hash;
}
}
public static class Import {
public static final Import[] EMPTY_ARRAY = new Import[0];
public final String myFullname;
public final int[] myFullname;
public final boolean myStaticImport;
public final boolean myOnDemand;
public final String myAlias;
public final int myAlias;
public Import(String fullname, boolean staticImport, boolean onDemand, String alias) {
this.myFullname = fullname;
this.myStaticImport = staticImport;
this.myOnDemand = onDemand;
this.myAlias = alias;
public Import(String fullname, boolean staticImport, boolean onDemand, @Nullable String alias) {
this(hashQualifiedName(fullname), staticImport, onDemand, hashIdentifier(alias));
}
public Import(int[] fullname, boolean staticImport, boolean onDemand, int alias) {
myFullname = fullname;
myStaticImport = staticImport;
myOnDemand = onDemand;
myAlias = alias;
}
@Override
@@ -89,18 +146,18 @@ public class IndexTree {
if (myStaticImport != anImport.myStaticImport) return false;
if (myOnDemand != anImport.myOnDemand) return false;
if (!myFullname.equals(anImport.myFullname)) return false;
if (myAlias != null ? !myAlias.equals(anImport.myAlias) : anImport.myAlias != null) return false;
if (!Arrays.equals(myFullname, anImport.myFullname)) return false;
if (myAlias != anImport.myAlias) return false;
return true;
}
@Override
public int hashCode() {
int result = myFullname.hashCode();
int result = Arrays.hashCode(myFullname);
result = 31 * result + (myStaticImport ? 1 : 0);
result = 31 * result + (myOnDemand ? 1 : 0);
result = 31 * result + (myAlias != null ? myAlias.hashCode() : 0);
result = 31 * result + myAlias;
return result;
}
}
@@ -118,16 +175,20 @@ public class IndexTree {
public static final ClassDecl[] EMPTY_ARRAY = new ClassDecl[0];
public final int myStubId;
public final int myMods;
public final String myName;
public final String[] mySupers;
public final int myName;
public final int[][] mySupers;
public ClassDecl(int stubId, int mods, String name, String[] supers, Decl[] decls) {
public ClassDecl(int stubId, int mods, @Nullable String name, String[] supers, Decl[] decls) {
this(stubId, mods, hashIdentifier(name), hashQualifiedNameArray(supers), decls);
}
public ClassDecl(int stubId, int mods, int name, int[][] supers, Decl[] decls) {
super(decls);
assert stubId > 0;
this.myStubId = stubId;
this.myMods = mods;
this.myName = name;
this.mySupers = supers;
myStubId = stubId;
myMods = mods;
myName = name;
mySupers = supers;
}
@Override
@@ -138,8 +199,8 @@ public class IndexTree {
ClassDecl classDecl = (ClassDecl)o;
if (myStubId != classDecl.myStubId) return false;
if (myMods != classDecl.myMods) return false;
if (myName != null ? !myName.equals(classDecl.myName) : classDecl.myName != null) return false;
if (!Arrays.equals(mySupers, classDecl.mySupers)) return false;
if (myName != classDecl.myName) return false;
if (!Arrays.deepEquals(mySupers, classDecl.mySupers)) return false;
if (!Arrays.equals(myDecls, classDecl.myDecls)) return false;
return true;
}
@@ -148,7 +209,7 @@ public class IndexTree {
public int hashCode() {
int result = myStubId;
result = 31 * result + myMods;
result = 31 * result + (myName != null ? myName.hashCode() : 0);
result = 31 * result + myName;
return result;
}
}
@@ -237,9 +237,7 @@ public class PsiMethodCallExpressionImpl extends ExpressionPsiElement implements
PsiType ret,
JavaResolveResult result,
LanguageLevel languageLevel) {
PsiSubstitutor substitutor = result instanceof MethodCandidateInfo && !PsiPolyExpressionUtil.isMethodCallTypeDependsOnInference(call, method)
? ((MethodCandidateInfo)result).getSiteSubstitutor()
: result.getSubstitutor();
PsiSubstitutor substitutor = result.getSubstitutor();
PsiType substitutedReturnType = substitutor.substitute(ret);
if (substitutedReturnType == null) {
return TypeConversionUtil.erasure(ret);
@@ -1,4 +1,4 @@
// "Generate overloaded method with default parameter value" "true"
// "Generate overloaded method with default parameter values" "true"
class Test {
void foo() {
foo(<caret>);
@@ -1,4 +1,4 @@
// "Generate overloaded method with default parameter value" "true"
// "Generate overloaded method with default parameter values" "true"
abstract class Test {
int foo(boolean... args) {
return foo(<caret>, args);
@@ -1,4 +1,4 @@
// "Generate overloaded method with default parameter value" "true"
// "Generate overloaded method with default parameter values" "true"
class Test {
int foo() {
return foo();
@@ -1,4 +1,4 @@
// "Generate overloaded constructor with default parameter value" "true"
// "Generate overloaded constructor with default parameter values" "true"
class Test {
Test() {
this(<caret>);
@@ -0,0 +1,6 @@
// "Generate overloaded method with default parameter values" "true"
class Test {
void foo(){}
void foo(int ii){
}
}
@@ -1,4 +1,4 @@
// "Generate overloaded method with default parameter value" "true"
// "Generate overloaded method with default parameter values" "true"
interface Test {
default void foo() {
foo();
@@ -1,4 +1,4 @@
// "Generate overloaded method with default parameter value" "true"
// "Generate overloaded method with default parameter values" "true"
class Test {
/**
*/
@@ -1,4 +1,4 @@
// "Generate overloaded method with default parameter value" "true"
// "Generate overloaded method with default parameter values" "true"
class Test {
int foo() {
return foo(<caret>);
@@ -1,4 +1,4 @@
// "Generate overloaded method with default parameter value" "true"
// "Generate overloaded method with default parameter values" "true"
interface Test {
static void foo() {
foo();
@@ -1,4 +1,4 @@
// "Generate overloaded method with default parameter value" "true"
// "Generate overloaded method with default parameter values" "true"
class Test {
<T> int foo(boolean... args) {
return foo(<caret>, args);
@@ -1,4 +1,4 @@
// "Generate overloaded method with default parameter value" "true"
// "Generate overloaded method with default parameter values" "true"
class Test {
int foo(boolean... args) {
return foo(<caret>, args);
@@ -1,4 +1,4 @@
// "Generate overloaded method with default parameter value" "true"
// "Generate overloaded method with default parameter values" "true"
class Test {
void foo(int i<caret>i){
}
@@ -1,4 +1,4 @@
// "Generate overloaded method with default parameter value" "true"
// "Generate overloaded method with default parameter values" "true"
abstract class Test {
abstract int foo(int i<caret>i, boolean... args);
}
@@ -1,4 +1,4 @@
// "Generate overloaded method with default parameter value" "true"
// "Generate overloaded method with default parameter values" "true"
class Test {
int foo(int i<caret>i){
//comment1
@@ -1,4 +1,4 @@
// "Generate overloaded constructor with default parameter value" "true"
// "Generate overloaded constructor with default parameter values" "true"
class Test {
Test(int i<caret>i){}
}
@@ -1,4 +1,4 @@
// "Generate overloaded method with default parameter value" "false"
// "Generate overloaded method with default parameter values" "true"
class Test {
void foo(){}
void foo(int i<caret>i){
@@ -1,4 +1,4 @@
// "Generate overloaded method with default parameter value" "true"
// "Generate overloaded method with default parameter values" "true"
interface Test {
void foo(int i<caret>i);
}
@@ -1,4 +1,4 @@
// "Generate overloaded method with default parameter value" "true"
// "Generate overloaded method with default parameter values" "true"
class Test {
/**
* @param i
@@ -1,4 +1,4 @@
// "Generate overloaded method with default parameter value" "true"
// "Generate overloaded method with default parameter values" "true"
class Test {
int foo(int i<caret>i){
return 1;
@@ -1,4 +1,4 @@
// "Generate overloaded method with default parameter value" "true"
// "Generate overloaded method with default parameter values" "true"
interface Test {
static void foo(int i<caret>i) {}
}
@@ -1,4 +1,4 @@
// "Generate overloaded method with default parameter value" "true"
// "Generate overloaded method with default parameter values" "true"
class Test {
<T> int foo(T i<caret>i, boolean... args){
return 1;
@@ -1,4 +1,4 @@
// "Generate overloaded method with default parameter value" "true"
// "Generate overloaded method with default parameter values" "true"
class Test {
int foo(int i<caret>i, boolean... args){
return 1;
@@ -1,13 +0,0 @@
// "Remove redundant 'else'" "true"
class a {
void foo() {
int a = 0;
int b = 0;
if (a != b) {
return;
}
a = b;
a++;
}
}
@@ -1,14 +0,0 @@
// "Remove redundant 'else'" "true"
class a {
void foo() {
int a = 0;
int b = 0;
if (a != b) {
return;
} e<caret>lse {
a = b;
}
a++;
}
}
@@ -1,15 +0,0 @@
// "Remove redundant 'else'" "false"
import java.io.IOException;
class a {
void foo(boolean condition) throws IOException{
if (condition) {
tMethod();
}
e<caret>lse {
System.out.println("else");
}
}
void tMethod() throws IOException {}
}
@@ -1,17 +0,0 @@
// "Remove redundant 'else'" "false"
class a {
void foo() {
int a = 0;
int b = 0;
if (a != b) {
a = 10;
} else if (a + 1 == b) {
return;
}
e<caret>lse {
a = b;
}
a++;
}
}
@@ -0,0 +1,4 @@
package p;
interface I {
default void foo();
}
@@ -0,0 +1,3 @@
package p;
public abstract class A implements I {}
@@ -0,0 +1,4 @@
package p;
interface I {
default void foo();
}
@@ -0,0 +1,8 @@
package p;
class EmptyValueIterator extends A implements ValueIterator {
}
interface ValueIterator extends I {}
@@ -0,0 +1,7 @@
import java.util.stream.Stream;
class InChain {
public static void main(String[] args) {
Stream.of("a").map((String<caret> s) -> s + "1").forEach(System.out::println);
}
}
@@ -0,0 +1,7 @@
import java.util.stream.Stream;
class InChain {
public static void main(String[] args) {
Stream.of("a").map(s -> s + "1").forEach(System.out::println);
}
}
@@ -0,0 +1,13 @@
class C {
{
@B @A int i = 0;
System.out.println(i);
}
void f() {
<selection>@A @B int j = 0;
System.out.println(j);</selection>
}
}
@interface A {}
@interface B {}
@@ -0,0 +1,16 @@
class C {
{
newMethod();
}
void f() {
newMethod();
}
private void newMethod() {
@A @B int j = 0;
System.out.println(j);
}
}
@interface A {}
@interface B {}
@@ -0,0 +1,11 @@
class C {
{
int i = 0;
System.out.println(i);
}
void f() {
<selection>final int j = 0;
System.out.println(j);</selection>
}
}
@@ -0,0 +1,14 @@
class C {
{
newMethod();
}
void f() {
newMethod();
}
private void newMethod() {
final int j = 0;
System.out.println(j);
}
}
@@ -0,0 +1,21 @@
class Test10 {
void test() {
<selection>new Super() {
int get() {
return 0;
}
};</selection>
new Super() {
@Override
int get() {
return 0;
}
};
}
}
class Super {
int get() {
return 1;
}
}
@@ -0,0 +1,20 @@
class Test10 {
void test() {
newMethod();
newMethod();
}
private void newMethod() {
new Super() {
int get() {
return 0;
}
};
}
}
class Super {
int get() {
return 1;
}
}
@@ -18,7 +18,6 @@ package com.intellij.codeInsight;
import com.intellij.openapi.application.ex.PathManagerEx;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleRootModificationUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.pom.java.LanguageLevel;
@@ -26,7 +25,6 @@ import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.UsefulTestCase;
import com.intellij.testFramework.builders.JavaModuleFixtureBuilder;
import com.intellij.testFramework.fixtures.*;
import com.intellij.util.Consumer;
public class MultipleJdksHighlightingTest extends UsefulTestCase {
@@ -197,6 +195,16 @@ public class MultipleJdksHighlightingTest extends UsefulTestCase {
myFixture.checkHighlighting();
}
public void testUnrelatedDefaultsFromDifferentJdkVersions() throws Exception {
ModuleRootModificationUtil.addDependency(myJava8Module, myJava7Module);
myFixture.copyFileToProject("java7/p/I.java");
myFixture.copyFileToProject("java8/p/I.java");
final String testName = getTestName(false);
myFixture.configureByFiles("java8/p/" + testName + ".java", "java7/p/" + testName + ".java");
myFixture.checkHighlighting();
}
private void doTestWithoutLibrary() {
final String name = getTestName(false);
myFixture.configureByFiles("java7/p/" + name + ".java", "java8/p/" + name + ".java");
@@ -30,8 +30,9 @@ public class DelegateWithDefaultParamValueTest extends LightQuickFixParameterize
if (actionShouldBeAvailable) {
TemplateState state = TemplateManagerImpl.getTemplateState(getEditor());
assert state != null;
state.gotoEnd(false);
if (state != null) {
state.gotoEnd(false);
}
}
}
@@ -1,33 +0,0 @@
/*
* Copyright 2000-2012 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.codeInsight.daemon.quickFix;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.testFramework.IdeaTestUtil;
public class RedundantLambdaParameterTypeIntentionTest extends LightQuickFixParameterizedTestCase {
public void test() throws Exception { doAllTests(); }
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/redundantLambdaParameterType";
}
@Override
protected Sdk getProjectJDK() {
return IdeaTestUtil.getMockJdk18();
}
}
@@ -0,0 +1,112 @@
/*
* Copyright 2000-2016 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:public void testwww() {}.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.codeInspection;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.lambda.RedundantLambdaParameterTypeInspection;
import com.intellij.idea.Bombed;
import com.intellij.openapi.roots.ModuleRootModificationUtil;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import java.util.Calendar;
import java.util.List;
public class RedundantLambdaParameterTypeInspectionTest extends LightCodeInsightFixtureTestCase {
private RedundantLambdaParameterTypeInspection myInspection = new RedundantLambdaParameterTypeInspection();
private String myIntentionName = "Remove redundant types";
@Override
protected String getBasePath() {
return JavaTestUtil.getRelativeJavaTestDataPath() + "/codeInspection/redundantLambdaParameterType";
}
@Override
protected void setUp() throws Exception {
super.setUp();
ModuleRootModificationUtil.setModuleSdk(myModule, IdeaTestUtil.getMockJdk18());
myFixture.enableInspections(myInspection);
}
@Override
protected void tearDown() throws Exception {
try {
myFixture.disableInspections(myInspection);
}
finally {
super.tearDown();
}
}
public void testAssignment() {
doTest();
}
public void testAssignmentNoParams() {
assertIntentionNotAvailable();
}
public void testAssignmentNoTypes() {
assertIntentionNotAvailable();
}
public void testAtVarargPlace() {
assertIntentionNotAvailable();
}
public void testCallNoTypeArgs() {
assertIntentionNotAvailable();
}
public void testCallNoTypeArgs1() {
assertIntentionNotAvailable();
}
public void testCallWithTypeArgs() {
doTest();
}
public void testInferredFromOtherArgs() {
doTest();
}
public void testNoSelfTypeParam() {
doTest();
}
public void testTypeParam() {
assertIntentionNotAvailable();
}
@Bombed(month = Calendar.AUGUST, day = 1, user = "Pavel Dolgov")
public void testInChain() {
doTest();
}
private void doTest() {
myFixture.configureByFiles(getTestName(false) + ".java");
final IntentionAction singleIntention = myFixture.findSingleIntention(myIntentionName);
myFixture.launchAction(singleIntention);
myFixture.checkResultByFile(getTestName(false) + ".java", getTestName(false) + "_after.java", true);
}
private void assertIntentionNotAvailable() {
myFixture.configureByFiles(getTestName(false) + ".java");
final List<IntentionAction> intentionActions = myFixture.filterAvailableIntentions(myIntentionName);
assertEmpty(myIntentionName + " is not expected", intentionActions);
}
}
@@ -58,10 +58,7 @@ import org.junit.Assert;
import java.io.IOException;
import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
@PlatformTestCase.WrapInCommand
@@ -915,4 +912,20 @@ public class SmartPsiElementPointersTest extends CodeInsightTestCase {
assertEquals(psiClass.getNameIdentifier().getTextRange(), TextRange.create(range));
}
public void testManySmartPointersCreationDeletionPerformance() throws Exception {
String text = StringUtil.repeatSymbol(' ', 100000);
PsiFile file = createFile("a.txt", text);
PlatformTestUtil.startPerformanceTest("", 2000, () -> {
List<SmartPsiFileRange> pointers = new ArrayList<>();
for (int i = 0; i < text.length() - 1; i++) {
pointers.add(getPointerManager().createSmartPsiFileRangePointer(file, new TextRange(i, i + 1)));
}
Collections.shuffle(pointers);
for (SmartPsiFileRange pointer : pointers) {
getPointerManager().removePointer(pointer);
}
}).cpuBound().assertTiming();
}
}
@@ -805,6 +805,18 @@ public class ExtractMethodTest extends LightCodeInsightTestCase {
doTest();
}
public void testLocalVariableModifierList() throws Exception {
doTest();
}
public void testLocalVariableAnnotationsOrder() throws Exception {
doTest();
}
public void testMethodAnnotations() throws Exception {
doTest();
}
public void testQualifyWhenConflictingNamePresent() throws Exception {
final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject());
settings.ELSE_ON_NEW_LINE = true;
@@ -19,17 +19,13 @@
*/
package com.intellij.psi.tree;
import com.intellij.lang.ASTNode;
import com.intellij.lang.Language;
import com.intellij.util.CharTable;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class CustomParsingType extends IElementType {
public abstract class CustomParsingType extends IElementType implements ICustomParsingType {
public CustomParsingType(@NotNull @NonNls String debugName, @Nullable Language language) {
super(debugName, language);
}
public abstract ASTNode parse(CharSequence text, CharTable table);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
* Copyright 2000-2016 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.
@@ -13,20 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.daemon.quickFix;
package com.intellij.psi.tree;
import com.intellij.lang.ASTNode;
import com.intellij.util.CharTable;
import org.jetbrains.annotations.NotNull;
/**
* User: anna
* Date: Aug 30, 2010
* A token which is more convenient to parse separately. Parsing is done when leaf elements are created.
*/
public class RemoveRedundantElseActionTest extends LightQuickFixParameterizedTestCase {
public void test() throws Exception { doAllTests(); }
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/removeRedundantElse";
}
public interface ICustomParsingType {
@NotNull
ASTNode parse(@NotNull CharSequence text, @NotNull CharTable table);
}
@@ -1562,7 +1562,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
}
if (oldNode.getElementType() instanceof ILazyParseableElementType && type instanceof ILazyParseableElementType ||
oldNode.getElementType() instanceof CustomParsingType && type instanceof CustomParsingType) {
oldNode.getElementType() instanceof ICustomParsingType && type instanceof ICustomParsingType) {
return ((TreeElement)oldNode).textMatches(token.getText())
? ThreeState.YES
: ThreeState.NO;
@@ -1853,8 +1853,8 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
return new PsiWhiteSpaceImpl(text);
}
if (type instanceof CustomParsingType) {
return (TreeElement)((CustomParsingType)type).parse(text, myCharTable);
if (type instanceof ICustomParsingType) {
return (TreeElement)((ICustomParsingType)type).parse(text, myCharTable);
}
if (type instanceof ILazyParseableElementType) {
@@ -29,7 +29,7 @@ import com.intellij.psi.*;
import com.intellij.psi.impl.PsiDocumentManagerBase;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.reference.SoftReference;
import com.intellij.util.ArrayUtil;
import com.intellij.util.CommonProcessors;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
@@ -68,7 +68,7 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
FilePointersList pointers = reference.file.getUserData(reference.key);
if (pointers != null) {
pointers.remove(reference);
pointers.removeReference(reference);
}
}
}
@@ -160,12 +160,13 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
SmartPointerElementInfo info = pointer.getElementInfo();
if (!(info instanceof SelfElementInfo)) return;
PointerReference reference = new PointerReference(pointer, containingFile, POINTERS_KEY);
while (true) {
FilePointersList pointers = getPointers(containingFile);
if (pointers == null) {
pointers = containingFile.putUserDataIfAbsent(POINTERS_KEY, new FilePointersList());
}
if (pointers.add(new PointerReference(pointer, containingFile, ourQueue, POINTERS_KEY))) {
if (pointers.add(reference)) {
if (((SelfElementInfo)info).hasRange()) {
pointers.markerCache.rangeChanged();
}
@@ -193,8 +194,10 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
if (containingFile == null) return;
VirtualFile vFile = containingFile.getViewProvider().getVirtualFile();
FilePointersList pointers = getPointers(vFile);
if (pointers == null) return;
pointers.remove(pointer);
PointerReference reference = ((SmartPsiElementPointerImpl)pointer).pointerReference;
if (pointers != null && reference != null) {
pointers.removeReference(reference);
}
}
}
@@ -264,17 +267,18 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
return myPsiDocManager;
}
private static class PointerReference extends WeakReference<SmartPsiElementPointerImpl> {
static class PointerReference extends WeakReference<SmartPsiElementPointerImpl> {
@NotNull private final VirtualFile file;
@NotNull private final Key<FilePointersList> key;
private int index = -2;
private PointerReference(@NotNull SmartPsiElementPointerImpl<?> pointer,
@NotNull VirtualFile containingFile,
@NotNull ReferenceQueue<SmartPsiElementPointerImpl> queue,
@NotNull Key<FilePointersList> key) {
super(pointer, queue);
super(pointer, ourQueue);
file = containingFile;
this.key = key;
pointer.pointerReference = this;
}
}
@@ -292,104 +296,98 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
}
if (nextAvailableIndex >= references.length || nextAvailableIndex > size*2) { // overflow or too many dead refs
int newCapacity = nextAvailableIndex >= references.length ? references.length * 3/2 +1 : size * 3/2+1;
PointerReference[] newReferences = new PointerReference[newCapacity];
int newCapacity = (nextAvailableIndex >= references.length ? references.length : size) * 3 / 2 + 1;
final PointerReference[] newReferences = new PointerReference[newCapacity];
int o = 0;
for (PointerReference oldRef : references) {
if (SoftReference.dereference(oldRef) != null) {
newReferences[o++] = oldRef;
final int[] o = {0};
processAlivePointers(new Processor<SmartPsiElementPointerImpl>() {
@Override
public boolean process(SmartPsiElementPointerImpl pointer) {
storePointerReference(newReferences, o[0]++, pointer.pointerReference);
return true;
}
}
});
references = newReferences;
size = nextAvailableIndex = o;
size = nextAvailableIndex = o[0];
}
references[nextAvailableIndex++] = reference;
assert references[nextAvailableIndex] == null : references[nextAvailableIndex];
storePointerReference(references, nextAvailableIndex++, reference);
size++;
mySorted = false;
return true;
}
private synchronized void remove(@NotNull PointerReference reference) {
int index = ArrayUtil.indexOf(references, reference, 0, nextAvailableIndex);
if (index != -1) {
removeReference(reference, index);
}
}
private synchronized void removeReference(@NotNull PointerReference reference) {
int index = reference.index;
if (index < 0) return;
private synchronized void remove(@NotNull SmartPsiElementPointer smartPointer) {
for (int i = 0; i < nextAvailableIndex; i++) {
PointerReference reference = references[i];
if (reference != null && reference.get() == smartPointer) {
removeReference(reference, i);
return;
}
}
}
private void removeReference(@NotNull PointerReference reference, int index) {
assert references[index] == reference : "At " + index + " expected " + reference + ", found " + references[index];
references[index].index = -1;
references[index] = null;
if (--size == 0) {
reference.file.replace(reference.key, this, null);
}
}
boolean processAlivePointers(@NotNull Processor<SmartPsiElementPointerImpl> processor) {
synchronized boolean processAlivePointers(@NotNull Processor<SmartPsiElementPointerImpl> processor) {
for (int i = 0; i < nextAvailableIndex; i++) {
SmartPsiElementPointerImpl pointer = SoftReference.dereference(references[i]);
if (pointer != null && !processor.process(pointer)) {
PointerReference ref = references[i];
if (ref == null) continue;
SmartPsiElementPointerImpl pointer = ref.get();
if (pointer == null) {
removeReference(ref);
continue;
}
if (!processor.process(pointer)) {
return false;
}
}
return true;
}
synchronized List<SelfElementInfo> getSortedInfos() {
private void ensureSorted() {
if (!mySorted) {
List<SmartPsiElementPointerImpl> hardRefs = ContainerUtil.newArrayListWithCapacity(size);
for (int i = 0; i < nextAvailableIndex; i++) {
PointerReference reference = references[i];
if (reference == null) continue;
List<SmartPsiElementPointerImpl> pointers = new ArrayList<SmartPsiElementPointerImpl>();
processAlivePointers(new CommonProcessors.CollectProcessor<SmartPsiElementPointerImpl>(pointers));
assert size == pointers.size();
SmartPsiElementPointerImpl pointer = reference.get();
if (pointer != null) {
hardRefs.add(pointer);
}
else {
removeReference(reference, i);
if (size == 0) {
return Collections.emptyList();
}
}
}
assert size == hardRefs.size();
Arrays.sort(references, 0, nextAvailableIndex, new Comparator<PointerReference>() {
Collections.sort(pointers, new Comparator<SmartPsiElementPointerImpl>() {
@Override
public int compare(PointerReference o1, PointerReference o2) {
SmartPsiElementPointerImpl p1 = SoftReference.dereference(o1);
SmartPsiElementPointerImpl p2 = SoftReference.dereference(o2);
if (p1 == null || p2 == null) {
return p1 != null ? -1 : p2 != null ? 1 : 0; // null references to the end
}
public int compare(SmartPsiElementPointerImpl p1, SmartPsiElementPointerImpl p2) {
return MarkerCache.INFO_COMPARATOR.compare((SelfElementInfo)p1.getElementInfo(), (SelfElementInfo)p2.getElementInfo());
}
});
nextAvailableIndex = hardRefs.size();
for (int i = 0; i < pointers.size(); i++) {
storePointerReference(references, i, pointers.get(i).pointerReference);
}
Arrays.fill(references, pointers.size(), nextAvailableIndex, null);
nextAvailableIndex = pointers.size();
mySorted = true;
}
}
List<SelfElementInfo> infos = ContainerUtil.newArrayListWithCapacity(size);
for (int i = 0; i < nextAvailableIndex; i++) {
Reference<SmartPsiElementPointerImpl> reference = references[i];
SmartPsiElementPointerImpl pointer = SoftReference.dereference(reference);
if (pointer != null) {
private static void storePointerReference(PointerReference[] references, int index, PointerReference ref) {
references[index] = ref;
ref.index = index;
}
synchronized List<SelfElementInfo> getSortedInfos() {
ensureSorted();
final List<SelfElementInfo> infos = ContainerUtil.newArrayListWithCapacity(size);
processAlivePointers(new Processor<SmartPsiElementPointerImpl>() {
@Override
public boolean process(SmartPsiElementPointerImpl pointer) {
SelfElementInfo info = (SelfElementInfo)pointer.getElementInfo();
if (!info.hasRange()) break;
if (!info.hasRange()) return false;
infos.add(info);
return true;
}
}
});
return infos;
}
@@ -46,6 +46,7 @@ class SmartPsiElementPointerImpl<E extends PsiElement> implements SmartPointerEx
private final SmartPointerElementInfo myElementInfo;
private final Class<? extends PsiElement> myElementClass;
private byte myReferenceCount = 1;
@Nullable SmartPointerManagerImpl.PointerReference pointerReference;
SmartPsiElementPointerImpl(@NotNull Project project, @NotNull E element, @Nullable PsiFile containingFile, boolean forInjected) {
this(element, createElementInfo(project, element, containingFile, forInjected), element.getClass());
@@ -17,6 +17,7 @@ package com.intellij.diff.comparison;
import com.intellij.diff.fragments.DiffFragment;
import com.intellij.diff.fragments.LineFragment;
import com.intellij.diff.fragments.MergeLineFragment;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.progress.ProgressIndicator;
import org.jetbrains.annotations.NotNull;
@@ -61,6 +62,16 @@ public abstract class ComparisonManager {
@NotNull ComparisonPolicy policy,
@NotNull ProgressIndicator indicator) throws DiffTooBigException;
/**
* Compare three texts by-line (LEFT - BASE - RIGHT)
*/
@NotNull
public abstract List<MergeLineFragment> compareLines(@NotNull CharSequence text1,
@NotNull CharSequence text2,
@NotNull CharSequence text3,
@NotNull ComparisonPolicy policy,
@NotNull ProgressIndicator indicator) throws DiffTooBigException;
/**
* Compare two texts by-word
*/
@@ -15,13 +15,8 @@
*/
package com.intellij.diff.comparison;
import com.intellij.diff.comparison.iterables.DiffIterableUtil.*;
import com.intellij.diff.comparison.iterables.DiffIterableUtil.ExpandChangeBuilder;
import com.intellij.diff.comparison.iterables.FairDiffIterable;
import com.intellij.diff.fragments.LineFragment;
import com.intellij.diff.fragments.LineFragmentImpl;
import com.intellij.diff.fragments.MergeLineFragment;
import com.intellij.diff.fragments.MergeLineFragmentImpl;
import com.intellij.diff.util.IntPair;
import com.intellij.diff.util.MergeRange;
import com.intellij.diff.util.Range;
import com.intellij.openapi.progress.ProgressIndicator;
@@ -29,68 +24,77 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.Equality;
import gnu.trove.TIntArrayList;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import static com.intellij.diff.comparison.ComparisonPolicy.IGNORE_WHITESPACES;
import static com.intellij.diff.comparison.TrimUtil.trimEnd;
import static com.intellij.diff.comparison.TrimUtil.trimStart;
import static com.intellij.diff.comparison.iterables.DiffIterableUtil.*;
import static com.intellij.diff.comparison.iterables.DiffIterableUtil.diff;
import static com.intellij.diff.comparison.iterables.DiffIterableUtil.fair;
import static com.intellij.openapi.util.text.StringUtil.isWhiteSpace;
public class ByLine {
@NotNull
public static List<LineFragment> compare(@NotNull CharSequence text1,
@NotNull CharSequence text2,
@NotNull ComparisonPolicy policy,
@NotNull ProgressIndicator indicator) {
public static FairDiffIterable compare(@NotNull List<? extends CharSequence> lines1,
@NotNull List<? extends CharSequence> lines2,
@NotNull ComparisonPolicy policy,
@NotNull ProgressIndicator indicator) {
indicator.checkCanceled();
List<Line> lines1 = getLines(text1, policy);
List<Line> lines2 = getLines(text2, policy);
FairDiffIterable changes = compareSmart(lines1, lines2, indicator);
changes = optimizeLineChunks(lines1, lines2, changes, indicator);
changes = expandRanges(lines1, lines2, changes, indicator);
return convertIntoFragments(lines1, lines2, changes);
return doCompare(getLines(lines1, policy), getLines(lines2, policy), policy, indicator);
}
@NotNull
public static List<LineFragment> compareTwoStep(@NotNull CharSequence text1,
@NotNull CharSequence text2,
@NotNull ComparisonPolicy policy,
@NotNull ProgressIndicator indicator) {
public static List<MergeRange> compare(@NotNull List<? extends CharSequence> lines1,
@NotNull List<? extends CharSequence> lines2,
@NotNull List<? extends CharSequence> lines3,
@NotNull ComparisonPolicy policy,
@NotNull ProgressIndicator indicator) {
indicator.checkCanceled();
return doCompare(getLines(lines1, policy), getLines(lines2, policy), getLines(lines3, policy), policy, indicator);
}
//
// Impl
//
@NotNull
static FairDiffIterable doCompare(@NotNull List<Line> lines1,
@NotNull List<Line> lines2,
@NotNull ComparisonPolicy policy,
@NotNull ProgressIndicator indicator) {
indicator.checkCanceled();
List<Line> lines1 = getLines(text1, policy);
List<Line> lines2 = getLines(text2, policy);
if (policy == IGNORE_WHITESPACES) {
FairDiffIterable changes = compareSmart(lines1, lines2, indicator);
changes = optimizeLineChunks(lines1, lines2, changes, indicator);
return correctChangesSecondStepIW(lines1, lines2, changes);
}
else {
List<Line> iwLines1 = convertMode(lines1, IGNORE_WHITESPACES);
List<Line> iwLines2 = convertMode(lines2, IGNORE_WHITESPACES);
List<Line> iwLines1 = convertToIgnoreWhitespace(lines1);
List<Line> iwLines2 = convertToIgnoreWhitespace(lines2);
FairDiffIterable iwChanges = compareSmart(iwLines1, iwLines2, indicator);
iwChanges = optimizeLineChunks(lines1, lines2, iwChanges, indicator);
FairDiffIterable changes = correctChangesSecondStep(lines1, lines2, iwChanges);
return convertIntoFragments(lines1, lines2, changes);
FairDiffIterable iwChanges = compareSmart(iwLines1, iwLines2, indicator);
iwChanges = optimizeLineChunks(lines1, lines2, iwChanges, indicator);
return correctChangesSecondStep(lines1, lines2, iwChanges);
}
}
@NotNull
public static List<MergeLineFragment> compareTwoStep(@NotNull CharSequence text1,
@NotNull CharSequence text2,
@NotNull CharSequence text3,
@NotNull ComparisonPolicy policy,
@NotNull ProgressIndicator indicator) {
static List<MergeRange> doCompare(@NotNull List<Line> lines1,
@NotNull List<Line> lines2,
@NotNull List<Line> lines3,
@NotNull ComparisonPolicy policy,
@NotNull ProgressIndicator indicator) {
indicator.checkCanceled();
List<Line> lines1 = getLines(text1, policy);
List<Line> lines2 = getLines(text2, policy);
List<Line> lines3 = getLines(text3, policy);
List<Line> iwLines1 = convertToIgnoreWhitespace(lines1);
List<Line> iwLines2 = convertToIgnoreWhitespace(lines2);
List<Line> iwLines3 = convertToIgnoreWhitespace(lines3);
List<Line> iwLines1 = convertMode(lines1, IGNORE_WHITESPACES);
List<Line> iwLines2 = convertMode(lines2, IGNORE_WHITESPACES);
List<Line> iwLines3 = convertMode(lines3, IGNORE_WHITESPACES);
FairDiffIterable iwChanges1 = compareSmart(iwLines2, iwLines1, indicator);
iwChanges1 = optimizeLineChunks(lines2, lines1, iwChanges1, indicator);
@@ -100,18 +104,30 @@ public class ByLine {
iwChanges2 = optimizeLineChunks(lines2, lines3, iwChanges2, indicator);
FairDiffIterable iterable2 = correctChangesSecondStep(lines2, lines3, iwChanges2);
List<MergeRange> conflicts = ComparisonMergeUtil.buildFair(iterable1, iterable2, indicator);
return convertIntoFragments(conflicts);
return ComparisonMergeUtil.buildFair(iterable1, iterable2, indicator);
}
//
// Impl
//
@NotNull
private static FairDiffIterable correctChangesSecondStep(@NotNull final List<Line> lines1,
@NotNull final List<Line> lines2,
@NotNull final FairDiffIterable changes) {
return doCorrectChangesSecondStep(lines1, lines2, changes,
Equality.CANONICAL);
}
@NotNull
private static FairDiffIterable correctChangesSecondStepIW(@NotNull final List<Line> lines1,
@NotNull final List<Line> lines2,
@NotNull final FairDiffIterable changes) {
return doCorrectChangesSecondStep(lines1, lines2, changes,
(l1, l2) -> StringUtil.equals(l1.getContent(), l2.getContent()));
}
@NotNull
private static FairDiffIterable doCorrectChangesSecondStep(@NotNull final List<Line> lines1,
@NotNull final List<Line> lines2,
@NotNull final FairDiffIterable changes,
@NotNull final Equality<Line> maximisingEquality) {
/*
* We want to fix invalid matching here:
*
@@ -156,7 +172,7 @@ public class ByLine {
Line line2 = lines2.get(index2);
if (!StringUtil.equalsIgnoreWhitespaces(sample, line1.getContent())) {
if (line1.equals(line2)) {
if (maximisingEquality.equals(line1, line2)) {
flush(index1, index2);
builder.markEqual(index1, index2);
}
@@ -197,13 +213,24 @@ public class ByLine {
}
private void alignExactMatching(TIntArrayList subLines1, TIntArrayList subLines2) {
if (subLines1.size() == subLines2.size()) return;
int n = Math.max(subLines1.size(), subLines2.size());
if (n > 10) return; // we use brute-force algorithm (C_n_k). This will limit search space by ~250 cases.
boolean skipAligning = n > 10 || // we use brute-force algorithm (C_n_k). This will limit search space by ~250 cases.
subLines1.size() == subLines2.size(); // nothing to do
if (skipAligning) {
int count = Math.min(subLines1.size(), subLines2.size());
for (int i = 0; i < count; i++) {
int index1 = subLines1.get(i);
int index2 = subLines2.get(i);
if (lines1.get(index1).equals(lines2.get(index2))) {
builder.markEqual(index1, index2);
}
}
return;
}
if (subLines1.size() < subLines2.size()) {
int[] matching = getBestMatchingAlignment(subLines1, subLines2, lines1, lines2);
int[] matching = getBestMatchingAlignment(subLines1, subLines2, lines1, lines2, maximisingEquality);
for (int i = 0; i < subLines1.size(); i++) {
int index1 = subLines1.get(i);
int index2 = subLines2.get(matching[i]);
@@ -213,7 +240,7 @@ public class ByLine {
}
}
else {
int[] matching = getBestMatchingAlignment(subLines2, subLines1, lines2, lines1);
int[] matching = getBestMatchingAlignment(subLines2, subLines1, lines2, lines1, maximisingEquality);
for (int i = 0; i < subLines2.size(); i++) {
int index1 = subLines1.get(matching[i]);
int index2 = subLines2.get(i);
@@ -232,7 +259,8 @@ public class ByLine {
private static int[] getBestMatchingAlignment(@NotNull final TIntArrayList subLines1,
@NotNull final TIntArrayList subLines2,
@NotNull final List<Line> lines1,
@NotNull final List<Line> lines2) {
@NotNull final List<Line> lines2,
@NotNull final Equality<Line> maximisingEquality) {
assert subLines1.size() < subLines2.size();
final int size = subLines1.size();
@@ -267,7 +295,7 @@ public class ByLine {
for (int i = 0; i < size; i++) {
int index1 = subLines1.get(i);
int index2 = subLines2.get(comb[i]);
if (lines1.get(index1).equals(lines2.get(index2))) weight++;
if (maximisingEquality.equals(lines1.get(index1), lines2.get(index2))) weight++;
}
if (weight > bestWeight) {
@@ -288,45 +316,6 @@ public class ByLine {
return new ChunkOptimizer.LineChunkOptimizer(lines1, lines2, iterable, indicator).build();
}
@NotNull
private static List<LineFragment> convertIntoFragments(@NotNull List<Line> lines1,
@NotNull List<Line> lines2,
@NotNull FairDiffIterable changes) {
List<LineFragment> fragments = new ArrayList<>();
for (Range ch : changes.iterateChanges()) {
IntPair offsets1 = getOffsets(lines1, ch.start1, ch.end1);
IntPair offsets2 = getOffsets(lines2, ch.start2, ch.end2);
fragments.add(new LineFragmentImpl(ch.start1, ch.end1, ch.start2, ch.end2,
offsets1.val1, offsets1.val2, offsets2.val1, offsets2.val2));
}
return fragments;
}
@NotNull
private static List<MergeLineFragment> convertIntoFragments(@NotNull List<MergeRange> conflicts) {
return ContainerUtil.map(conflicts, ch -> new MergeLineFragmentImpl(ch));
}
@NotNull
private static IntPair getOffsets(@NotNull List<Line> lines, int startIndex, int endIndex) {
if (startIndex == endIndex) {
int offset;
if (startIndex < lines.size()) {
offset = lines.get(startIndex).getOffset1();
}
else {
offset = lines.get(lines.size() - 1).getOffset2();
}
return new IntPair(offset, offset);
}
else {
int offset1 = lines.get(startIndex).getOffset1();
int offset2 = lines.get(endIndex - 1).getOffset2();
return new IntPair(offset1, offset2);
}
}
/*
* Compare lines in two steps:
* - compare ignoring "unimportant" lines
@@ -361,90 +350,44 @@ public class ByLine {
return Pair.create(bigLines, indexes);
}
@NotNull
private static FairDiffIterable expandRanges(@NotNull List<Line> lines1,
@NotNull List<Line> lines2,
@NotNull FairDiffIterable iterable,
@NotNull ProgressIndicator indicator) {
List<Range> changes = new ArrayList<>();
for (Range ch : iterable.iterateChanges()) {
Range expanded = TrimUtil.expand(lines1, lines2, ch.start1, ch.start2, ch.end1, ch.end2);
if (!expanded.isEmpty()) changes.add(expanded);
}
return fair(create(changes, lines1.size(), lines2.size()));
}
//
// Lines
//
@NotNull
private static List<Line> getLines(@NotNull CharSequence text, @NotNull ComparisonPolicy policy) {
List<Line> lines = new ArrayList<>();
int offset = 0;
while (true) {
Line line = createLine(text, offset, policy);
lines.add(line);
offset = line.getOffset2();
if (!line.hasNewline()) break;
}
return lines;
private static List<Line> getLines(@NotNull List<? extends CharSequence> text, @NotNull ComparisonPolicy policy) {
return ContainerUtil.map(text, (line) -> new Line(line, policy));
}
@NotNull
private static Line createLine(@NotNull CharSequence text, int offset, @NotNull ComparisonPolicy policy) {
switch (policy) {
case DEFAULT:
return Line.createDefault(text, offset);
case IGNORE_WHITESPACES:
return Line.createIgnore(text, offset);
case TRIM_WHITESPACES:
return Line.createTrim(text, offset);
default:
throw new IllegalArgumentException(policy.name());
}
}
@NotNull
private static List<Line> convertToIgnoreWhitespace(@NotNull List<Line> original) {
private static List<Line> convertMode(@NotNull List<Line> original, @NotNull ComparisonPolicy policy) {
List<Line> result = new ArrayList<>(original.size());
for (Line line : original) {
result.add(Line.createIgnore(line.getOriginalText(), line.getOffset1()));
result.add(new Line(line.getContent(), policy));
}
return result;
}
static class Line extends TextChunk {
enum Mode {DEFAULT, TRIM, IGNORE}
@NotNull private final Mode myMode;
static class Line {
@NotNull private final CharSequence myText;
@NotNull private final ComparisonPolicy myPolicy;
private final int myHash;
private final int myNonSpaceChars;
private final boolean myNewline;
public Line(@NotNull CharSequence text, int offset1, int offset2,
@NotNull Mode mode, int hash, int nonSpaceChars, boolean newline) {
super(text, offset1, offset2);
myMode = mode;
myHash = hash;
myNonSpaceChars = nonSpaceChars;
myNewline = newline;
}
public boolean hasNewline() {
return myNewline;
public Line(@NotNull CharSequence text, @NotNull ComparisonPolicy policy) {
myText = text;
myPolicy = policy;
myHash = hashCode(text, policy);
myNonSpaceChars = countNonSpaceChars(text);
}
@NotNull
@Override
public CharSequence getContent() {
return getOriginalText().subSequence(getOffset1(), getOffset2() - (myNewline ? 1 : 0));
return myText;
}
public int getNonSpaceChars() {
return myNonSpaceChars;
}
@Override
@@ -453,20 +396,11 @@ public class ByLine {
if (o == null || getClass() != o.getClass()) return false;
Line line = (Line)o;
assert myMode == line.myMode;
assert myPolicy == line.myPolicy;
if (hashCode() != line.hashCode()) return false;
switch (myMode) {
case DEFAULT:
return StringUtil.equals(getContent(), line.getContent());
case TRIM:
return StringUtil.equalsTrimWhitespaces(getContent(), line.getContent());
case IGNORE:
return StringUtil.equalsIgnoreWhitespaces(getContent(), line.getContent());
default:
throw new IllegalArgumentException(myMode.toString());
}
return equals(getContent(), line.getContent(), myPolicy);
}
@Override
@@ -474,87 +408,47 @@ public class ByLine {
return myHash;
}
public int getNonSpaceChars() {
return myNonSpaceChars;
}
public static Line createDefault(@NotNull CharSequence text, int startOffset) {
int len = text.length();
int h = 0;
private static int countNonSpaceChars(@NotNull CharSequence text) {
int nonSpace = 0;
boolean newline = false;
int offset = startOffset;
int len = text.length();
int offset = 0;
while (offset < len) {
char c = text.charAt(offset);
if (c == '\n') {
offset++;
newline = true;
break;
}
if (!isWhiteSpace(c)) nonSpace++;
h = 31 * h + c;
offset++;
}
return new Line(text, startOffset, offset, Mode.DEFAULT, h, nonSpace, newline);
}
public static Line createIgnore(@NotNull CharSequence text, int startOffset) {
int len = text.length();
int h = 0;
int nonSpace = 0;
boolean newline = false;
int offset = startOffset;
while (offset < len) {
char c = text.charAt(offset);
if (c == '\n') {
offset++;
newline = true;
break;
}
if (!isWhiteSpace(c)) {
nonSpace++;
h = 31 * h + c;
}
offset++;
}
return new Line(text, startOffset, offset, Mode.IGNORE, h, nonSpace, newline);
}
public static Line createTrim(@NotNull CharSequence text, int startOffset) {
int len = text.length();
int nonSpace = 0;
boolean newline = false;
int offset = startOffset;
while (offset < len) {
char c = text.charAt(offset);
if (c == '\n') {
offset++;
newline = true;
break;
}
if (!isWhiteSpace(c)) nonSpace++;
offset++;
}
int h = calcTrimHash(text, startOffset, offset);
return new Line(text, startOffset, offset, Mode.TRIM, h, nonSpace, newline);
return nonSpace;
}
private static boolean equals(@NotNull CharSequence text1, @NotNull CharSequence text2, @NotNull ComparisonPolicy policy) {
switch (policy) {
case DEFAULT:
return StringUtil.equals(text1, text2);
case TRIM_WHITESPACES:
return StringUtil.equalsTrimWhitespaces(text1, text2);
case IGNORE_WHITESPACES:
return StringUtil.equalsIgnoreWhitespaces(text1, text2);
default:
throw new IllegalArgumentException(policy.toString());
}
}
private static int calcTrimHash(@NotNull CharSequence text, int offset1, int offset2) {
offset1 = trimStart(text, offset1, offset2);
offset2 = trimEnd(text, offset1, offset2);
return StringUtil.stringHashCode(text, offset1, offset2);
private static int hashCode(@NotNull CharSequence text, @NotNull ComparisonPolicy policy) {
switch (policy) {
case DEFAULT:
return StringUtil.stringHashCode(text);
case TRIM_WHITESPACES:
int offset1 = trimStart(text, 0, text.length());
int offset2 = trimEnd(text, offset1, text.length());
return StringUtil.stringHashCode(text, offset1, offset2);
case IGNORE_WHITESPACES:
return StringUtil.stringHashCodeIgnoreWhitespaces(text);
default:
throw new IllegalArgumentException(policy.name());
}
}
}
}
@@ -17,24 +17,23 @@ package com.intellij.diff.comparison;
import com.intellij.diff.comparison.LineFragmentSplitter.WordBlock;
import com.intellij.diff.comparison.iterables.DiffIterable;
import com.intellij.diff.comparison.iterables.DiffIterableUtil;
import com.intellij.diff.comparison.iterables.DiffIterableUtil.*;
import com.intellij.diff.comparison.iterables.FairDiffIterable;
import com.intellij.diff.fragments.DiffFragment;
import com.intellij.diff.fragments.MergeWordFragment;
import com.intellij.diff.fragments.MergeWordFragmentImpl;
import com.intellij.diff.util.MergeRange;
import com.intellij.diff.util.Range;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.MergingCharSequence;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import static com.intellij.diff.comparison.ComparisonManagerImpl.convertIntoDiffFragments;
import static com.intellij.diff.comparison.ComparisonManagerImpl.convertIntoMergeWordFragments;
import static com.intellij.diff.comparison.TrimUtil.*;
import static com.intellij.diff.comparison.TrimUtil.trim;
import static com.intellij.diff.comparison.iterables.DiffIterableUtil.*;
@@ -66,7 +65,7 @@ public class ByWord {
FairDiffIterable delimitersIterable = matchAdjustmentDelimiters(text1, text2, words1, words2, wordChanges, indicator);
DiffIterable iterable = matchAdjustmentWhitespaces(text1, text2, delimitersIterable, policy, indicator);
return convertIntoFragments(iterable);
return convertIntoDiffFragments(iterable);
}
@NotNull
@@ -92,7 +91,7 @@ public class ByWord {
List<MergeRange> wordConflicts = ComparisonMergeUtil.buildFair(iterable1, iterable2, indicator);
List<MergeRange> result = matchAdjustmentWhitespaces(text1, text2, text3, wordConflicts, policy, indicator);
return convertIntoFragments(result);
return convertIntoMergeWordFragments(result);
}
@NotNull
@@ -144,7 +143,7 @@ public class ByWord {
offsets.start1, offsets.start2, indicator);
DiffIterable iterable = matchAdjustmentWhitespaces(subtext1, subtext2, delimitersIterable, policy, indicator);
List<DiffFragment> fragments = convertIntoFragments(iterable);
List<DiffFragment> fragments = convertIntoDiffFragments(iterable);
int newlines1 = countNewlines(subwords1);
int newlines2 = countNewlines(subwords2);
@@ -159,16 +158,6 @@ public class ByWord {
// Impl
//
@NotNull
private static List<MergeWordFragment> convertIntoFragments(@NotNull List<MergeRange> conflicts) {
return ContainerUtil.map(conflicts, ch -> new MergeWordFragmentImpl(ch));
}
@NotNull
private static List<DiffFragment> convertIntoFragments(@NotNull DiffIterable iterable) {
return DiffIterableUtil.convertIntoFragments(iterable);
}
@NotNull
private static FairDiffIterable optimizeWordChunks(@NotNull CharSequence text1,
@NotNull CharSequence text2,
@@ -863,14 +852,32 @@ public class ByWord {
int getOffset2();
}
static class WordChunk extends TextChunk implements InlineChunk {
static class WordChunk implements InlineChunk {
@NotNull private final CharSequence myText;
private final int myOffset1;
private final int myOffset2;
private final int myHash;
public WordChunk(@NotNull CharSequence text, int offset1, int offset2, int hash) {
super(text, offset1, offset2);
myText = text;
myOffset1 = offset1;
myOffset2 = offset2;
myHash = hash;
}
@NotNull
public CharSequence getContent() {
return myText.subSequence(myOffset1, myOffset2);
}
public int getOffset1() {
return myOffset1;
}
public int getOffset2() {
return myOffset2;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -15,10 +15,11 @@
*/
package com.intellij.diff.comparison;
import com.intellij.diff.fragments.DiffFragment;
import com.intellij.diff.fragments.DiffFragmentImpl;
import com.intellij.diff.fragments.LineFragment;
import com.intellij.diff.fragments.LineFragmentImpl;
import com.intellij.diff.comparison.iterables.DiffIterable;
import com.intellij.diff.comparison.iterables.FairDiffIterable;
import com.intellij.diff.fragments.*;
import com.intellij.diff.util.IntPair;
import com.intellij.diff.util.MergeRange;
import com.intellij.diff.util.Range;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
@@ -33,8 +34,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static com.intellij.diff.comparison.iterables.DiffIterableUtil.convertIntoFragments;
public class ComparisonManagerImpl extends ComparisonManager {
public static final Logger LOG = Logger.getInstance(ComparisonManagerImpl.class);
@@ -44,12 +43,24 @@ public class ComparisonManagerImpl extends ComparisonManager {
@NotNull CharSequence text2,
@NotNull ComparisonPolicy policy,
@NotNull ProgressIndicator indicator) throws DiffTooBigException {
if (policy == ComparisonPolicy.IGNORE_WHITESPACES) {
return ByLine.compare(text1, text2, policy, indicator);
}
else {
return ByLine.compareTwoStep(text1, text2, policy, indicator);
}
List<Line> lines1 = getLines(text1);
List<Line> lines2 = getLines(text2);
FairDiffIterable iterable = ByLine.compare(lines1, lines2, policy, indicator);
return convertIntoLineFragments(lines1, lines2, iterable);
}
@NotNull
@Override
public List<MergeLineFragment> compareLines(@NotNull CharSequence text1,
@NotNull CharSequence text2,
@NotNull CharSequence text3,
@NotNull ComparisonPolicy policy,
@NotNull ProgressIndicator indicator) throws DiffTooBigException {
List<Line> lines1 = getLines(text1);
List<Line> lines2 = getLines(text2);
List<Line> lines3 = getLines(text3);
List<MergeRange> ranges = ByLine.compare(lines1, lines2, lines3, policy, indicator);
return convertIntoMergeLineFragments(ranges);
}
@NotNull
@@ -145,13 +156,13 @@ public class ComparisonManagerImpl extends ComparisonManager {
@NotNull ComparisonPolicy policy,
@NotNull ProgressIndicator indicator) throws DiffTooBigException {
if (policy == ComparisonPolicy.IGNORE_WHITESPACES) {
return convertIntoFragments(ByChar.compareIgnoreWhitespaces(text1, text2, indicator));
return convertIntoDiffFragments(ByChar.compareIgnoreWhitespaces(text1, text2, indicator));
}
if (policy == ComparisonPolicy.DEFAULT) {
return convertIntoFragments(ByChar.compareTwoStep(text1, text2, indicator));
return convertIntoDiffFragments(ByChar.compareTwoStep(text1, text2, indicator));
}
LOG.warn(policy.toString() + " is not supported by ByChar comparison");
return convertIntoFragments(ByChar.compareTwoStep(text1, text2, indicator));
return convertIntoDiffFragments(ByChar.compareTwoStep(text1, text2, indicator));
}
@Override
@@ -159,6 +170,63 @@ public class ComparisonManagerImpl extends ComparisonManager {
return ComparisonUtil.isEquals(text1, text2, policy);
}
//
// Fragments
//
@NotNull
public static List<DiffFragment> convertIntoDiffFragments(@NotNull DiffIterable changes) {
final List<DiffFragment> fragments = new ArrayList<>();
for (Range ch : changes.iterateChanges()) {
fragments.add(new DiffFragmentImpl(ch.start1, ch.end1, ch.start2, ch.end2));
}
return fragments;
}
@NotNull
public static List<LineFragment> convertIntoLineFragments(@NotNull List<Line> lines1,
@NotNull List<Line> lines2,
@NotNull FairDiffIterable changes) {
List<LineFragment> fragments = new ArrayList<>();
for (Range ch : changes.iterateChanges()) {
IntPair offsets1 = getOffsets(lines1, ch.start1, ch.end1);
IntPair offsets2 = getOffsets(lines2, ch.start2, ch.end2);
fragments.add(new LineFragmentImpl(ch.start1, ch.end1, ch.start2, ch.end2,
offsets1.val1, offsets1.val2, offsets2.val1, offsets2.val2));
}
return fragments;
}
@NotNull
private static IntPair getOffsets(@NotNull List<Line> lines, int startIndex, int endIndex) {
if (startIndex == endIndex) {
int offset;
if (startIndex < lines.size()) {
offset = lines.get(startIndex).getOffset1();
}
else {
offset = lines.get(lines.size() - 1).getOffset2();
}
return new IntPair(offset, offset);
}
else {
int offset1 = lines.get(startIndex).getOffset1();
int offset2 = lines.get(endIndex - 1).getOffset2();
return new IntPair(offset1, offset2);
}
}
@NotNull
public static List<MergeLineFragment> convertIntoMergeLineFragments(@NotNull List<MergeRange> conflicts) {
return ContainerUtil.map(conflicts, ch -> new MergeLineFragmentImpl(ch.start1, ch.end1, ch.start2, ch.end2, ch.start3, ch.end3));
}
@NotNull
public static List<MergeWordFragment> convertIntoMergeWordFragments(@NotNull List<MergeRange> conflicts) {
return ContainerUtil.map(conflicts, ch -> new MergeWordFragmentImpl(ch.start1, ch.end1, ch.start2, ch.end2, ch.start3, ch.end3));
}
//
// Post process line fragments
//
@@ -304,4 +372,45 @@ public class ComparisonManagerImpl extends ComparisonManager {
int length2 = lineFragment.getEndOffset2() - lineFragment.getStartOffset2();
return Collections.singletonList(new DiffFragmentImpl(0, length1, 0, length2));
}
@NotNull
private static List<Line> getLines(@NotNull CharSequence text) {
List<Line> lines = new ArrayList<>();
int offset = 0;
while (true) {
int lineEnd = StringUtil.indexOf(text, '\n', offset);
if (lineEnd != -1) {
lines.add(new Line(text, offset, lineEnd, true));
offset = lineEnd + 1;
}
else {
lines.add(new Line(text, offset, text.length(), false));
break;
}
}
return lines;
}
private static class Line extends CharSequenceSubSequence {
private final int myOffset1;
private final int myOffset2;
private final boolean myNewline;
public Line(@NotNull CharSequence chars, int offset1, int offset2, boolean newline) {
super(chars, offset1, offset2);
myOffset1 = offset1;
myOffset2 = offset2;
myNewline = newline;
}
public int getOffset1() {
return myOffset1;
}
public int getOffset2() {
return myOffset2 + (myNewline ? 1 : 0);
}
}
}
@@ -1,59 +0,0 @@
/*
* Copyright 2000-2015 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.diff.comparison;
import org.jetbrains.annotations.NotNull;
abstract class TextChunk {
@NotNull private final CharSequence myText;
private final int myOffset1;
private final int myOffset2;
public TextChunk(@NotNull CharSequence text, int offset1, int offset2) {
myText = text;
myOffset1 = offset1;
myOffset2 = offset2;
}
@Override
public abstract int hashCode();
@Override
public abstract boolean equals(Object obj);
@NotNull
public CharSequence getContent() {
return myText.subSequence(myOffset1, myOffset2);
}
@NotNull
public CharSequence getOriginalText() {
return myText;
}
public int getOffset1() {
return myOffset1;
}
public int getOffset2() {
return myOffset2;
}
@Override
public String toString() {
return getContent().toString();
}
}
@@ -18,7 +18,6 @@ package com.intellij.diff.comparison.iterables;
import com.intellij.diff.comparison.DiffTooBigException;
import com.intellij.diff.comparison.TrimUtil;
import com.intellij.diff.fragments.DiffFragment;
import com.intellij.diff.fragments.DiffFragmentImpl;
import com.intellij.diff.util.Range;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.util.Comparing;
@@ -88,20 +87,6 @@ public class DiffIterableUtil {
return diff(data1, data2, indicator);
}
/*
* Compare two arrays, basing on equals() and hashCode() of it's elements
*
* If the input arrays are too big, "everything is changed" can be returned.
*/
@NotNull
public static <T> FairDiffIterable diffSomehow(@NotNull T[] data1, @NotNull T[] data2, @NotNull ProgressIndicator indicator) {
indicator.checkCanceled();
// TODO: use ProgressIndicator inside
Diff.Change change = Diff.buildChangesSomehow(data1, data2);
return fair(create(change, data1.length, data2.length));
}
//
// Iterable
//
@@ -158,15 +143,6 @@ public class DiffIterableUtil {
// Misc
//
@NotNull
public static List<DiffFragment> convertIntoFragments(@NotNull DiffIterable changes) {
final List<DiffFragment> fragments = new ArrayList<>();
for (Range ch : changes.iterateChanges()) {
fragments.add(new DiffFragmentImpl(ch.start1, ch.end1, ch.start2, ch.end2));
}
return fragments;
}
@NotNull
public static Iterable<Pair<Range, Boolean>> iterateAll(@NotNull final DiffIterable iterable) {
return () -> new Iterator<Pair<Range, Boolean>>() {
@@ -15,7 +15,6 @@
*/
package com.intellij.diff.fragments;
import com.intellij.diff.util.MergeRange;
import com.intellij.diff.util.ThreeSide;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -57,14 +56,6 @@ public class MergeLineFragmentImpl implements MergeLineFragment {
myInnerFragments = innerFragments;
}
public MergeLineFragmentImpl(@NotNull MergeRange range) {
this(range, null);
}
public MergeLineFragmentImpl(@NotNull MergeRange range, @Nullable List<MergeWordFragment> innerFragments) {
this(range.start1, range.end1, range.start2, range.end2, range.start3, range.end3, innerFragments);
}
public MergeLineFragmentImpl(@NotNull MergeLineFragment fragment, @Nullable List<MergeWordFragment> fragments) {
this(fragment.getStartLine(ThreeSide.LEFT), fragment.getEndLine(ThreeSide.LEFT),
fragment.getStartLine(ThreeSide.BASE), fragment.getEndLine(ThreeSide.BASE),
@@ -41,10 +41,6 @@ public class MergeWordFragmentImpl implements MergeWordFragment {
myEndOffset3 = endOffset3;
}
public MergeWordFragmentImpl(@NotNull MergeRange range) {
this(range.start1, range.end1, range.start2, range.end2, range.start3, range.end3);
}
@Override
public int getStartOffset(@NotNull ThreeSide side) {
return side.select(myStartOffset1, myStartOffset2, myStartOffset3);
@@ -18,7 +18,7 @@ package com.intellij.diff.merge;
import com.intellij.diff.DiffContext;
import com.intellij.diff.FrameDiffTool;
import com.intellij.diff.actions.ProxyUndoRedoAction;
import com.intellij.diff.comparison.ByLine;
import com.intellij.diff.comparison.ComparisonManager;
import com.intellij.diff.comparison.ComparisonMergeUtil;
import com.intellij.diff.comparison.ComparisonPolicy;
import com.intellij.diff.comparison.DiffTooBigException;
@@ -363,8 +363,9 @@ public class TextMergeViewer implements MergeTool.MergeViewer {
return ContainerUtil.map(documents, Document::getImmutableCharSequence);
});
List<MergeLineFragment> lineFragments = ByLine.compareTwoStep(sequences.get(0), sequences.get(1), sequences.get(2),
ComparisonPolicy.DEFAULT, indicator);
ComparisonManager manager = ComparisonManager.getInstance();
List<MergeLineFragment> lineFragments = manager.compareLines(sequences.get(0), sequences.get(1), sequences.get(2),
ComparisonPolicy.DEFAULT, indicator);
List<MergeConflictType> conflictTypes = ReadAction.compute(() -> {
indicator.checkCanceled();
@@ -16,7 +16,7 @@
package com.intellij.diff.tools.simple;
import com.intellij.diff.DiffContext;
import com.intellij.diff.comparison.ByLine;
import com.intellij.diff.comparison.ComparisonManager;
import com.intellij.diff.comparison.ComparisonPolicy;
import com.intellij.diff.comparison.DiffTooBigException;
import com.intellij.diff.contents.DiffContent;
@@ -125,8 +125,10 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewerEx {
});
final ComparisonPolicy comparisonPolicy = getIgnorePolicy().getComparisonPolicy();
List<MergeLineFragment> lineFragments = ByLine.compareTwoStep(sequences.get(0), sequences.get(1), sequences.get(2),
comparisonPolicy, indicator);
ComparisonManager manager = ComparisonManager.getInstance();
List<MergeLineFragment> lineFragments = manager.compareLines(sequences.get(0), sequences.get(1), sequences.get(2),
comparisonPolicy, indicator);
List<MergeConflictType> conflictTypes = ReadAction.compute(() -> {
indicator.checkCanceled();

Some files were not shown because too many files have changed in this diff Show More