mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master' into amakeev/remote-debug_linux
This commit is contained in:
+37
@@ -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) {
|
||||
|
||||
+1
-1
@@ -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) {
|
||||
|
||||
+155
-11
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
-191
@@ -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";
|
||||
}
|
||||
}
|
||||
-109
@@ -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();
|
||||
}
|
||||
}
|
||||
+49
-32
@@ -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;
|
||||
|
||||
+59
-16
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-4
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-3
@@ -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
-1
@@ -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
-1
@@ -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
-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() {
|
||||
return foo();
|
||||
|
||||
+1
-1
@@ -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>);
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// "Generate overloaded method with default parameter values" "true"
|
||||
class Test {
|
||||
void foo(){}
|
||||
void foo(int ii){
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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
-1
@@ -1,4 +1,4 @@
|
||||
// "Generate overloaded method with default parameter value" "true"
|
||||
// "Generate overloaded method with default parameter values" "true"
|
||||
class Test {
|
||||
/**
|
||||
*/
|
||||
|
||||
+1
-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() {
|
||||
return foo(<caret>);
|
||||
|
||||
+1
-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() {
|
||||
foo();
|
||||
|
||||
+1
-1
@@ -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
-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(boolean... args) {
|
||||
return foo(<caret>, args);
|
||||
|
||||
+1
-1
@@ -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
-1
@@ -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
-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){
|
||||
//comment1
|
||||
|
||||
+1
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-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){
|
||||
return 1;
|
||||
|
||||
+1
-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
-1
@@ -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
-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;
|
||||
|
||||
-13
@@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
-14
@@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
-15
@@ -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 {}
|
||||
}
|
||||
|
||||
-17
@@ -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();
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package p;
|
||||
|
||||
public abstract class A implements I {}
|
||||
@@ -0,0 +1,4 @@
|
||||
package p;
|
||||
interface I {
|
||||
default void foo();
|
||||
}
|
||||
+8
@@ -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);
|
||||
}
|
||||
}
|
||||
+7
@@ -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 {}
|
||||
+16
@@ -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>
|
||||
}
|
||||
}
|
||||
+14
@@ -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");
|
||||
|
||||
+3
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-33
@@ -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();
|
||||
}
|
||||
}
|
||||
+112
@@ -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);
|
||||
}
|
||||
}
|
||||
+17
-4
@@ -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);
|
||||
}
|
||||
|
||||
+10
-13
@@ -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) {
|
||||
|
||||
+68
-70
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -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();
|
||||
|
||||
+5
-3
@@ -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
Reference in New Issue
Block a user