diff --git a/build.txt b/build.txt index 96ff88c46f66..6f540e14d3a6 100644 --- a/build.txt +++ b/build.txt @@ -1 +1 @@ -144.SNAPSHOT +146.SNAPSHOT diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java index bcd2424fb754..0698df0e079e 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 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. @@ -40,6 +40,7 @@ import com.intellij.psi.infos.MethodCandidateInfo; import com.intellij.psi.util.*; import com.intellij.refactoring.util.RefactoringChangeUtil; import com.intellij.ui.ColorUtil; +import com.intellij.util.VisibilityUtil; import com.intellij.util.containers.MostlySingularMultiMap; import com.intellij.util.ui.UIUtil; import com.intellij.xml.util.XmlStringUtil; @@ -100,9 +101,9 @@ public class HighlightMethodUtil { int superAccessLevel = PsiUtil.getAccessLevel(superMethod.getModifierList()); if (accessLevel < superAccessLevel) { String description = JavaErrorMessages.message("weaker.privileges", - createClashMethodMessage(method, superMethod, true), - accessModifier, - PsiUtil.getAccessModifier(superAccessLevel)); + createClashMethodMessage(method, superMethod, true), + VisibilityUtil.toPresentableText(accessModifier), + PsiUtil.getAccessModifier(superAccessLevel)); TextRange textRange; if (includeRealPositionInfo) { if (modifierList.hasModifierProperty(PsiModifier.PACKAGE_LOCAL)) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/visibility/AccessCanBeTightenedInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/visibility/AccessCanBeTightenedInspection.java index 78a0c28c39ef..d89c92bd8b77 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/visibility/AccessCanBeTightenedInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/visibility/AccessCanBeTightenedInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 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. @@ -32,6 +32,7 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.usageView.UsageInfo; import com.intellij.util.Processor; +import com.intellij.util.VisibilityUtil; import com.intellij.util.containers.ContainerUtil; import com.siyeh.ig.fixes.ChangeModifierFix; import com.siyeh.ig.psiutils.MethodUtils; @@ -185,12 +186,14 @@ class AccessCanBeTightenedInspection extends BaseJavaBatchLocalInspectionTool { log(member.getName() + " unused; ignore"); return; // do not propose private for unused method } + int max = maxLevel.get(); if (max == PsiUtil.ACCESS_LEVEL_PRIVATE && memberClass == null) { max = suggestPackageLocal(member); } - log(member.getName()+": effective level is '" + PsiUtil.getAccessModifier(max) + "'"); + String maxModifier = PsiUtil.getAccessModifier(max); + log(member.getName() + ": effective level is '" + maxModifier + "'"); if (max < currentLevel) { if (max == PsiUtil.ACCESS_LEVEL_PACKAGE_LOCAL && member instanceof PsiClass && childMembersAreUsedOutsideMyPackage.contains(member)) { @@ -205,7 +208,7 @@ class AccessCanBeTightenedInspection extends BaseJavaBatchLocalInspectionTool { } }); assert toHighlight != null : member +" ; " + ((PsiNameIdentifierOwner)member).getNameIdentifier() + "; "+ memberModifierList.getText(); - myHolder.registerProblem(toHighlight, "Access can be "+PsiUtil.getAccessModifier(max), new ChangeModifierFix(PsiUtil.getAccessModifier(max))); + myHolder.registerProblem(toHighlight, "Access can be " + VisibilityUtil.toPresentableText(maxModifier), new ChangeModifierFix(maxModifier)); } } diff --git a/java/java-impl/src/com/intellij/codeInspection/deadCode/DummyEntryPointsPresentation.java b/java/java-impl/src/com/intellij/codeInspection/deadCode/DummyEntryPointsPresentation.java index 20465b1fe919..c9be5d934b3d 100644 --- a/java/java-impl/src/com/intellij/codeInspection/deadCode/DummyEntryPointsPresentation.java +++ b/java/java-impl/src/com/intellij/codeInspection/deadCode/DummyEntryPointsPresentation.java @@ -15,6 +15,7 @@ */ package com.intellij.codeInspection.deadCode; +import com.intellij.codeInspection.CommonProblemDescriptor; import com.intellij.codeInspection.GlobalJavaInspectionContext; import com.intellij.codeInspection.InspectionsBundle; import com.intellij.codeInspection.ex.*; @@ -39,7 +40,7 @@ public class DummyEntryPointsPresentation extends UnusedDeclarationPresentation } @Override - public QuickFixAction[] getQuickFixes(@NotNull final RefEntity[] refElements) { + public QuickFixAction[] getQuickFixes(@NotNull final RefEntity[] refElements, CommonProblemDescriptor[] allowedDescriptors) { if (myQuickFixActions == null) { myQuickFixActions = new QuickFixAction[]{new MoveEntriesToSuspicious(getToolWrapper())}; } diff --git a/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationPresentation.java b/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationPresentation.java index 76602ca1c660..ad13714f3cc7 100644 --- a/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationPresentation.java +++ b/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationPresentation.java @@ -145,7 +145,7 @@ public class UnusedDeclarationPresentation extends DefaultInspectionToolPresenta } @Override - public QuickFixAction[] getQuickFixes(@NotNull final RefEntity[] refElements) { + public QuickFixAction[] getQuickFixes(@NotNull final RefEntity[] refElements, CommonProblemDescriptor[] allowedDescriptors) { return myQuickFixActions; } diff --git a/java/java-impl/src/com/intellij/refactoring/move/moveMembers/MoveJavaMemberHandler.java b/java/java-impl/src/com/intellij/refactoring/move/moveMembers/MoveJavaMemberHandler.java index 950532ebe61b..43bcfcc7d455 100644 --- a/java/java-impl/src/com/intellij/refactoring/move/moveMembers/MoveJavaMemberHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/move/moveMembers/MoveJavaMemberHandler.java @@ -143,7 +143,7 @@ public class MoveJavaMemberHandler implements MoveMemberHandler { } protected static boolean hasMethod(PsiClass targetClass, PsiMethod method) { - PsiMethod[] targetClassMethods = targetClass.getMethods(); + PsiMethod[] targetClassMethods = targetClass.findMethodsByName(method.getName(), true); for (PsiMethod candidate : targetClassMethods) { if (candidate != method && MethodSignatureUtil.areSignaturesEqual(method.getSignature(PsiSubstitutor.EMPTY), @@ -155,15 +155,8 @@ public class MoveJavaMemberHandler implements MoveMemberHandler { } protected static boolean hasField(PsiClass targetClass, PsiField field) { - String fieldName = field.getName(); - PsiField[] targetClassFields = targetClass.getFields(); - for (PsiField candidate : targetClassFields) { - if (candidate != field && - fieldName.equals(candidate.getName())) { - return true; - } - } - return false; + final PsiField fieldByName = targetClass.findFieldByName(field.getName(), true); + return fieldByName != null && fieldByName != field; } @Override diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java index 1602daa653e1..302bfa32f82d 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 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. @@ -24,6 +24,7 @@ import com.intellij.openapi.roots.LanguageLevelProjectExtension; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.java.LanguageLevel; @@ -123,7 +124,11 @@ public final class PsiUtil extends PsiUtilCore { if (type instanceof PsiCapturedWildcardType) { final PsiType upperBound = ((PsiCapturedWildcardType)type).getUpperBound(); if (upperBound instanceof PsiClassType) { - String classText = "class I {}"; + final PsiClass resolved = ((PsiClassType)upperBound).resolve(); + final PsiFile containingFile = resolved != null ? resolved.getContainingFile() : null; + final String packageName = containingFile instanceof PsiClassOwner ? ((PsiClassOwner)containingFile).getPackageName() : null; + String classText = StringUtil.isEmptyOrSpaces(packageName) ? "" : "package " +packageName + ";\n "; + classText += "class I {}"; final PsiJavaFile file = (PsiJavaFile)PsiFileFactory.getInstance(expression.getProject()).createFileFromText("inference_dummy.java", JavaLanguage.INSTANCE, classText); final PsiTypeParameter freshParameter = file.getClasses()[0].getTypeParameters()[0]; @@ -348,10 +353,11 @@ public final class PsiUtil extends PsiUtilCore { } @PsiModifier.ModifierConstant - @Nullable + @NotNull public static String getAccessModifier(@AccessLevel int accessLevel) { + assert accessLevel > 0 && accessLevel <= accessModifiers.length : accessLevel; @SuppressWarnings("UnnecessaryLocalVariable") @PsiModifier.ModifierConstant - final String modifier = accessLevel > accessModifiers.length ? null : accessModifiers[accessLevel - 1]; + final String modifier = accessModifiers[accessLevel - 1]; return modifier; } diff --git a/java/java-psi-api/src/com/intellij/util/VisibilityUtil.java b/java/java-psi-api/src/com/intellij/util/VisibilityUtil.java index 04aa459203ec..970c25f08725 100644 --- a/java/java-psi-api/src/com/intellij/util/VisibilityUtil.java +++ b/java/java-psi-api/src/com/intellij/util/VisibilityUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 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. @@ -31,6 +31,7 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; public class VisibilityUtil { @@ -107,7 +108,8 @@ public class VisibilityUtil { } @Nls - public static String getVisibilityStringToDisplay(PsiMember member) { + @NotNull + public static String getVisibilityStringToDisplay(@NotNull PsiMember member) { if (member.hasModifierProperty(PsiModifier.PUBLIC)) { return toPresentableText(PsiModifier.PUBLIC); } @@ -120,7 +122,8 @@ public class VisibilityUtil { return toPresentableText(PsiModifier.PACKAGE_LOCAL); } - public static String toPresentableText(@PsiModifier.ModifierConstant String modifier) { + @NotNull + public static String toPresentableText(@PsiModifier.ModifierConstant @NotNull String modifier) { return PsiBundle.visibilityPresentation(modifier); } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/StrictSubtypingConstraint.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/StrictSubtypingConstraint.java index 9177da7586f5..6a5d70ae6475 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/StrictSubtypingConstraint.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/StrictSubtypingConstraint.java @@ -150,7 +150,8 @@ public class StrictSubtypingConstraint implements ConstraintFormula { final PsiType tSubstituted = tSubstitutor.substitute(parameter); final PsiType sSubstituted = sSubstitutor.substituteWithBoundsPromotion(parameter); if (tSubstituted == null ^ sSubstituted == null) { - return false; + session.setErased(); + return true; } constraints.add(new SubtypingConstraint(tSubstituted, sSubstituted)); } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeCompatibilityConstraint.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeCompatibilityConstraint.java index f86c78c2726a..368431f65696 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeCompatibilityConstraint.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeCompatibilityConstraint.java @@ -75,7 +75,7 @@ public class TypeCompatibilityConstraint implements ConstraintFormula { final PsiClassType.ClassResolveResult sResult = ((PsiClassType)s).resolveGenerics(); final PsiClass tClass = tResult.getElement(); final PsiClass sClass = sResult.getElement(); - if (tClass != null && sClass != null) { + if (tClass != null && sClass != null && !(sClass instanceof InferenceVariable)) { final PsiSubstitutor sSubstitutor = TypeConversionUtil.getClassSubstitutor(tClass, sClass, sResult.getSubstitutor()); if (sSubstitutor != null) { if (PsiUtil.isRawSubstitutor(tClass, sSubstitutor)) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/AccessLevelClash.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/AccessLevelClash.java index 7fa62b8aa01e..95f045d64325 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/AccessLevelClash.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/AccessLevelClash.java @@ -4,7 +4,7 @@ interface i { } public class a implements i { - void ff() {} + void ff() {} } class ai implements i { public int ff() { return 0;} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/StaticOverride.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/StaticOverride.java index be1914e180dc..0e946e6e8e15 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/StaticOverride.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/StaticOverride.java @@ -34,7 +34,7 @@ interface i2 { class weak { void f1() {} } -class a2 extends weak implements i { +class a2 extends weak implements i { } class a3 { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/CapturedWildcardPackageLocalAccess.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/CapturedWildcardPackageLocalAccess.java new file mode 100644 index 000000000000..edea5438c654 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/CapturedWildcardPackageLocalAccess.java @@ -0,0 +1,8 @@ +import java.util.Map; + +class Test { + void f() {} + void m(Map map, String name) { + map.get(name).f(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/IDEA56754.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/IDEA56754.java index 34c407f3dfde..ee489aab75ad 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/IDEA56754.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/IDEA56754.java @@ -1,5 +1,5 @@ class Foo { public T bar(Class type, String str) { - return Enum.valueOf(type, str); + return Enum.valueOf(type, str); } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/IDEA150688.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/IDEA150688.java new file mode 100644 index 000000000000..258b71b33649 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/IDEA150688.java @@ -0,0 +1,11 @@ +abstract class Issue { + private void enumUsage(Class enumClass) { + passEnumClass(enumClass); + } + + abstract > void passEnumClass(Class var1); + + void a(Enum enumValue) { + Enum constant = Enum.valueOf(enumValue.getClass(), "name"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/UncheckedWarningConvertingToInferenceVariable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/UncheckedWarningConvertingToInferenceVariable.java new file mode 100644 index 000000000000..9f8d7029043a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/UncheckedWarningConvertingToInferenceVariable.java @@ -0,0 +1,11 @@ +import java.util.List; + +class Test { + void m(Class clazz){ + List data = foo(clazz); + } + + public static T foo(Class clazz) { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/UncheckedWarningDuringStrictSubtyping.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/UncheckedWarningDuringStrictSubtyping.java new file mode 100644 index 000000000000..fc79f089ce38 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/UncheckedWarningDuringStrictSubtyping.java @@ -0,0 +1,21 @@ +import java.util.List; + +interface FacetId {} + +abstract class One { + public abstract , C extends One> void findFacetType(FacetId typeId); + private void addSubFacet(FacetId underlyingType) { + findFacetType(underlyingType); + } +} + +class Bar {} +class Foo {} +class Main { + void foo(Bar a) { + m(a); + } + + , C> void m(Bar a) { + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/IncludeConstraintsWhenParentMethodIsDuringCalculation.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/IncludeConstraintsWhenParentMethodIsDuringCalculation.java index 7d5c0557acf7..8869943add57 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/IncludeConstraintsWhenParentMethodIsDuringCalculation.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/IncludeConstraintsWhenParentMethodIsDuringCalculation.java @@ -12,7 +12,10 @@ class Test { SuperFoo ls = foo(() -> new Foo<>()); +upper bounds: Object, Number +inference variable X has incompatible bounds: + equality constraints: R +upper bounds: Number">() -> new Foo<>()); SuperFoo li = foo(() -> new Foo<>()); SuperFoo lw = foo(() -> new Foo<>()); } \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/moveMembers/existingFieldInSuper/after/A.java b/java/java-tests/testData/refactoring/moveMembers/existingFieldInSuper/after/A.java new file mode 100644 index 000000000000..810415096682 --- /dev/null +++ b/java/java-tests/testData/refactoring/moveMembers/existingFieldInSuper/after/A.java @@ -0,0 +1,12 @@ +class One { + static boolean truth = true; + static void important() { + System.out.println(1); + } +} +class A extends One { + Two() { + important(); + System.out.println(truth); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/moveMembers/existingFieldInSuper/after/B.java b/java/java-tests/testData/refactoring/moveMembers/existingFieldInSuper/after/B.java new file mode 100644 index 000000000000..06e97dde4a42 --- /dev/null +++ b/java/java-tests/testData/refactoring/moveMembers/existingFieldInSuper/after/B.java @@ -0,0 +1,6 @@ +public class B { + static boolean truth = false; + static void important() { + System.out.println(2); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/moveMembers/existingFieldInSuper/before/A.java b/java/java-tests/testData/refactoring/moveMembers/existingFieldInSuper/before/A.java new file mode 100644 index 000000000000..1b8822c0e01b --- /dev/null +++ b/java/java-tests/testData/refactoring/moveMembers/existingFieldInSuper/before/A.java @@ -0,0 +1,12 @@ +class One { + static boolean truth = true; + static void important() { + System.out.println(1); + } +} +class A extends One { + Two() { + important(); + System.out.println(truth); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/moveMembers/existingFieldInSuper/before/B.java b/java/java-tests/testData/refactoring/moveMembers/existingFieldInSuper/before/B.java new file mode 100644 index 000000000000..06e97dde4a42 --- /dev/null +++ b/java/java-tests/testData/refactoring/moveMembers/existingFieldInSuper/before/B.java @@ -0,0 +1,6 @@ +public class B { + static boolean truth = false; + static void important() { + System.out.println(2); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GenericsHighlighting8Test.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GenericsHighlighting8Test.java index 5b7d17ee603c..ae6977d8a644 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GenericsHighlighting8Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GenericsHighlighting8Test.java @@ -966,4 +966,8 @@ public class GenericsHighlighting8Test extends LightDaemonAnalyzerTestCase { public void testCapturedWildcardWithPrimitiveTypesChecks() throws Exception { doTest(); } + + public void testCapturedWildcardPackageLocalAccess() throws Exception { + doTest(); + } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java index 9544b9e6ed87..fb33f7c9af73 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java @@ -379,6 +379,18 @@ public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase doTest(); } + public void testUncheckedWarningConvertingToInferenceVariable() throws Exception { + doTest(); + } + + public void testUncheckedWarningDuringStrictSubtyping() throws Exception { + doTest(); + } + + public void testIDEA150688() throws Exception { + doTest(); + } + private void doTest() throws Exception { doTest(false); } diff --git a/java/java-tests/testSrc/com/intellij/refactoring/MoveMembersTest.java b/java/java-tests/testSrc/com/intellij/refactoring/MoveMembersTest.java index 51f896f1c9d2..7a8c549adf4b 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/MoveMembersTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/MoveMembersTest.java @@ -154,6 +154,17 @@ public class MoveMembersTest extends MultiFileTestCase { } } + public void testExistingFieldInSuper() throws Exception { + try { + doTest("B", "A", 0, 1); + fail("conflict expected"); + } + catch (BaseRefactoringProcessor.ConflictsInTestsException e) { + assertEquals("Field truth already exists in the target class.\n" + + "Method important() already exists in the target class.", e.getMessage()); + } + } + public void testInnerToInterface() throws Exception { doTest("A", "B", 0); } diff --git a/java/java-tests/testSrc/com/intellij/refactoring/RenameMethodMultiTest.java b/java/java-tests/testSrc/com/intellij/refactoring/RenameMethodMultiTest.java index 18f0cbe22483..4247b3a8ab44 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/RenameMethodMultiTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/RenameMethodMultiTest.java @@ -19,7 +19,6 @@ import com.intellij.JavaTestUtil; import com.intellij.lang.java.JavaLanguage; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiDocumentManager; @@ -71,9 +70,9 @@ public class RenameMethodMultiTest extends MultiFileTestCase { doTest("p.B", "void method()", "finalMethod"); } catch (BaseRefactoringProcessor.ConflictsInTestsException e) { - Assert.assertEquals("Renaming method will override final \"method A.finalMethod()\"\n" + - "Method finalMethod() will override \n" + - "a method of the base class p.A", e.getMessage()); + Assert.assertEquals("Method finalMethod() will override \n" + + "a method of the base class p.A\n" + + "Renaming method will override final \"method A.finalMethod()\"", e.getMessage()); return; } fail("Conflicts were not found"); diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleBuildTarget.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleBuildTarget.java index 05c185862399..8d3cd7931ebb 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleBuildTarget.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleBuildTarget.java @@ -18,6 +18,7 @@ package org.jetbrains.jps.incremental; import com.intellij.openapi.util.io.FileUtil; import com.intellij.util.Consumer; import com.intellij.util.SmartList; +import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -135,6 +136,7 @@ public final class ModuleBuildTarget extends JVMModuleBuildTarget excludedRootProviders = JpsServiceManager.getInstance().getExtensions(ExcludedJavaSourceRootProvider.class); final Set moduleExcludes = new THashSet(FileUtil.FILE_HASHING_STRATEGY); moduleExcludes.addAll(index.getModuleExcludes(myModule)); + final JpsJavaCompilerConfiguration compilerConfig = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(myModule.getProject()); roots_loop: for (JpsTypedModuleSourceRoot sourceRoot : myModule.getSourceRoots(type)) { @@ -147,8 +149,19 @@ public final class ModuleBuildTarget extends JVMModuleBuildTarget excludes = computeRootExcludes(sourceRoot.getFile(), index); + final ProcessorConfigProfile profile = compilerConfig.getAnnotationProcessingProfile(myModule); + if (profile.isEnabled()) { + final File outputDir = ProjectPaths.getAnnotationProcessorGeneratedSourcesOutputDir(myModule, JavaSourceRootType.TEST_SOURCE == sourceRoot.getRootType(), profile); + if (outputDir != null && FileUtil.isAncestor(sourceRoot.getFile(), outputDir, true)) { + excludes = ContainerUtil.newTroveSet(FileUtil.FILE_HASHING_STRATEGY, excludes); + excludes.add(outputDir); + } + } + + roots.add(new JavaSourceRootDescriptor(sourceRoot.getFile(), this, false, false, packagePrefix, excludes)); } return roots; } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/BuildFSState.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/BuildFSState.java index b7caa0b575f7..7cd3b4685c4a 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/BuildFSState.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/BuildFSState.java @@ -138,11 +138,18 @@ public class BuildFSState { return false; } final CompileScope scope = context.getScope(); + final BuildRootIndex rootIndex = context.getProjectDescriptor().getBuildRootIndex(); try { delta.lockData(); for (Set files : delta.getSourcesToRecompile().values()) { + files_loop: for (File file : files) { if ((getEventRegistrationStamp(file) > targetBuildStart || FileSystemUtil.lastModified(file) > targetBuildStart) && scope.isAffected(target, file)) { + for (BuildRootDescriptor rd : rootIndex.findAllParentDescriptors(file, context)) { + if (rd.isGenerated()) { // do not send notification for generated sources + continue files_loop; + } + } return true; } } diff --git a/json/src/com/intellij/json/JsonBundle.properties b/json/src/com/intellij/json/JsonBundle.properties index b73ef1f4fddf..516c91f6ed26 100644 --- a/json/src/com/intellij/json/JsonBundle.properties +++ b/json/src/com/intellij/json/JsonBundle.properties @@ -37,3 +37,6 @@ quickfix.add.double.quotes.desc=Wrap with double quotes surround.with.object.literal.desc=object literal json.template.context.type=JSON + +#json schema +json.schema.add.schema.chooser.title=Select JSON Schema File \ No newline at end of file diff --git a/json/src/com/jetbrains/jsonSchema/JsonSchemaConfigurable.java b/json/src/com/jetbrains/jsonSchema/JsonSchemaConfigurable.java index 4c005900c3f0..880b6a7abd91 100644 --- a/json/src/com/jetbrains/jsonSchema/JsonSchemaConfigurable.java +++ b/json/src/com/jetbrains/jsonSchema/JsonSchemaConfigurable.java @@ -2,11 +2,11 @@ package com.jetbrains.jsonSchema; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.NamedConfigurable; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.CollectConsumer; import com.jetbrains.jsonSchema.impl.JsonSchemaReader; import org.jetbrains.annotations.Nls; @@ -22,18 +22,18 @@ import java.io.IOException; */ public class JsonSchemaConfigurable extends NamedConfigurable { private final Project myProject; - @NotNull private final VirtualFile myFile; + @NotNull private final String mySchemaFilePath; @NotNull private final JsonSchemaMappingsConfigurationBase.SchemaInfo mySchema; @Nullable private final Runnable myTree; private JsonSchemaMappingsView myView; private String myDisplayName; public JsonSchemaConfigurable(Project project, - @NotNull VirtualFile schemaFile, @NotNull JsonSchemaMappingsConfigurationBase.SchemaInfo schema, + @NotNull String schemaFilePath, @NotNull JsonSchemaMappingsConfigurationBase.SchemaInfo schema, @Nullable Runnable updateTree) { super(true, updateTree); myProject = project; - myFile = schemaFile; + mySchemaFilePath = schemaFilePath; mySchema = schema; myTree = updateTree; myDisplayName = mySchema.getName(); @@ -98,27 +98,32 @@ public class JsonSchemaConfigurable extends NamedConfigurable collectConsumer = new CollectConsumer<>(); - final File file = new File(myProject.getBasePath(), myView.getSchemaSubPath()); - try { - if (!JsonSchemaReader.isJsonSchema(FileUtil.loadFile(file), collectConsumer)) { - final String message; - if (collectConsumer.getResult().isEmpty()) message = "Can not read JSON schema from file (Unknown reason)"; - else message = "Can not read JSON schema from file: " + StringUtil.join(collectConsumer.getResult(), "; "); - throw new ConfigurationException(message); - } - } - catch (IOException e) { - throw new ConfigurationException("Can not read JSON schema from file: " + e.getMessage()); + final CollectConsumer collectConsumer = new CollectConsumer<>(); + final File file = new File(myProject.getBasePath(), myView.getSchemaSubPath()); + try { + if (!JsonSchemaReader.isJsonSchema(FileUtil.loadFile(file), collectConsumer)) { + final String message; + if (collectConsumer.getResult().isEmpty()) message = "Can not read JSON schema from file (Unknown reason)"; + else message = "Can not read JSON schema from file: " + StringUtil.join(collectConsumer.getResult(), "; "); + logErrorForUser(message); + throw new ConfigurationException(message); } } + catch (IOException e) { + final String message = "Can not read JSON schema from file: " + e.getMessage(); + logErrorForUser(message); + throw new ConfigurationException(message); + } + } + + private void logErrorForUser(@NotNull final String error) { + JsonSchemaReader.ERRORS_NOTIFICATION.createNotification(error, MessageType.ERROR).notify(myProject); } @Override public void reset() { if (myView == null) return; - myView.setItems(myFile, mySchema.getPatterns()); + myView.setItems(mySchemaFilePath, mySchema.getPatterns()); setDisplayName(mySchema.getName()); } diff --git a/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsConfigurable.java b/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsConfigurable.java index 06e870840274..0d023655316f 100644 --- a/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsConfigurable.java +++ b/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsConfigurable.java @@ -1,5 +1,6 @@ package com.jetbrains.jsonSchema; +import com.intellij.json.JsonBundle; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -12,6 +13,7 @@ import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.ui.MasterDetailsComponent; +import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; @@ -130,7 +132,8 @@ public class JsonSchemaMappingsConfigurable extends MasterDetailsComponent imple private void addProjectSchema() { final VirtualFile file = - FileChooser.chooseFile(FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor(), myProject, null); + FileChooser.chooseFile(FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor().withTitle( + JsonBundle.message("json.schema.add.schema.chooser.title")), myProject, null); if (file != null) { final String relativePath = VfsUtil.getRelativePath(file, myProject.getBaseDir()); if (relativePath == null) { @@ -140,6 +143,7 @@ public class JsonSchemaMappingsConfigurable extends MasterDetailsComponent imple final JsonSchemaChecker importer = new JsonSchemaChecker(file, true); if (!importer.checkSchemaFile()) { if (!StringUtil.isEmptyOrSpaces(importer.getError())) { + JsonSchemaReader.ERRORS_NOTIFICATION.createNotification(importer.getError(), MessageType.ERROR).notify(myProject); Messages.showErrorDialog(myProject, importer.getError(), READ_JSON_SCHEMA); } return; @@ -194,7 +198,8 @@ public class JsonSchemaMappingsConfigurable extends MasterDetailsComponent imple }*/ private void addCreatedMappings(@NotNull VirtualFile schemaFile, @NotNull final JsonSchemaMappingsConfigurationBase.SchemaInfo info) { - final MyNode node = new MyNode(new JsonSchemaConfigurable(myProject, schemaFile, info, myTreeUpdater), info.isApplicationLevel()); + final MyNode node = new MyNode(new JsonSchemaConfigurable(myProject, FileUtil.toSystemDependentName(schemaFile.getPath()), + info, myTreeUpdater), info.isApplicationLevel()); addNode(node, myRoot); selectNodeInTree(node, true); } @@ -206,11 +211,8 @@ public class JsonSchemaMappingsConfigurable extends MasterDetailsComponent imple final List list = getStoredList(); for (JsonSchemaMappingsConfigurationBase.SchemaInfo info : list) { - final String[] parts = info.getRelativePathToSchema().replace('\\', '/').split("/"); - final VirtualFile schemaFile = VfsUtil.findRelativeFile(myProject.getBaseDir(), parts); - if (schemaFile != null) { - myRoot.add(new MyNode(new JsonSchemaConfigurable(myProject, schemaFile, info, myTreeUpdater), info.isApplicationLevel())); - } + myRoot.add(new MyNode(new JsonSchemaConfigurable(myProject, new File(myProject.getBasePath(), info.getRelativePathToSchema()).getPath(), + info, myTreeUpdater), info.isApplicationLevel())); } ((DefaultTreeModel) myTree.getModel()).reload(myRoot); if (myRoot.children().hasMoreElements()) { diff --git a/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsProjectConfiguration.java b/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsProjectConfiguration.java index d132be205846..d9f82ea77ca0 100644 --- a/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsProjectConfiguration.java +++ b/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsProjectConfiguration.java @@ -2,9 +2,16 @@ package com.jetbrains.jsonSchema; import com.intellij.openapi.components.*; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.xmlb.annotations.Transient; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.File; +import java.util.HashMap; +import java.util.Map; /** * @author Irina.Chernushina on 2/2/2016. @@ -17,6 +24,8 @@ import java.io.File; ) public class JsonSchemaMappingsProjectConfiguration extends JsonSchemaMappingsConfigurationBase { private Project myProject; + @Transient + private final Map mySchemaFiles = new HashMap<>(); public static JsonSchemaMappingsProjectConfiguration getInstance(@NotNull final Project project) { return ServiceManager.getService(project, JsonSchemaMappingsProjectConfiguration.class); @@ -33,4 +42,48 @@ public class JsonSchemaMappingsProjectConfiguration extends JsonSchemaMappingsCo public File convertToAbsoluteFile(@NotNull String path) { return myProject.getBasePath() == null ? new File(path) : new File(myProject.getBasePath(), path); } + + public boolean isRegisteredSchemaFile(@NotNull VirtualFile file) { + return mySchemaFiles.containsKey(file); + } + + @Override + public void setState(@NotNull Map state) { + super.setState(state); + recalculateSchemaFiles(); + } + + @Override + public void addSchema(@NotNull SchemaInfo info) { + super.addSchema(info); + recalculateSchemaFiles(); + } + + @Override + public void removeSchema(@NotNull SchemaInfo info) { + super.removeSchema(info); + recalculateSchemaFiles(); + } + + @Override + public void loadState(JsonSchemaMappingsConfigurationBase state) { + super.loadState(state); + recalculateSchemaFiles(); + } + + private void recalculateSchemaFiles() { + mySchemaFiles.clear(); + if (myProject == null || myProject.getBaseDir() == null) return; + + for (JsonSchemaMappingsConfigurationBase.SchemaInfo info : myState.values()) { + final String pathToSchema = FileUtil.toSystemIndependentName(info.getRelativePathToSchema()); + final VirtualFile schemaFile = VfsUtil.findRelativeFile(myProject.getBaseDir(), pathToSchema); + if (schemaFile != null) mySchemaFiles.put(schemaFile, info); + } + } + + @Nullable + public SchemaInfo getSchemaBySchemaFile(@NotNull final VirtualFile file) { + return mySchemaFiles.get(file); + } } diff --git a/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsView.java b/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsView.java index ff753e7a506b..eb43bee8ea20 100644 --- a/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsView.java +++ b/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsView.java @@ -1,5 +1,6 @@ package com.jetbrains.jsonSchema; +import com.intellij.json.JsonBundle; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonShortcuts; import com.intellij.openapi.application.ModalityState; @@ -81,7 +82,7 @@ public class JsonSchemaMappingsView { final JPanel wrapper = new JPanel(new BorderLayout()); final JPanel wrapper2 = new JPanel(new BorderLayout()); mySchemaField = new TextFieldWithBrowseButton(); - SwingHelper.installFileCompletionAndBrowseDialog(myProject, mySchemaField, "Select JSON Schema File", + SwingHelper.installFileCompletionAndBrowseDialog(myProject, mySchemaField, JsonBundle.message("json.schema.add.schema.chooser.title"), FileChooserDescriptorFactory.createSingleFileDescriptor()); attachNavigateToSchema(); final JBLabel label = new JBLabel("JSON schema file:"); @@ -127,7 +128,7 @@ public class JsonSchemaMappingsView { return items; } - public void setItems(VirtualFile schemaFile, final List data) { + public void setItems(String schemaFilePath, final List data) { /*final ArrayList copy = new ArrayList(); if (myProject != null && myProject.getBasePath() != null) { for (JsonSchemaMappingsConfigurationBase.Item item : copy) { @@ -141,8 +142,9 @@ public class JsonSchemaMappingsView { } else { copy.addAll(data); }*/ - mySchemaField.setText(FileUtil.toSystemDependentName(schemaFile.getPath())); - myTableView.setModelAndUpdateColumns(new ListTableModel(createColumns(), new ArrayList(data))); + mySchemaField.setText(schemaFilePath); + myTableView.setModelAndUpdateColumns( + new ListTableModel(createColumns(), new ArrayList(data))); } public String getSchemaSubPath() { diff --git a/json/src/com/jetbrains/jsonSchema/JsonSchemaRefactoringListenerProvider.java b/json/src/com/jetbrains/jsonSchema/JsonSchemaRefactoringListenerProvider.java new file mode 100644 index 000000000000..8263e5f28717 --- /dev/null +++ b/json/src/com/jetbrains/jsonSchema/JsonSchemaRefactoringListenerProvider.java @@ -0,0 +1,66 @@ +/* + * 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://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.jetbrains.jsonSchema; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiUtilBase; +import com.intellij.refactoring.listeners.RefactoringElementListener; +import com.intellij.refactoring.listeners.RefactoringElementListenerProvider; +import com.intellij.refactoring.listeners.UndoRefactoringElementAdapter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Irina.Chernushina on 2/17/2016. + */ +public class JsonSchemaRefactoringListenerProvider implements RefactoringElementListenerProvider { + @Nullable + @Override + public RefactoringElementListener getListener(PsiElement element) { + if (element == null) { + return null; + } + VirtualFile fileAtElement = PsiUtilBase.asVirtualFile(element); + if (fileAtElement == null) { + return null; + } + Project project = element.getProject(); + final JsonSchemaMappingsProjectConfiguration configuration = JsonSchemaMappingsProjectConfiguration.getInstance(project); + final JsonSchemaMappingsConfigurationBase.SchemaInfo schemaInfo = configuration.getSchemaBySchemaFile(fileAtElement); + if (schemaInfo != null && project.getBaseDir() != null) { + return new UndoRefactoringElementAdapter() { + @Override + protected void refactored(@NotNull PsiElement element, @Nullable String oldQualifiedName) { + VirtualFile newFile = PsiUtilBase.asVirtualFile(element); + if (newFile != null) { + final String relativePath = VfsUtil.getRelativePath(newFile, project.getBaseDir()); + if (relativePath != null) { + configuration.removeSchema(schemaInfo); + final JsonSchemaMappingsConfigurationBase.SchemaInfo newSchema = + new JsonSchemaMappingsConfigurationBase.SchemaInfo(schemaInfo.getName(), relativePath, schemaInfo.isApplicationLevel(), + schemaInfo.getPatterns()); + configuration.addSchema(newSchema); + } + } + } + }; + } + return null; + } +} diff --git a/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaSelfProviderFactory.java b/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaSelfProviderFactory.java index c204eccd3e3f..c33ca8030d0a 100644 --- a/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaSelfProviderFactory.java +++ b/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaSelfProviderFactory.java @@ -18,11 +18,8 @@ package com.jetbrains.jsonSchema.extension; import com.intellij.json.JsonFileType; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ResourceUtil; -import com.jetbrains.jsonSchema.JsonSchemaMappingsConfigurationBase; import com.jetbrains.jsonSchema.JsonSchemaMappingsProjectConfiguration; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -31,7 +28,6 @@ import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.net.URL; -import java.util.Map; /** * @author Irina.Chernushina on 2/16/2016. @@ -46,15 +42,7 @@ public class JsonSchemaSelfProviderFactory implements JsonSchemaProviderFactory @Override public boolean isAvailable(@NotNull VirtualFile file) { if (project == null || !JsonFileType.INSTANCE.equals(file.getFileType())) return false; - final JsonSchemaMappingsProjectConfiguration configuration = JsonSchemaMappingsProjectConfiguration.getInstance(project); - final Map map = configuration.getStateMap(); - for (JsonSchemaMappingsConfigurationBase.SchemaInfo info : map.values()) { - final String pathToSchema = FileUtil.toSystemIndependentName(info.getRelativePathToSchema()); - if (! file.getPath().endsWith(pathToSchema)) continue; - final VirtualFile schemaFile = VfsUtil.findRelativeFile(project.getBaseDir(), pathToSchema); - if (file.equals(schemaFile)) return true; - } - return false; + return JsonSchemaMappingsProjectConfiguration.getInstance(project).isRegisteredSchemaFile(file); } @Nullable diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaReader.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaReader.java index f15efbcd8e0a..5b91e87ee8d8 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaReader.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaReader.java @@ -4,6 +4,7 @@ import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; +import com.intellij.notification.NotificationGroup; import com.intellij.util.Consumer; import com.intellij.util.ThrowablePairConsumer; import org.jetbrains.annotations.NotNull; @@ -17,6 +18,8 @@ import java.util.*; * @author Irina.Chernushina on 8/27/2015. */ public class JsonSchemaReader { + public static final NotificationGroup ERRORS_NOTIFICATION = NotificationGroup.logOnlyGroup("JSON Schema"); + public JsonSchemaObject read(@NotNull final Reader reader) throws IOException { final JsonReader in = new JsonReader(reader); diff --git a/platform/analysis-api/src/com/intellij/codeHighlighting/HighlightDisplayLevel.java b/platform/analysis-api/src/com/intellij/codeHighlighting/HighlightDisplayLevel.java index 5a19b1271032..ff9971630250 100644 --- a/platform/analysis-api/src/com/intellij/codeHighlighting/HighlightDisplayLevel.java +++ b/platform/analysis-api/src/com/intellij/codeHighlighting/HighlightDisplayLevel.java @@ -180,7 +180,9 @@ public class HighlightDisplayLevel { @Override public void paintIcon(Component c, Graphics g, int x, int y) { g.setColor(getColor()); - g.fillRect(x + 2, y + 2, 10, 10); + int shift = JBUI.scale(2); + int size = JBUI.scale(10); + g.fillRect(x + shift, y + shift, size, size); } @Override diff --git a/platform/analysis-api/src/com/intellij/codeInspection/SuppressIntentionAction.java b/platform/analysis-api/src/com/intellij/codeInspection/SuppressIntentionAction.java index 9b7551beb33f..bd92a55295a0 100644 --- a/platform/analysis-api/src/com/intellij/codeInspection/SuppressIntentionAction.java +++ b/platform/analysis-api/src/com/intellij/codeInspection/SuppressIntentionAction.java @@ -100,6 +100,10 @@ public abstract class SuppressIntentionAction implements Iconable, IntentionActi */ public abstract boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element); + public boolean isSuppressAll() { + return false; + } + @Nullable private static PsiElement getElement(@NotNull Editor editor, @NotNull PsiFile file) { CaretModel caretModel = editor.getCaretModel(); diff --git a/platform/analysis-api/src/com/intellij/codeInspection/SuppressQuickFix.java b/platform/analysis-api/src/com/intellij/codeInspection/SuppressQuickFix.java index e0fff380f57c..6c4d53750775 100644 --- a/platform/analysis-api/src/com/intellij/codeInspection/SuppressQuickFix.java +++ b/platform/analysis-api/src/com/intellij/codeInspection/SuppressQuickFix.java @@ -22,4 +22,6 @@ import org.jetbrains.annotations.NotNull; public interface SuppressQuickFix extends LocalQuickFix { SuppressQuickFix[] EMPTY_ARRAY = new SuppressQuickFix[0]; boolean isAvailable(@NotNull final Project project, @NotNull final PsiElement context); + + boolean isSuppressAll(); } diff --git a/platform/analysis-api/src/com/intellij/codeInspection/reference/RefEntity.java b/platform/analysis-api/src/com/intellij/codeInspection/reference/RefEntity.java index 3c7d66018e1b..fab170c4cae7 100644 --- a/platform/analysis-api/src/com/intellij/codeInspection/reference/RefEntity.java +++ b/platform/analysis-api/src/com/intellij/codeInspection/reference/RefEntity.java @@ -48,6 +48,7 @@ public interface RefEntity extends UserDataHolder { * * @return the list of children. */ + @Nullable List getChildren(); /** diff --git a/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/actions/AbstractBatchSuppressByNoInspectionCommentFix.java b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/actions/AbstractBatchSuppressByNoInspectionCommentFix.java index 2572bb617156..56188b949649 100644 --- a/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/actions/AbstractBatchSuppressByNoInspectionCommentFix.java +++ b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/actions/AbstractBatchSuppressByNoInspectionCommentFix.java @@ -106,6 +106,11 @@ public abstract class AbstractBatchSuppressByNoInspectionCommentFix implements C invoke(project, element); } + @Override + public boolean isSuppressAll() { + return SuppressionUtil.ALL.equals(myID); + } + protected final void replaceSuppressionComment(@NotNull final PsiElement comment) { SuppressionUtil.replaceSuppressionComment(comment, myID, myReplaceOtherSuppressionIds, getCommentLanguage(comment)); } diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/SuppressIntentionActionFromFix.java b/platform/analysis-impl/src/com/intellij/codeInspection/SuppressIntentionActionFromFix.java index 330baea5d08d..e3274879aab6 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/SuppressIntentionActionFromFix.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/SuppressIntentionActionFromFix.java @@ -85,4 +85,8 @@ public class SuppressIntentionActionFromFix extends SuppressIntentionAction { public String getFamilyName() { return myFix.getFamilyName(); } + + public boolean isSuppressAll() { + return myFix.isSuppressAll(); + } } diff --git a/platform/configuration-store-impl/src/StateMap.kt b/platform/configuration-store-impl/src/StateMap.kt index 54e94df82ebb..2941b890e314 100644 --- a/platform/configuration-store-impl/src/StateMap.kt +++ b/platform/configuration-store-impl/src/StateMap.kt @@ -203,7 +203,7 @@ class StateMap private constructor(private val names: Array, private val } val currentState = states.get(index) - LOG.assertTrue(currentState is Element) + LOG.assertTrue(currentState is Element, currentState?.let { it.javaClass.name } ?: "null") states.set(index, if (state == null) null else archiveState(state).toByteArray()) } } diff --git a/platform/configuration-store-impl/src/StorageBaseEx.kt b/platform/configuration-store-impl/src/StorageBaseEx.kt index 72ed7d7cd8a8..bdc7ce320f03 100644 --- a/platform/configuration-store-impl/src/StorageBaseEx.kt +++ b/platform/configuration-store-impl/src/StorageBaseEx.kt @@ -56,7 +56,7 @@ class StateGetter(private val component: PersistentStateCompon try { stateAfterLoad = component.state } - catch(e: Throwable) { + catch (e: Throwable) { LOG.error("Cannot get state after load", e) stateAfterLoad = null } diff --git a/platform/configuration-store-impl/src/XmlElementStorage.kt b/platform/configuration-store-impl/src/XmlElementStorage.kt index ceedbe8741e8..f0b2854718e6 100644 --- a/platform/configuration-store-impl/src/XmlElementStorage.kt +++ b/platform/configuration-store-impl/src/XmlElementStorage.kt @@ -226,7 +226,7 @@ fun save(states: StateMap, rootElementName: String, newLiveStates: Map EXTENSION_POINT + = new ExtensionPointName("com.intellij.stripTrailingSpacesFilterFactory"); + + /** + * Creates a filter which may restrict trailing spaces removal. + * + * @param project The current project or null if there is no project context. + * @param document The document to be processed. + * @return The filter as defined in {@link StripTrailingSpacesFilter}. The factory may return one of the several predefined filters: + * {@link StripTrailingSpacesFilter#NOT_ALLOWED}, {@link StripTrailingSpacesFilter#POSTPONED} or + * {@link StripTrailingSpacesFilter#ALL_LINES}. The latter can be returned, for example, if a language-specific logic is not + * applicable to the document. + */ + @NotNull + public abstract StripTrailingSpacesFilter createFilter(@Nullable Project project, @NotNull Document document); +} diff --git a/platform/core-api/src/com/intellij/openapi/editor/StripTrailingSpacesLineFilter.java b/platform/core-api/src/com/intellij/openapi/editor/StripTrailingSpacesLineFilter.java deleted file mode 100644 index 958b76c18870..000000000000 --- a/platform/core-api/src/com/intellij/openapi/editor/StripTrailingSpacesLineFilter.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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://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.openapi.editor; - -import com.intellij.openapi.extensions.ExtensionPointName; -import com.intellij.openapi.project.Project; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.*; - -/** - * Allows to suppress stripping spaces from some lines when a document is being saved and "Strip spaces on Save" option is not "None". - */ -public abstract class StripTrailingSpacesLineFilter { - public static final ExtensionPointName LINE_FILTER_EXTENSION_POINT - = new ExtensionPointName("com.intellij.stripTrailingSpacesLineFilter"); - - /** - * Tells if spaces can be stripped from the document. Returns false if trailing spaces should be preserved regardless of - * the editor settings and other filters. - * - * @param project The current project or null if there is no project context. - * @param document The document. - * @return True if it's OK to strip spaces, false otherwise. - */ - public abstract boolean isStripSpacesAllowed(@Nullable Project project, @NotNull Document document); - - /** - * Processes a document and sets bits to 1 (true) for lines which should remain untouched when trailing spaces are removed. - * - * @param project The current project or null if there is no project context. - * @param document The document to process. - * @param disabledLinesBitSet The bit set which can be modified by the apply() method. Each bit index corresponds to a line - * - * @return True if successful, false if can't be applied now, probably later. - */ - public abstract boolean apply(@Nullable Project project, @NotNull Document document, @NotNull BitSet disabledLinesBitSet); - -} diff --git a/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java b/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java index 976eed7817f7..67b008ed17ff 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java @@ -51,10 +51,9 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.ArrayList; import java.util.Arrays; -import java.util.BitSet; import java.util.List; -import static com.intellij.openapi.editor.StripTrailingSpacesLineFilter.LINE_FILTER_EXTENSION_POINT; +import static com.intellij.openapi.editor.StripTrailingSpacesFilterFactory.EXTENSION_POINT; public class DocumentImpl extends UserDataHolderBase implements DocumentEx { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.DocumentImpl"); @@ -199,13 +198,18 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { if (!isStripTrailingSpacesEnabled) { return true; } - StripTrailingSpacesLineFilter[] filters = LINE_FILTER_EXTENSION_POINT.getExtensions(); - for (StripTrailingSpacesLineFilter filter : filters) { - if (!filter.isStripSpacesAllowed(project, this)) return true; - } - BitSet disabledLinesBitSet = new BitSet(getLineCount()); - for (StripTrailingSpacesLineFilter filter : filters) { - if (!filter.apply(project, this, disabledLinesBitSet)) return false; + List filters = new ArrayList(); + for (StripTrailingSpacesFilterFactory filterFactory : EXTENSION_POINT.getExtensions()) { + StripTrailingSpacesFilter filter = filterFactory.createFilter(project, this); + if (filter == StripTrailingSpacesFilter.NOT_ALLOWED) { + return true; + } + else if (filter == StripTrailingSpacesFilter.POSTPONED) { + return false; + } + else { + filters.add(filter); + } } boolean markAsNeedsStrippingLater = false; @@ -230,7 +234,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { lineLoop: for (int line = 0; line < getLineCount(); line++) { LineSet lineSet = getLineSet(); - if (inChangedLinesOnly && !lineSet.isModified(line) || disabledLinesBitSet.get(line)) continue; + if (inChangedLinesOnly && !lineSet.isModified(line) || !canStripSpacesFrom(line, filters)) continue; int whiteSpaceStart = -1; final int lineEnd = lineSet.getLineEnd(line) - lineSet.getSeparatorLength(line); int lineStart = lineSet.getLineStart(line); @@ -288,6 +292,13 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { return markAsNeedsStrippingLater; } + private static boolean canStripSpacesFrom(int line, @NotNull List filters) { + for (StripTrailingSpacesFilter filter : filters) { + if (!filter.isStripSpacesAllowedForLine(line)) return false; + } + return true; + } + @Override public void setReadOnly(boolean isReadOnly) { if (myIsReadOnly != isReadOnly) { diff --git a/platform/core-impl/src/com/intellij/openapi/editor/impl/PsiBasedStripTrailingSpacesLineFilter.java b/platform/core-impl/src/com/intellij/openapi/editor/impl/PsiBasedStripTrailingSpacesFilter.java similarity index 50% rename from platform/core-impl/src/com/intellij/openapi/editor/impl/PsiBasedStripTrailingSpacesLineFilter.java rename to platform/core-impl/src/com/intellij/openapi/editor/impl/PsiBasedStripTrailingSpacesFilter.java index 39c948078e0f..5f96c07a1567 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/impl/PsiBasedStripTrailingSpacesLineFilter.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/impl/PsiBasedStripTrailingSpacesFilter.java @@ -18,9 +18,11 @@ package com.intellij.openapi.editor.impl; import com.intellij.lang.Language; import com.intellij.lang.LanguageUtil; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.StripTrailingSpacesLineFilter; +import com.intellij.openapi.editor.StripTrailingSpacesFilter; +import com.intellij.openapi.editor.StripTrailingSpacesFilterFactory; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; @@ -29,28 +31,45 @@ import org.jetbrains.annotations.Nullable; import java.util.BitSet; -public abstract class PsiBasedStripTrailingSpacesLineFilter extends StripTrailingSpacesLineFilter { - @Override - public boolean isStripSpacesAllowed(@Nullable Project project, @NotNull Document document) { - return true; +public abstract class PsiBasedStripTrailingSpacesFilter implements StripTrailingSpacesFilter { + private final @NotNull BitSet myDisabledLinesBitSet; + private final @NotNull Document myDocument; + + public abstract static class Factory extends StripTrailingSpacesFilterFactory { + @NotNull + @Override + public final StripTrailingSpacesFilter createFilter(@Nullable Project project, @NotNull Document document) { + Language language = getDocumentLanguage(document); + if (language != null && isApplicableTo(language)) { + PsiFile psiFile = getPsiFile(project, document); + if (psiFile != null) { + PsiBasedStripTrailingSpacesFilter filter = createFilter(document); + filter.process(psiFile); + return filter; + } + return POSTPONED; + } + return ALL_LINES; + } + + @NotNull + protected abstract PsiBasedStripTrailingSpacesFilter createFilter(@NotNull Document document); + + protected abstract boolean isApplicableTo(@NotNull Language language); + } + + protected PsiBasedStripTrailingSpacesFilter(@NotNull Document document) { + myDocument = document; + myDisabledLinesBitSet = new BitSet(document.getLineCount()); } @Override - public final boolean apply(@Nullable Project project, @NotNull Document document, @NotNull BitSet disabledLinesBitSet) { - Language language = getDocumentLanguage(document); - if (language != null && isApplicableTo(language)) { - PsiFile psiFile = getPsiFile(project, document); - if (psiFile != null) { - return apply(document, psiFile, disabledLinesBitSet); - } - return false; - } - return true; + public boolean isStripSpacesAllowedForLine(int line) { + return !myDisabledLinesBitSet.get(line); } - protected abstract boolean isApplicableTo(@NotNull Language language); - protected abstract boolean apply(@NotNull Document document, @NotNull PsiFile psiFile, BitSet disabledLinesBitSet); + protected abstract void process(@NotNull PsiFile psiFile); @Nullable private static Language getDocumentLanguage(@NotNull Document document) { @@ -72,4 +91,10 @@ public abstract class PsiBasedStripTrailingSpacesLineFilter extends StripTrailin } return null; } + + protected final void disableRange(@NotNull TextRange range) { + int startLine = myDocument.getLineNumber(range.getStartOffset()); + int endLine = myDocument.getLineNumber(range.getEndOffset()); + myDisabledLinesBitSet.set(startLine, endLine); + } } diff --git a/platform/core-impl/src/com/intellij/openapi/progress/impl/CoreProgressManager.java b/platform/core-impl/src/com/intellij/openapi/progress/impl/CoreProgressManager.java index 10a4adda1573..707eb4eb9607 100644 --- a/platform/core-impl/src/com/intellij/openapi/progress/impl/CoreProgressManager.java +++ b/platform/core-impl/src/com/intellij/openapi/progress/impl/CoreProgressManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 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. @@ -38,9 +38,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; import javax.swing.*; -import java.util.Collection; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.Future; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -406,66 +404,69 @@ public class CoreProgressManager extends ProgressManager implements Disposable { } private void registerIndicatorAndRun(@NotNull ProgressIndicator indicator, - @NotNull Thread currentThread, - ProgressIndicator oldIndicator, - @NotNull Runnable process) { - Set underIndicator; - boolean alreadyUnder; - boolean isStandard; + @NotNull Thread currentThread, + ProgressIndicator oldIndicator, + @NotNull Runnable process) { + List> threadsUnderThisIndicator = new ArrayList>(); synchronized (threadsUnderIndicator) { - underIndicator = threadsUnderIndicator.get(indicator); - if (underIndicator == null) { - underIndicator = new SmartHashSet(); - threadsUnderIndicator.put(indicator, underIndicator); - } - alreadyUnder = !underIndicator.add(currentThread); - isStandard = indicator instanceof StandardProgressIndicator; - if (!isStandard) { - nonStandardIndicators.add(indicator); - if (myCheckCancelledFuture == null) { - myCheckCancelledFuture = startBackgroundIndicatorPing(); + for (ProgressIndicator thisIndicator = indicator; thisIndicator != null; thisIndicator = thisIndicator instanceof WrappedProgressIndicator ? ((WrappedProgressIndicator)thisIndicator).getOriginalProgressIndicator() : null) { + Set underIndicator = threadsUnderIndicator.get(thisIndicator); + if (underIndicator == null) { + underIndicator = new SmartHashSet(); + threadsUnderIndicator.put(thisIndicator, underIndicator); } - } + boolean alreadyUnder = !underIndicator.add(currentThread); + threadsUnderThisIndicator.add(alreadyUnder ? null : underIndicator); - if (indicator.isCanceled()) { - threadsUnderCanceledIndicator.add(currentThread); - } - else { - threadsUnderCanceledIndicator.remove(currentThread); - } - thereIsProcessUnderCanceledIndicator = !threadsUnderCanceledIndicator.isEmpty(); - } - - try { - if (indicator instanceof WrappedProgressIndicator) { - ProgressIndicator wrappee = ((WrappedProgressIndicator)indicator).getOriginalProgressIndicator(); - if (wrappee == indicator) throw new IllegalArgumentException(indicator + " wraps itself"); - registerIndicatorAndRun(wrappee, currentThread, oldIndicator, process); - } - else { - process.run(); - } - } - finally { - synchronized (threadsUnderIndicator) { - boolean removed = alreadyUnder || underIndicator.remove(currentThread); - if (removed && underIndicator.isEmpty()) { - threadsUnderIndicator.remove(indicator); - } + boolean isStandard = thisIndicator instanceof StandardProgressIndicator; if (!isStandard) { - nonStandardIndicators.remove(indicator); - if (nonStandardIndicators.isEmpty()) { - myCheckCancelledFuture.cancel(true); - myCheckCancelledFuture = null; + nonStandardIndicators.add(thisIndicator); + if (myCheckCancelledFuture == null) { + myCheckCancelledFuture = startBackgroundIndicatorPing(); } } - // by this time oldIndicator may have been canceled - if (oldIndicator != null && oldIndicator.isCanceled()) { + + if (thisIndicator.isCanceled()) { threadsUnderCanceledIndicator.add(currentThread); } else { threadsUnderCanceledIndicator.remove(currentThread); } + } + + thereIsProcessUnderCanceledIndicator = !threadsUnderCanceledIndicator.isEmpty(); + } + + try { + process.run(); + } + finally { + synchronized (threadsUnderIndicator) { + ProgressIndicator thisIndicator = null; + // order doesn't matter + for (int i = 0; i < threadsUnderThisIndicator.size(); i++) { + thisIndicator = i == 0 ? indicator : ((WrappedProgressIndicator)thisIndicator).getOriginalProgressIndicator(); + Set underIndicator = threadsUnderThisIndicator.get(i); + boolean removed = underIndicator != null && underIndicator.remove(currentThread); + if (removed && underIndicator.isEmpty()) { + threadsUnderIndicator.remove(thisIndicator); + } + boolean isStandard = thisIndicator instanceof StandardProgressIndicator; + if (!isStandard) { + nonStandardIndicators.remove(thisIndicator); + if (nonStandardIndicators.isEmpty()) { + myCheckCancelledFuture.cancel(true); + myCheckCancelledFuture = null; + } + } + // by this time oldIndicator may have been canceled + if (oldIndicator != null && oldIndicator.isCanceled()) { + threadsUnderCanceledIndicator.add(currentThread); + } + else { + threadsUnderCanceledIndicator.remove(currentThread); + } + } thereIsProcessUnderCanceledIndicator = !threadsUnderCanceledIndicator.isEmpty(); } } diff --git a/platform/core-impl/src/com/intellij/openapi/progress/util/ProgressWrapper.java b/platform/core-impl/src/com/intellij/openapi/progress/util/ProgressWrapper.java index 438e936005de..f9a1d6a6226b 100644 --- a/platform/core-impl/src/com/intellij/openapi/progress/util/ProgressWrapper.java +++ b/platform/core-impl/src/com/intellij/openapi/progress/util/ProgressWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 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. @@ -43,12 +43,13 @@ public class ProgressWrapper extends AbstractProgressIndicatorBase implements Wr } protected ProgressWrapper(@NotNull ProgressIndicator original, boolean checkCanceledForMe) { + assert original instanceof StandardProgressIndicator : "Original indicator must be StandardProcessIndicator"; myOriginal = original; myCheckCanceledForMe = checkCanceledForMe; nested = 1 + (original instanceof ProgressWrapper ? ((ProgressWrapper)original).nested : -1); - if (nested > 50) { - LOG.error("Too many wrapped indicators"); - } + //if (nested > 50) { + // LOG.error("Too many wrapped indicators"); + //} ProgressManager.assertNotCircular(original); } @@ -60,13 +61,36 @@ public class ProgressWrapper extends AbstractProgressIndicatorBase implements Wr @Override public final boolean isCanceled() { - return myOriginal.isCanceled() || myCheckCanceledForMe && super.isCanceled(); + ProgressWrapper current = this; + while (true) { + if (current.myCheckCanceledForMe && current.isCanceledRaw()) return true; + ProgressIndicator original = current.getOriginalProgressIndicator(); + if (original instanceof ProgressWrapper) { + current = (ProgressWrapper)original; + } + else { + return original.isCanceled(); + } + } } + private boolean isCanceledRaw() { return super.isCanceled(); } + private void checkCanceledRaw() { super.checkCanceled(); } + @Override public final void checkCanceled() { - myOriginal.checkCanceled(); - super.checkCanceled(); + ProgressWrapper current = this; + while (true) { + current.checkCanceledRaw(); + ProgressIndicator original = current.getOriginalProgressIndicator(); + if (original instanceof ProgressWrapper) { + current = (ProgressWrapper)original; + } + else { + original.checkCanceled(); + break; + } + } } @Override diff --git a/platform/diff-impl/src/com/intellij/diff/comparison/ChunkOptimizer.java b/platform/diff-impl/src/com/intellij/diff/comparison/ChunkOptimizer.java index 6b38549c1e14..f762d86637ee 100644 --- a/platform/diff-impl/src/com/intellij/diff/comparison/ChunkOptimizer.java +++ b/platform/diff-impl/src/com/intellij/diff/comparison/ChunkOptimizer.java @@ -24,6 +24,7 @@ import com.intellij.diff.util.Side; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.util.registry.Registry; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -82,6 +83,9 @@ abstract class ChunkOptimizer { int equalForward = expandForward(myData1, myData2, range1.end1, range1.end2, range1.end1 + count2, range1.end2 + count2); int equalBackward = expandBackward(myData1, myData2, range2.start1 - count1, range2.start2 - count1, range2.start1, range2.start2); + // nothing to do + if (equalForward == 0 && equalBackward == 0) return; + // merge chunks left [A]B[B] -> [AB]B if (equalForward == count2) { myRanges.remove(myRanges.size() - 1); @@ -223,31 +227,81 @@ abstract class ChunkOptimizer { @Override protected int getShift(@NotNull Side touchSide, int equalForward, int equalBackward, @NotNull Range range1, @NotNull Range range2) { + Integer shift; + + shift = getUnchangedBoundaryShift(touchSide, equalForward, equalBackward, range1, range2, 0); + if (shift != null) return shift; + + shift = getChangedBoundaryShift(touchSide, equalForward, equalBackward, range1, range2, 0); + if (shift != null) return shift; + + shift = getUnchangedBoundaryShift(touchSide, equalForward, equalBackward, range1, range2, myThreshold); + if (shift != null) return shift; + + shift = getChangedBoundaryShift(touchSide, equalForward, equalBackward, range1, range2, myThreshold); + if (shift != null) return shift; + + return 0; + } + + /** + * search for an empty line boundary in unchanged lines + * ie: we want insertion/deletion to go right before/after of an empty line + */ + @Nullable + private Integer getUnchangedBoundaryShift(@NotNull Side touchSide, + int equalForward, int equalBackward, + @NotNull Range range1, @NotNull Range range2, + int threshold) { List touchLines = touchSide.select(myData1, myData2); int touchStart = touchSide.select(range2.start1, range2.start2); - int shiftForward = findUnimportantLineShift(touchLines, touchStart, equalForward, true, 0); - int shiftBackward = findUnimportantLineShift(touchLines, touchStart - 1, equalBackward, false, 0); + int shiftForward = findNextUnimportantLine(touchLines, touchStart, equalForward + 1, threshold); + int shiftBackward = findPrevUnimportantLine(touchLines, touchStart - 1, equalBackward + 1, threshold); - if (shiftForward == -1 && shiftBackward == -1 && myThreshold != 0) { - shiftForward = findUnimportantLineShift(touchLines, touchStart, equalForward, true, myThreshold); - shiftBackward = findUnimportantLineShift(touchLines, touchStart - 1, equalBackward, false, myThreshold); - } - - if (shiftForward == 0 || shiftBackward == 0) return 0; - if (shiftForward == -1 && shiftBackward == -1) return 0; - - return shiftForward != -1 ? shiftForward : -shiftBackward; + return getShift(shiftForward, shiftBackward); } - private static int findUnimportantLineShift(@NotNull List lines, int offset, int count, boolean leftToRight, int threshold) { + /** + * search for an empty line boundary in changed lines + * ie: we want insertion/deletion to start/end with an empty line + */ + @Nullable + private Integer getChangedBoundaryShift(@NotNull Side touchSide, + int equalForward, int equalBackward, + @NotNull Range range1, @NotNull Range range2, + int threshold) { + Side nonTouchSide = touchSide.other(); + List nonTouchLines = nonTouchSide.select(myData1, myData2); + int changeStart = nonTouchSide.select(range1.end1, range1.end2); + int changeEnd = nonTouchSide.select(range2.start1, range2.start2); + + int shiftForward = findNextUnimportantLine(nonTouchLines, changeStart, equalForward + 1, threshold); + int shiftBackward = findPrevUnimportantLine(nonTouchLines, changeEnd - 1, equalBackward + 1, threshold); + + return getShift(shiftForward, shiftBackward); + } + + private static int findNextUnimportantLine(@NotNull List lines, int offset, int count, int threshold) { for (int i = 0; i < count; i++) { - int index = leftToRight ? offset + i : offset - i; - if (lines.get(index).getNonSpaceChars() <= threshold) { - return i; - } + if (lines.get(offset + i).getNonSpaceChars() <= threshold) return i; } return -1; } + + private static int findPrevUnimportantLine(@NotNull List lines, int offset, int count, int threshold) { + for (int i = 0; i < count; i++) { + if (lines.get(offset - i).getNonSpaceChars() <= threshold) return i; + } + return -1; + } + + @Nullable + private static Integer getShift(int shiftForward, int shiftBackward) { + if (shiftForward == -1 && shiftBackward == -1) return null; + if (shiftForward == 0 || shiftBackward == 0) return 0; + + return shiftForward != -1 ? shiftForward : -shiftBackward; + } } } diff --git a/platform/diff-impl/tests/com/intellij/diff/DiffTestCase.kt b/platform/diff-impl/tests/com/intellij/diff/DiffTestCase.kt index 0a66d8d23c55..8145b2a415a7 100644 --- a/platform/diff-impl/tests/com/intellij/diff/DiffTestCase.kt +++ b/platform/diff-impl/tests/com/intellij/diff/DiffTestCase.kt @@ -100,10 +100,6 @@ abstract class DiffTestCase : UsefulTestCase() { fun parseSource(string: CharSequence): String = string.toString().replace('_', '\n') - fun parseMatching(before: String, after: String): Couple { - return Couple.of(parseMatching(before), parseMatching(after)) - } - fun parseMatching(matching: String): BitSet { val set = BitSet() matching.filterNot { it == '.' }.forEachIndexed { i, c -> if (c != ' ') set.set(i) } diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilTestBase.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilTestBase.kt index f135220f20e3..1559310cec45 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilTestBase.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilTestBase.kt @@ -25,9 +25,10 @@ import com.intellij.util.containers.ContainerUtil import java.util.* abstract class ComparisonUtilTestBase : DiffTestCase() { - private fun doLineTest(before: Document, after: Document, expected: List?, policy: ComparisonPolicy) { + private fun doLineTest(before: Document, after: Document, matchings: Couple?, expected: List?, policy: ComparisonPolicy) { val fragments = MANAGER.compareLines(before.charsSequence, after.charsSequence, policy, INDICATOR) checkConsistency(fragments, before, after) + if (matchings != null) checkLineMatching(fragments, matchings) if (expected != null) checkLineChanges(fragments, expected) } @@ -111,6 +112,18 @@ abstract class ComparisonUtilTestBase : DiffTestCase() { assertOrderedEquals(changes, expected) } + private fun checkLineMatching(fragments: List, matchings: Couple) { + val set1 = BitSet() + val set2 = BitSet() + for (fragment in fragments) { + set1.set(fragment.startLine1, fragment.endLine1) + set2.set(fragment.startLine2, fragment.endLine2) + } + + assertEquals(matchings.first, set1) + assertEquals(matchings.second, set2) + } + private fun checkDiffMatching(fragments: List, matchings: Couple) { val set1 = BitSet() val set2 = BitSet() @@ -201,7 +214,7 @@ abstract class ComparisonUtilTestBase : DiffTestCase() { assertTrue(change != null || matchings != null) when (type) { - TestType.LINE -> doLineTest(before!!, after!!, change, policy) + TestType.LINE -> doLineTest(before!!, after!!, matchings, change, policy) TestType.WORD -> doWordTest(before!!, after!!, matchings, change, policy) TestType.CHAR -> doCharTest(before!!, after!!, matchings, change, policy) TestType.SPLITTER -> { @@ -267,6 +280,47 @@ abstract class ComparisonUtilTestBase : DiffTestCase() { fun ignore() { ignoreMatching = parseMatching(before, after) } + + private fun parseMatching(before: String, after: String): Couple { + if (type == TestType.LINE) { + val builder = this@TestBuilder + return Couple.of(parseLineMatching(before, builder.before!!), parseLineMatching(after, builder.after!!)) + } + else { + return Couple.of(parseMatching(before), parseMatching(after)) + } + } + + fun parseLineMatching(matching: String, document: Document): BitSet { + assertEquals(matching.length, document.textLength) + + val lines1 = matching.split('_', '*') + val lines2 = document.charsSequence.split('\n') + assertEquals(lines1.size, lines2.size) + for (i in 0..lines1.size - 1) { + assertEquals(lines1[i].length, lines2[i].length, "line $i") + } + + + val set = BitSet() + + var index = 0 + var lineNumber = 0 + while (index < matching.length) { + var end = matching.indexOfAny(listOf("_", "*"), index) + 1 + if (end == 0) end = matching.length + + val line = matching.subSequence(index, end) + if (line.find { it != ' ' && it != '_' } != null) { + assert(!line.contains(' ')) + set.set(lineNumber) + } + lineNumber++ + index = end + } + + return set + } } diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/LineComparisonUtilTest.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/LineComparisonUtilTest.kt index e97750d310a5..9144618851d3 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/LineComparisonUtilTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/LineComparisonUtilTest.kt @@ -257,7 +257,7 @@ class LineComparisonUtilTest : ComparisonUtilTestBase() { fun `test regression - shifted similar lines should be matched as a single change, not insertion-deletion`() { lines { (" X_ X" - " X_ X") - default(mod(0, 0, 2, 2)) + ("--_---" - "---_----").default() testDefault() } } @@ -265,19 +265,59 @@ class LineComparisonUtilTest : ComparisonUtilTestBase() { fun `test prefer chunks bounded by empty line`() { lines { ("A_B_o_o_Y_Z_ _A_B_z_z_Y_Z" - "A_B_o_o_Y_Z_ _A_B_u_u_Y_Z_ _A_B_z_z_Y_Z") - default(ins(7, 7, 7)) + (" _ _ _ _ _ _ _ _ _ _ _ _ " - " _ _ _ _ _ _ _-_-_-_-_-_-_-_ _ _ _ _ _ ").default() + testAll() + } + + lines { + ("A_B_o_o_Y_Z_ _A_B_ _ _Y_Z" - "A_B_o_o_Y_Z_ _A_B_u_u_Y_Z_ _A_B_ _ _Y_Z") + (" _ _ _ _ _ _ _ _ _ _ _ _ " - " _ _ _ _ _ _ _ _ _-_-_-_-_-_-_-_ _ _ _ ").default() testAll() } lines { ("A_B_o_o_ _A_B_z_z" - "A_B_o_o_ _A_B_u_u_ _A_B_z_z") - default(ins(5, 5, 5)) + (" _ _ _ _ _ _ _ _ " - " _ _ _ _ _-_-_-_-_-_ _ _ _ ").default() testAll() } lines { ("o_o_Y_Z_ _z_z_Y_Z" - "o_o_Y_Z_ _u_u_Y_Z_ _z_z_Y_Z") - default(ins(5, 5, 5)) + (" _ _ _ _ _ _ _ _ " - " _ _ _ _ _-_-_-_-_-_ _ _ _ ").default() + testAll() + } + } + + fun `test prefer chunks bounded by short line`() { + lines { + ("A====_B====_o====_o====_Y====_Z====_!_A====_B====_z====_z====_Y====_Z====" - + "A====_B====_o====_o====_Y====_Z====_!_A====_B====_u====_u====_Y====_Z====_!_A====_B====_z====_z====_Y====_Z====") + (" _ _ _ _ _ _ _ _ _ _ _ _ " - + " _ _ _ _ _ _ _-----_-----_-----_-----_-----_-----_-_ _ _ _ _ _ ").default() + testAll() + } + + lines { + ("A====_B====_o====_o====_Y====_Z====_ _A====_B====_!_!_Y====_Z====" - + "A====_B====_o====_o====_Y====_Z====_ _A====_B====_u====_u====_Y====_Z====_ _A====_B====_!_!_Y====_Z====") + (" _ _ _ _ _ _ _ _ _ _ _ _ " - + " _ _ _ _ _ _ _-----_-----_-----_-----_-----_-----_-_ _ _ _ _ _ ").default() + testAll() + } + + lines { + ("A====_B====_o====_o====_Y====_Z====_!_A====_B====_ _ _Y====_Z====" - + "A====_B====_o====_o====_Y====_Z====_!_A====_B====_u====_u====_Y====_Z====_!_A====_B====_ _ _Y====_Z====") + (" _ _ _ _ _ _ _ _ _ _ _ _ " - + " _ _ _ _ _ _ _ _ _-----_-----_-----_-----_-_-----_-----_ _ _ _ ").default() + testAll() + } + } + + fun `test prefer chunks bounded by empty line - using inserted content`() { + lines { + ("e_x_A_B_z" - "x_A_B_ _q_A_B_z") + ("-_ _ _ _ " - " _ _ _-_-_-_-_ ").default() testAll() } } @@ -285,12 +325,13 @@ class LineComparisonUtilTest : ComparisonUtilTestBase() { fun `test prefer smaller amount of chunks`() { lines() { ("X_A_X_Y_" - "X_Y_") - default(del(0, 0, 2)) + ("-_-_ _ _" - " _ _").default() testAll() } lines() { (" __x___y" - "__y") + ("-**-*__ " - "__ ").default() default(del(0, 0, 3)) testAll() } @@ -298,7 +339,8 @@ class LineComparisonUtilTest : ComparisonUtilTestBase() { lines { ("U======_X======_Y======_z======_X======_Y======_X======_U======_X======_z======" - "U======_Y======_X======_U======_X======") - default(del(1, 1, 4), del(9, 5, 1)) + (" _-------_-------_-------_-------_ _ _ _ _-------" - + " _ _ _ _ ").default() testAll() } } @@ -306,7 +348,7 @@ class LineComparisonUtilTest : ComparisonUtilTestBase() { fun `test regression - can trim chunks after 'compareTwoSteps'`() { lines { ("q__7_ 6_ 7" - "_7") - default(del(0, 0, 1), del(3, 2, 2)) + ("-*_ _--*--" - "_ ").default() testDefault() } } @@ -314,7 +356,7 @@ class LineComparisonUtilTest : ComparisonUtilTestBase() { fun `test regression - can trim chunks after 'optimizeLineChunks'`() { lines { ("A=====_ B=====_ }_}_B=====_" - "A=====_ }_}_B=====_") - default(del(1, 1, 1)) + (" _-------_ _ _ _" - " _ _ _ _").default() testAll() } } @@ -322,26 +364,26 @@ class LineComparisonUtilTest : ComparisonUtilTestBase() { fun `test bad cases caused by 'compareTwoStep' logic`() { lines { ("x_!" - "!_x_y") - default(del(0, 0, 1), ins(2, 1, 2)) + ("-_ " - " _-_-").default() testAll() } lines { ("!_x_y" - "x_!") - default(del(0, 0, 1), mod(2, 1, 1, 1)) + ("-_ _-" - " _-").default() testAll() } lines { ("x_! " - "!_x_y") - default(mod(0, 0, 2, 3)) - trim(del(0, 0, 1), ins(2, 1, 2)) + ("-_--" - "-_-_-").default() + ("-_ " - " _-_-").trim() testAll() } lines { ("!_x_y" - "x_! ") - default(del(0, 0, 1), mod(2, 1, 1, 1)) + ("-_ _-" - " _--").default() testAll() } @@ -356,16 +398,16 @@ class LineComparisonUtilTest : ComparisonUtilTestBase() { fun `test bad cases caused by 'compareSmart' logic`() { lines { ("A=====_ B=====_ }_}_B=====" - "A=====_ }_}_B=====") - // TODO trim(del(1, 1, 1)) - default(del(1, 1, 1)) - trim(ins(1, 1, 2), del(2, 4, 3)) + // TODO (" _-------_ _ _ " - " _ _ _ ").trim() + (" _-------_ _ _ " - " _ _ _ ").default() + (" _ _--_-_------" - " _--_-_ ").trim() testAll() } lines { ("A=====_ B=====_X_ }_}_Z_B=====_" - "A=====_ }_}_B=====_") - // TODO default(del(1, 1, 2), del(5, 3, 1)) - default(mod(1, 1, 5, 2)) + // TODO (" _-------_-_ _ _-_ _" - " _ _ _ _").default() + (" _-------_-_--_-_-_ _" - " _--_-_ _").default() testAll() } } @@ -373,17 +415,16 @@ class LineComparisonUtilTest : ComparisonUtilTestBase() { fun `test trim changed blocks after second step correction`() { lines() { ("====}_==== }_Y_====}" - "====}_Y_====}") - default(del(1, 1, 1)) // result after second step correction - ignore(mod(1, 1, 2, 1)) // result looks strange because of 'diff.unimportant.line.char.count' - testDefault() - testTrim() + (" _------_ _ " - " _ _ ").default() // result after second step correction + (" _ _-_-----" - " _-_ ").ignore() // result looks strange because of 'diff.unimportant.line.char.count' + testAll() } } fun `test second step correction processes all confusing lines`() { lines { ("====}_==== }_Y_==== }_====}" - "==== }_Y_==== }") - default(del(0, 0, 1), del(4, 3, 1)) + ("-----_ _ _ _-----" - " _ _ ").default() testDefault() } } diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java b/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java index d1ffa0cc5a1d..4cb85dc4b76a 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java @@ -916,7 +916,7 @@ public class CommonCodeStyleSettings { public int WRAP_ON_TYPING = WrapOnTyping.DEFAULT.intValue; //-------------------------Indent options------------------------------------------------- - public static class IndentOptions implements Cloneable { + public static class IndentOptions implements Cloneable, JDOMExternalizable { public int INDENT_SIZE = 4; public int CONTINUATION_INDENT_SIZE = 8; public int TAB_SIZE = 4; @@ -931,10 +931,12 @@ public class CommonCodeStyleSettings { private static final Key INDENT_OPTIONS_KEY = Key.create("INDENT_OPTIONS_KEY"); private boolean myInaccurate; + @Override public void readExternal(Element element) throws InvalidDataException { DefaultJDOMExternalizer.readExternal(this, element); } + @Override public void writeExternal(Element element) throws WriteExternalException { DefaultJDOMExternalizer.writeExternal(this, element, field -> { if ("KEEP_INDENTS_ON_EMPTY_LINES".equals(field.getName())) { diff --git a/platform/lang-impl/src/com/intellij/analysis/AnalysisUIOptions.java b/platform/lang-impl/src/com/intellij/analysis/AnalysisUIOptions.java index 960c78e1befc..2842c67c72ce 100644 --- a/platform/lang-impl/src/com/intellij/analysis/AnalysisUIOptions.java +++ b/platform/lang-impl/src/com/intellij/analysis/AnalysisUIOptions.java @@ -50,7 +50,7 @@ public class AnalysisUIOptions implements PersistentStateComponent texts = new ArrayList(); for (QuickFixAction quickFix : quickFixes) { - String text = quickFix.getText(where); + String text = quickFix.getText(); if (text == null) continue; texts.add(escapeQuickFixText(text)); } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionRVContentProviderImpl.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionRVContentProviderImpl.java index 1cf2714e7840..5db74ec6f5c6 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionRVContentProviderImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionRVContentProviderImpl.java @@ -83,7 +83,7 @@ public class InspectionRVContentProviderImpl extends InspectionRVContentProvider public QuickFixAction[] getQuickFixes(@NotNull final InspectionToolWrapper toolWrapper, @NotNull final InspectionTree tree) { final RefEntity[] refEntities = tree.getSelectedElements(); InspectionToolPresentation presentation = tree.getContext().getPresentation(toolWrapper); - return refEntities.length == 0 ? null : presentation.getQuickFixes(refEntities); + return refEntities.length == 0 ? null : presentation.getQuickFixes(refEntities, tree.getSelectedDescriptors()); } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/LocalQuickFixWrapper.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/LocalQuickFixWrapper.java index 278f1f51b9aa..18342b16befe 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/LocalQuickFixWrapper.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/LocalQuickFixWrapper.java @@ -40,28 +40,20 @@ import java.util.Set; */ public class LocalQuickFixWrapper extends QuickFixAction { private final QuickFix myFix; - private String myText; public LocalQuickFixWrapper(@NotNull QuickFix fix, @NotNull InspectionToolWrapper toolWrapper) { super(fix.getName(), toolWrapper); myFix = fix; - myText = myFix.getName(); + setText(myFix.getName()); } @Override public void update(AnActionEvent e) { super.update(e); - getTemplatePresentation().setText(myText); - e.getPresentation().setText(myText); - } - - @Override - public String getText(RefEntity where) { - return myText; } public void setText(@NotNull String text) { - myText = text; + getTemplatePresentation().setText(text); } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/QuickFixAction.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/QuickFixAction.java index 2fb8e2290147..abd82c1a8789 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/QuickFixAction.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/QuickFixAction.java @@ -41,7 +41,6 @@ import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiUtilCore; -import com.intellij.util.ArrayUtil; import com.intellij.util.SequentialModalProgressTask; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; @@ -102,7 +101,7 @@ public class QuickFixAction extends AnAction { return false; } - public String getText(RefEntity where) { + public String getText() { return getTemplatePresentation().getText(); } @@ -110,15 +109,13 @@ public class QuickFixAction extends AnAction { public void actionPerformed(final AnActionEvent e) { final InspectionResultsView view = getInvoker(e); final InspectionTree tree = view.getTree(); - if (isProblemDescriptorsAcceptable()) { - final CommonProblemDescriptor[] descriptors = tree.getSelectedDescriptors(); - if (descriptors.length > 0) { - doApplyFix(view.getProject(), descriptors, tree.getContext()); - return; - } + final CommonProblemDescriptor[] descriptors; + if (isProblemDescriptorsAcceptable() && (descriptors = tree.getSelectedDescriptors()).length > 0) { + doApplyFix(view.getProject(), descriptors, tree.getContext()); + } else { + doApplyFix(getSelectedElements(e), view); } - - doApplyFix(getSelectedElements(e), view); + view.updateRightPanel(); } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/offlineViewer/OfflineInspectionRVContentProvider.java b/platform/lang-impl/src/com/intellij/codeInspection/offlineViewer/OfflineInspectionRVContentProvider.java index db99eb2ff6ca..7db298c8ddff 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/offlineViewer/OfflineInspectionRVContentProvider.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/offlineViewer/OfflineInspectionRVContentProvider.java @@ -21,7 +21,6 @@ package com.intellij.codeInspection.offlineViewer; import com.intellij.codeInspection.CommonProblemDescriptor; -import com.intellij.codeInspection.QuickFix; import com.intellij.codeInspection.ex.*; import com.intellij.codeInspection.offline.OfflineProblemDescriptor; import com.intellij.codeInspection.reference.RefElement; @@ -31,7 +30,6 @@ import com.intellij.codeInspection.ui.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.util.Function; -import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashSet; import com.intellij.util.ui.tree.TreeUtil; import org.jetbrains.annotations.NotNull; @@ -69,7 +67,7 @@ public class OfflineInspectionRVContentProvider extends InspectionRVContentProvi final TreePath[] treePaths = tree.getSelectionPaths(); if (treePaths == null) return QuickFixAction.EMPTY; final List selectedElements = new ArrayList(); - final Map> actions = new HashMap>(); + final Map actions = new HashMap<>(); for (TreePath selectionPath : treePaths) { TreeUtil.traverseDepth((TreeNode)selectionPath.getLastPathComponent(), new TreeUtil.Traverse() { @Override @@ -79,18 +77,10 @@ public class OfflineInspectionRVContentProvider extends InspectionRVContentProvi final OfflineProblemDescriptorNode descriptorNode = (OfflineProblemDescriptorNode)node; final RefEntity element = descriptorNode.getElement(); selectedElements.add(element); - Set quickFixes = actions.get(element); - if (quickFixes == null) { - quickFixes = new HashSet(); - actions.put(element, quickFixes); - } - final CommonProblemDescriptor descriptor = descriptorNode.getDescriptor(); - if (descriptor != null) { - final QuickFix[] fixes = descriptor.getFixes(); - if (fixes != null) { - ContainerUtil.addAll(quickFixes, fixes); - } - } + CommonProblemDescriptor[] quickFixes = actions.get(element); + actions.put(element, quickFixes == null ? + new CommonProblemDescriptor[]{descriptorNode.getDescriptor()} : + DefaultInspectionToolPresentation.mergeDescriptors(quickFixes, new CommonProblemDescriptor[]{descriptorNode.getDescriptor()})); } else if (node instanceof RefElementNode) { selectedElements.add(((RefElementNode)node).getElement()); @@ -106,7 +96,7 @@ public class OfflineInspectionRVContentProvider extends InspectionRVContentProvi GlobalInspectionContextImpl context = tree.getContext(); InspectionToolPresentation presentation = context.getPresentation(toolWrapper); - return presentation.extractActiveFixes(selectedRefElements, actions); + return presentation.extractActiveFixes(selectedRefElements, actions, tree.getSelectedDescriptors()); } @Override diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/Browser.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/Browser.java deleted file mode 100644 index ce8378d0b917..000000000000 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/Browser.java +++ /dev/null @@ -1,532 +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.codeInspection.ui; - -import com.intellij.codeInsight.FileModificationService; -import com.intellij.codeInsight.daemon.HighlightDisplayKey; -import com.intellij.codeInspection.*; -import com.intellij.codeInspection.ex.*; -import com.intellij.codeInspection.reference.RefElement; -import com.intellij.codeInspection.reference.RefEntity; -import com.intellij.codeInspection.ui.actions.SuppressActionWrapper; -import com.intellij.ide.BrowserUtil; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.command.CommandProcessor; -import com.intellij.openapi.editor.colors.EditorColorsManager; -import com.intellij.openapi.editor.colors.EditorColorsScheme; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.vfs.VfsUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileManager; -import com.intellij.profile.codeInspection.InspectionProjectProfileManagerImpl; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiManager; -import com.intellij.psi.util.PsiModificationTracker; -import com.intellij.ui.ScrollPaneFactory; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.ui.UIUtil; -import com.intellij.xml.util.XmlStringUtil; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import javax.swing.event.HyperlinkEvent; -import javax.swing.event.HyperlinkListener; -import javax.swing.text.Document; -import javax.swing.text.Style; -import javax.swing.text.StyleConstants; -import javax.swing.text.StyledDocument; -import javax.swing.text.html.HTMLDocument; -import javax.swing.text.html.HTMLFrameHyperlinkEvent; -import java.awt.*; -import java.io.IOException; -import java.io.StringReader; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; - -class Browser extends JPanel { - private static final String UNDER_CONSTRUCTION = InspectionsBundle.message("inspection.tool.description.under.construction.text"); - private final List myClickListeners = ContainerUtil.createLockFreeCopyOnWriteList(); - private RefEntity myCurrentEntity; - private JEditorPane myHTMLViewer; - private final InspectionResultsView myView; - private final HyperlinkListener myHyperLinkListener; - private CommonProblemDescriptor myCurrentDescriptor; - - public static class ClickEvent { - public static final int REF_ELEMENT = 1; - public static final int FILE_OFFSET = 2; - private final VirtualFile myFile; - private final int myStartPosition; - private final int myEndPosition; - private final RefElement refElement; - private final int myEventType; - - public ClickEvent(VirtualFile myFile, int myStartPosition, int myEndPosition) { - this.myFile = myFile; - this.myStartPosition = myStartPosition; - this.myEndPosition = myEndPosition; - myEventType = FILE_OFFSET; - refElement = null; - } - - public int getEventType() { - return myEventType; - } - - public VirtualFile getFile() { - return myFile; - } - - public int getStartOffset() { - return myStartPosition; - } - - public int getEndOffset() { - return myEndPosition; - } - - public RefElement getClickedElement() { - return refElement; - } - - } - - public void dispose(){ - removeAll(); - if (myHTMLViewer != null) { - myHTMLViewer.removeHyperlinkListener(myHyperLinkListener); - myHTMLViewer = null; - } - myClickListeners.clear(); - } - - public interface ClickListener { - void referenceClicked(ClickEvent e); - } - - private void showPageFromHistory(@NotNull RefEntity newEntity) { - InspectionToolWrapper toolWrapper = getToolWrapper(newEntity); - try { - String html = generateHTML(newEntity, toolWrapper); - myHTMLViewer.read(new StringReader(html), null); - setupStyle(); - myHTMLViewer.setCaretPosition(0); - } - catch (Exception e) { - showEmpty(); - } - finally { - myCurrentEntity = newEntity; - myCurrentDescriptor = null; - } - } - - public void showPageFor(RefEntity refEntity, CommonProblemDescriptor descriptor) { - try { - String html = generateHTML(refEntity, descriptor); - myHTMLViewer.read(new StringReader(html), null); - setupStyle(); - myHTMLViewer.setCaretPosition(0); - } - catch (Exception e) { - showEmpty(); - } - finally { - myCurrentEntity = refEntity; - myCurrentDescriptor = descriptor; - } - } - - public void showPageFor(RefEntity newEntity) { - if (newEntity == null) { - showEmpty(); - return; - } - //multiple problems for one entity -> refresh browser - showPageFromHistory(newEntity.getRefManager().getRefinedElement(newEntity)); - } - - public Browser(@NotNull InspectionResultsView view) { - super(new BorderLayout()); - myView = view; - - myCurrentEntity = null; - myCurrentDescriptor = null; - - myHTMLViewer = new JEditorPane(UIUtil.HTML_MIME, InspectionsBundle.message("inspection.offline.view.empty.browser.text")); - myHTMLViewer.setEditable(false); - myHyperLinkListener = new HyperlinkListener() { - @Override - public void hyperlinkUpdate(HyperlinkEvent e) { - Browser.this.hyperlinkUpdate(e); - } - }; - myHTMLViewer.addHyperlinkListener(myHyperLinkListener); - - final JScrollPane pane = ScrollPaneFactory.createScrollPane(myHTMLViewer); - pane.setBorder(null); - add(pane, BorderLayout.CENTER); - setupStyle(); - } - - private void hyperlinkUpdate(HyperlinkEvent e) { - if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED) { - return; - } - JEditorPane pane = (JEditorPane)e.getSource(); - if (e instanceof HTMLFrameHyperlinkEvent) { - HTMLFrameHyperlinkEvent evt = (HTMLFrameHyperlinkEvent)e; - HTMLDocument doc = (HTMLDocument)pane.getDocument(); - doc.processHTMLFrameHyperlinkEvent(evt); - return; - } - URL url = null; - try { - url = e.getURL(); - @NonNls String ref = url.getRef(); - if (ref.startsWith("pos:")) { - int delimeterPos = ref.indexOf(':', "pos:".length() + 1); - String startPosition = ref.substring("pos:".length(), delimeterPos); - String endPosition = ref.substring(delimeterPos + 1); - Integer textStartOffset = new Integer(startPosition); - Integer textEndOffset = new Integer(endPosition); - String fileURL = url.toExternalForm(); - fileURL = fileURL.substring(0, fileURL.indexOf('#')); - VirtualFile vFile = VirtualFileManager.getInstance().findFileByUrl(fileURL); - if (vFile != null) { - fireClickEvent(vFile, textStartOffset.intValue(), textEndOffset.intValue()); - } - } - else if (ref.startsWith("descr:")) { - if (myCurrentDescriptor instanceof ProblemDescriptor) { - PsiElement psiElement = ((ProblemDescriptor)myCurrentDescriptor).getPsiElement(); - if (psiElement == null) return; - VirtualFile vFile = psiElement.getContainingFile().getVirtualFile(); - if (vFile != null) { - TextRange range = ((ProblemDescriptorBase)myCurrentDescriptor).getTextRange(); - fireClickEvent(vFile, range.getStartOffset(), range.getEndOffset()); - } - } - } - else if (ref.startsWith("invoke:")) { - int actionNumber = Integer.parseInt(ref.substring("invoke:".length())); - InspectionToolWrapper toolWrapper = getToolWrapper(); - InspectionToolPresentation presentation = myView.getGlobalInspectionContext().getPresentation(toolWrapper); - QuickFixAction fixAction = presentation.getQuickFixes(new RefElement[]{(RefElement)myCurrentEntity})[actionNumber]; - fixAction.doApplyFix(new RefElement[]{(RefElement)myCurrentEntity}, myView); - } - else if (ref.startsWith("invokelocal:")) { - int actionNumber = Integer.parseInt(ref.substring("invokelocal:".length())); - if (actionNumber > -1) { - invokeLocalFix(actionNumber); - } - } else if (ref.startsWith("suppress:")){ - final SuppressActionWrapper.SuppressTreeAction[] suppressTreeActions = - new SuppressActionWrapper(myView.getProject(), getToolWrapper(), myView.getTree().getSelectionPaths()).getChildren(null); - final List activeActions = new ArrayList(); - for (SuppressActionWrapper.SuppressTreeAction suppressTreeAction : suppressTreeActions) { - if (suppressTreeAction.isAvailable()) activeActions.add(suppressTreeAction); - } - if (!activeActions.isEmpty()) { - int actionNumber = Integer.parseInt(ref.substring("suppress:".length())); - if (actionNumber > -1 && activeActions.size() > actionNumber) { - activeActions.get(actionNumber).actionPerformed(null); - } - } - } - else { - int offset = Integer.parseInt(ref); - String fileURL = url.toExternalForm(); - fileURL = fileURL.substring(0, fileURL.indexOf('#')); - VirtualFile vFile = VirtualFileManager.getInstance().findFileByUrl(fileURL); - if (vFile == null) { - vFile = VfsUtil.findFileByURL(url); - } - if (vFile != null) { - fireClickEvent(vFile, offset, offset); - } - } - } - catch (Throwable t) { - if (url != null) { - BrowserUtil.browse(url); - } - } - } - - private void setupStyle() { - Document document = myHTMLViewer.getDocument(); - if (!(document instanceof StyledDocument)) { - return; - } - - StyledDocument styledDocument = (StyledDocument)document; - - EditorColorsManager colorsManager = EditorColorsManager.getInstance(); - EditorColorsScheme scheme = colorsManager.getGlobalScheme(); - - Style style = styledDocument.addStyle("active", null); - StyleConstants.setFontFamily(style, scheme.getEditorFontName()); - StyleConstants.setFontSize(style, scheme.getEditorFontSize()); - styledDocument.setCharacterAttributes(0, document.getLength(), style, false); - } - - public void addClickListener(ClickListener listener) { - myClickListeners.add(listener); - } - - private void fireClickEvent(VirtualFile file, int startPosition, int endPosition) { - ClickEvent e = new ClickEvent(file, startPosition, endPosition); - - for (ClickListener listener : myClickListeners) { - listener.referenceClicked(e); - } - } - - private String generateHTML(final RefEntity refEntity, @NotNull final InspectionToolWrapper toolWrapper) { - final StringBuffer buf = new StringBuffer(); - final HTMLComposerImpl htmlComposer = getPresentation(toolWrapper).getComposer(); - if (refEntity instanceof RefElement) { - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - htmlComposer.compose(buf, refEntity); - } - }); - } - else { - htmlComposer.compose(buf, refEntity); - } - - uppercaseFirstLetter(buf); - - if (refEntity instanceof RefElement){ - appendSuppressSection(buf); - } - - insertHeaderFooter(buf); - - return buf.toString(); - } - - private InspectionToolPresentation getPresentation(@NotNull InspectionToolWrapper toolWrapper) { - return myView.getGlobalInspectionContext().getPresentation(toolWrapper); - } - - @SuppressWarnings({"HardCodedStringLiteral"}) - private static void insertHeaderFooter(final StringBuffer buf) { - buf.insert(0, ""); - buf.append(""); - } - - private String generateHTML(final RefEntity refEntity, final CommonProblemDescriptor descriptor) { - final StringBuffer buf = new StringBuffer(); - final Runnable action = new Runnable() { - @Override - public void run() { - InspectionToolWrapper toolWrapper = getToolWrapper(refEntity); - getPresentation(toolWrapper).getComposer().compose(buf, refEntity, descriptor); - } - }; - ApplicationManager.getApplication().runReadAction(action); - - uppercaseFirstLetter(buf); - - if (refEntity instanceof RefElement) { - appendSuppressSection(buf); - } - - insertHeaderFooter(buf); - return buf.toString(); - } - - private InspectionToolWrapper getToolWrapper(final RefEntity refEntity) { - InspectionToolWrapper toolWrapper = getToolWrapper(); - assert toolWrapper != null; - final GlobalInspectionContextImpl context = myView.getGlobalInspectionContext(); - if (refEntity instanceof RefElement){ - PsiElement element = ((RefElement)refEntity).getElement(); - if (element == null) return toolWrapper; - InspectionProfileWrapper profileWrapper = InspectionProjectProfileManagerImpl.getInstanceImpl(context.getProject()).getProfileWrapper(); - toolWrapper = profileWrapper.getInspectionTool(toolWrapper.getShortName(), element); - } - return toolWrapper; - } - - private void appendSuppressSection(final StringBuffer buf) { - final InspectionToolWrapper toolWrapper = getToolWrapper(); - if (toolWrapper != null) { - final HighlightDisplayKey key = HighlightDisplayKey.find(toolWrapper.getShortName()); - if (key != null){//dummy entry points - final SuppressActionWrapper.SuppressTreeAction[] suppressActions = new SuppressActionWrapper(myView.getProject(), toolWrapper, myView.getTree().getSelectionPaths()).getChildren(null); - if (suppressActions.length > 0) { - final List activeSuppressActions = new ArrayList(); - for (SuppressActionWrapper.SuppressTreeAction suppressAction : suppressActions) { - if (suppressAction.isAvailable()) { - activeSuppressActions.add(suppressAction); - } - } - if (!activeSuppressActions.isEmpty()) { - int idx = 0; - @NonNls final String br = "
"; - buf.append(br); - HTMLComposerImpl.appendHeading(buf, InspectionsBundle.message("inspection.export.results.suppress")); - for (AnAction suppressAction : activeSuppressActions) { - buf.append(br); - if (idx == activeSuppressActions.size() - 1) { - buf.append(br); - } - HTMLComposer.appendAfterHeaderIndention(buf); - @NonNls final String href = "" + suppressAction.getTemplatePresentation().getText() + ""; - buf.append(href); - idx++; - } - } - } - } - } - } - - private static void uppercaseFirstLetter(final StringBuffer buf) { - if (buf.length() > 1) { - char[] firstLetter = new char[1]; - buf.getChars(0, 1, firstLetter, 0); - buf.setCharAt(0, Character.toUpperCase(firstLetter[0])); - } - } - - @SuppressWarnings({"HardCodedStringLiteral"}) - public void showEmpty() { - myCurrentEntity = null; - try { - myHTMLViewer.read(new StringReader(InspectionsBundle.message("inspection.offline.view.empty.browser.text")), null); - } - catch (IOException e) { - //can't be - } - } - - public void showDescription(@NotNull InspectionToolWrapper toolWrapper){ - if (toolWrapper.getShortName().isEmpty()){ - showEmpty(); - return; - } - @NonNls StringBuffer page = new StringBuffer(); - page.append(""); - page.append(""); - page.append("" + - ""); - page.append(""); - page.append(""); - page.append("" + - "
"); - HTMLComposer.appendHeading(page, InspectionsBundle.message("inspection.tool.in.browser.id.title")); - page.append("
"); - page.append(toolWrapper.getShortName()); - page.append("
"); - HTMLComposer.appendHeading(page, InspectionsBundle.message("inspection.tool.in.browser.description.title")); - page.append("
"); - @NonNls final String underConstruction = "" + UNDER_CONSTRUCTION + ""; - try { - @NonNls String description = toolWrapper.loadDescription(); - if (description == null) { - description = underConstruction; - } - page.append(DefaultInspectionToolPresentation.stripUIRefsFromInspectionDescription(UIUtil.getHtmlBody(description))); - - page.append("
"); - myHTMLViewer.setText(XmlStringUtil.wrapInHtml(page)); - setupStyle(); - } - finally { - myCurrentEntity = null; - } - } - - @Nullable - private InspectionToolWrapper getToolWrapper() { - return myView.getTree().getSelectedToolWrapper(); - } - - public void invokeLocalFix(int idx) { - if (myView.getTree().getSelectionCount() != 1) return; - final InspectionTreeNode node = (InspectionTreeNode)myView.getTree().getSelectionPath().getLastPathComponent(); - if (node instanceof ProblemDescriptionNode) { - final ProblemDescriptionNode problemNode = (ProblemDescriptionNode)node; - final CommonProblemDescriptor descriptor = problemNode.getDescriptor(); - final RefEntity element = problemNode.getElement(); - invokeFix(element, descriptor, idx); - } - else if (node instanceof RefElementNode) { - RefElementNode elementNode = (RefElementNode)node; - RefEntity element = elementNode.getElement(); - CommonProblemDescriptor descriptor = elementNode.getProblem(); - if (descriptor != null) { - invokeFix(element, descriptor, idx); - } - } - } - - private void invokeFix(final RefEntity element, final CommonProblemDescriptor descriptor, final int idx) { - final QuickFix[] fixes = descriptor.getFixes(); - if (fixes != null && fixes.length > idx && fixes[idx] != null) { - if (element instanceof RefElement) { - PsiElement psiElement = ((RefElement)element).getElement(); - if (psiElement != null && psiElement.isValid()) { - if (!FileModificationService.getInstance().preparePsiElementForWrite(psiElement)) return; - performFix(element, descriptor, idx, fixes[idx]); - } - } - else { - performFix(element, descriptor, idx, fixes[idx]); - } - } - } - - private void performFix(final RefEntity element, final CommonProblemDescriptor descriptor, final int idx, final QuickFix fix) { - final Runnable command = new Runnable() { - @Override - public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - final PsiModificationTracker tracker = PsiManager.getInstance(myView.getProject()).getModificationTracker(); - final long startCount = tracker.getModificationCount(); - CommandProcessor.getInstance().markCurrentCommandAsGlobal(myView.getProject()); - //CCE here means QuickFix was incorrectly inherited - fix.applyFix(myView.getProject(), descriptor); - if (startCount != tracker.getModificationCount()) { - InspectionToolWrapper toolWrapper = myView.getTree().getSelectedToolWrapper(); - if (toolWrapper != null) { - InspectionToolPresentation presentation = - myView.getGlobalInspectionContext().getPresentation(toolWrapper); - presentation.ignoreProblem(element, descriptor, idx); - } - myView.updateView(false); - } - } - }); - } - }; - CommandProcessor.getInstance().executeCommand(myView.getProject(), command, fix.getName(), null); - } -} - diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/DefaultInspectionToolPresentation.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/DefaultInspectionToolPresentation.java index e0dea90be811..cc87a9c5bd14 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/DefaultInspectionToolPresentation.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/DefaultInspectionToolPresentation.java @@ -267,7 +267,7 @@ public class DefaultInspectionToolPresentation implements ProblemDescriptionsPro } @NotNull - private static CommonProblemDescriptor[] mergeDescriptors(@NotNull CommonProblemDescriptor[] problems1, + public static CommonProblemDescriptor[] mergeDescriptors(@NotNull CommonProblemDescriptor[] problems1, @NotNull CommonProblemDescriptor[] problems2) { CommonProblemDescriptor[] out = new CommonProblemDescriptor[problems1.length + problems2.length]; int o = problems1.length; @@ -641,41 +641,77 @@ public class DefaultInspectionToolPresentation implements ProblemDescriptionsPro @Override @Nullable - public QuickFixAction[] getQuickFixes(@NotNull final RefEntity[] refElements) { - return extractActiveFixes(refElements, getQuickFixActions()); + public QuickFixAction[] getQuickFixes(@NotNull final RefEntity[] refElements, CommonProblemDescriptor[] allowedDescriptors) { + return extractActiveFixes(refElements, getProblemElements(), allowedDescriptors); } @Override @Nullable - public QuickFixAction[] extractActiveFixes(@NotNull RefEntity[] refElements, @NotNull Map> actions) { - Map result = new com.intellij.util.containers.HashMap(); + public QuickFixAction[] extractActiveFixes(@NotNull RefEntity[] refElements, + @NotNull Map descriptorMap, + @Nullable CommonProblemDescriptor[] allowedDescriptors) { + final Set allowedDescriptorSet = allowedDescriptors == null ? null : ContainerUtil.newHashSet(allowedDescriptors); + Map result = new com.intellij.util.containers.HashMap<>(); + boolean isFirst = true; for (RefEntity refElement : refElements) { - final Set localQuickFixes = actions.get(refElement); - if (localQuickFixes == null) continue; - for (QuickFix fix : localQuickFixes) { - if (fix == null) continue; - final Class klass = fix instanceof ActionClassHolder ? ((ActionClassHolder ) fix).getActionClass() : fix.getClass(); - final QuickFixAction quickFixAction = result.get(klass); - if (quickFixAction != null) { - try { - String familyName = fix.getFamilyName(); - familyName = !familyName.isEmpty() ? "\'" + familyName + "\'" : familyName; - ((LocalQuickFixWrapper)quickFixAction).setText(InspectionsBundle.message("inspection.descriptor.provider.apply.fix", familyName)); - } - catch (AbstractMethodError e) { - //for plugin compatibility - ((LocalQuickFixWrapper)quickFixAction).setText(InspectionsBundle.message("inspection.descriptor.provider.apply.fix", "")); - } + final CommonProblemDescriptor[] descriptors = descriptorMap.get(refElement); + if (descriptors == null) continue; + for (CommonProblemDescriptor d : descriptors) { + if (allowedDescriptorSet != null && !allowedDescriptorSet.contains(d)) { + continue; } - else { - LocalQuickFixWrapper quickFixWrapper = new LocalQuickFixWrapper(fix, myToolWrapper); - result.put(klass, quickFixWrapper); + QuickFix[] fixes = d.getFixes(); + if (fixes != null) { + if (isFirst) { + for (QuickFix fix : fixes) { + if (fix == null) continue; + final Class klass = getFixClass(fix); + LocalQuickFixWrapper quickFixWrapper = new LocalQuickFixWrapper(fix, myToolWrapper); + result.put(klass, quickFixWrapper); + } + isFirst = false; + } + else { + for (Class clazz : new ArrayList<>(result.keySet())) { + boolean isFound = false; + for (QuickFix fix : fixes) { + if (fix == null) continue; + final Class klass = getFixClass(fix); + if (clazz.equals(klass)) { + isFound = true; + final QuickFixAction quickFixAction = result.get(clazz); + try { + String familyName = fix.getFamilyName(); + familyName = !familyName.isEmpty() ? "\'" + familyName + "\'" : familyName; + ((LocalQuickFixWrapper)quickFixAction) + .setText(InspectionsBundle.message("inspection.descriptor.provider.apply.fix", familyName)); + } + catch (AbstractMethodError e) { + //for plugin compatibility + ((LocalQuickFixWrapper)quickFixAction) + .setText(InspectionsBundle.message("inspection.descriptor.provider.apply.fix", "")); + } + break; + } + } + if (!isFound) { + result.remove(clazz); + if (result.isEmpty()) { + return QuickFixAction.EMPTY; + } + } + } + } } } } return result.values().isEmpty() ? null : result.values().toArray(new QuickFixAction[result.size()]); } + private static Class getFixClass(QuickFix fix) { + return fix instanceof ActionClassHolder ? ((ActionClassHolder)fix).getActionClass() : fix.getClass(); + } + @Override public RefEntity getElement(@NotNull CommonProblemDescriptor descriptor) { return getProblemToElements().get(descriptor); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionNode.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionNode.java index 0bb3105e8ef7..bde4f550e5aa 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionNode.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionNode.java @@ -23,7 +23,6 @@ import com.intellij.util.IconUtil; import org.jetbrains.annotations.NotNull; import javax.swing.*; -import java.util.Enumeration; /** * @author max @@ -51,19 +50,9 @@ public class InspectionNode extends InspectionTreeNode { } public boolean isTooBigForOnlineRefresh() { - if(!myTooBigForOnlineRefresh) myTooBigForOnlineRefresh = getProblemCount()>1000; + if (!myTooBigForOnlineRefresh) { + myTooBigForOnlineRefresh = getProblemCount() > 1000; + } return myTooBigForOnlineRefresh; } - - @Override - public int getProblemCount() { - int sum = 0; - Enumeration children = children(); - while (children.hasMoreElements()) { - InspectionTreeNode child = (InspectionTreeNode)children.nextElement(); - if (child instanceof InspectionNode) continue; - sum += child.getProblemCount(); - } - return sum; - } } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionNodeInfo.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionNodeInfo.java new file mode 100644 index 000000000000..f6afb1cca039 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionNodeInfo.java @@ -0,0 +1,109 @@ +/* + * 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://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.codeInspection.ui; + +import com.intellij.codeInsight.daemon.HighlightDisplayKey; +import com.intellij.codeInspection.ex.DisableInspectionToolAction; +import com.intellij.codeInspection.ex.InspectionProfileImpl; +import com.intellij.codeInspection.ex.InspectionToolWrapper; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.profile.codeInspection.InspectionProjectProfileManager; +import com.intellij.profile.codeInspection.ui.SingleInspectionProfilePanel; +import com.intellij.ui.*; +import com.intellij.ui.components.JBScrollPane; +import com.intellij.util.ui.JBInsets; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.MouseEvent; + +/** + * @author Dmitry Batkovich + */ +public class InspectionNodeInfo extends JPanel { + private final static Logger LOG = Logger.getInstance(InspectionNodeInfo.class); + + private final JButton myButton; + private final SimpleColoredComponent myTitle; + private final HighlightDisplayKey myKey; + private final InspectionProfileImpl myCurrentProfile; + private final String myName; + private final Project myProject; + + public InspectionNodeInfo(final InspectionToolWrapper toolWrapper, Project project) { + setLayout(new GridBagLayout()); + setBorder(IdeBorderFactory.createEmptyBorder(0, 3, 0, 0)); + myProject = project; + myTitle = new SimpleColoredComponent(); + myCurrentProfile = (InspectionProfileImpl)InspectionProjectProfileManager.getInstance(project).getProjectProfileImpl(); + myKey = HighlightDisplayKey.find(toolWrapper.getID()); + myName = toolWrapper.getDisplayName(); + myButton = new JButton(); + + add(myTitle, + new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new JBInsets(0, 2, 0, 0), + 0, 0)); + + JEditorPane description = new JEditorPane(); + description.setContentType(UIUtil.HTML_MIME); + description.setEditable(false); + description.setOpaque(false); + description.addHyperlinkListener(BrowserHyperlinkListener.INSTANCE); + final String toolDescription = toolWrapper.loadDescription(); + SingleInspectionProfilePanel.readHTML(description, SingleInspectionProfilePanel.toHTML(description, toolDescription == null ? "" : toolDescription, true)); + + add(ScrollPaneFactory.createScrollPane(description, true), + new GridBagConstraints(0, 1, 1, 1, 0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, + new JBInsets(5, 5, 0, 0), 0, 0)); + add(myButton, + new GridBagConstraints(0, 2, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, + new JBInsets(15, 0, 0, 0), 0, 0)); + updateEnableButtonText(false); + + new ClickListener() { + @Override + public boolean onClick(@NotNull MouseEvent event, int clickCount) { + updateEnableButtonText(true); + return true; + } + }.installOn(myButton); + } + + private void updateEnableButtonText(boolean revert) { + boolean isEnabled = myCurrentProfile.isToolEnabled(myKey); + if (revert) { + final boolean isEnabledAsFinal = isEnabled; + DisableInspectionToolAction.modifyAndCommitProjectProfile(model -> { + if (isEnabledAsFinal) { + model.disableTool(myKey.getID(), myProject); + } + else { + ((InspectionProfileImpl)model).enableTool(myKey.getID(), myProject); + } + }, myProject); + isEnabled = !isEnabled; + } + myButton.setText((isEnabled ? "Disable" : "Enable") + " inspection"); + myTitle.clear(); + myTitle.append(myName); + if (!isEnabled) { + myTitle.append(" Disabled", SimpleTextAttributes.GRAYED_ATTRIBUTES); + } + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java index 8f9eb15614d8..91eafe4a6777 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java @@ -21,7 +21,6 @@ import com.intellij.analysis.AnalysisScope; import com.intellij.analysis.AnalysisUIOptions; import com.intellij.codeHighlighting.HighlightDisplayLevel; import com.intellij.codeInsight.daemon.HighlightDisplayKey; -import com.intellij.codeInsight.highlighting.HighlightManager; import com.intellij.codeInspection.*; import com.intellij.codeInspection.ex.*; import com.intellij.codeInspection.reference.RefElement; @@ -36,30 +35,31 @@ import com.intellij.injected.editor.VirtualFileWindow; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.colors.EditorColors; -import com.intellij.openapi.editor.colors.EditorColorsManager; -import com.intellij.openapi.editor.markup.TextAttributes; -import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.*; import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.ToolWindowId; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.pom.Navigatable; import com.intellij.profile.codeInspection.InspectionProjectProfileManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; +import com.intellij.psi.*; import com.intellij.psi.util.PsiUtilCore; import com.intellij.ui.*; +import com.intellij.ui.components.JBLabel; +import com.intellij.usageView.UsageInfo; +import com.intellij.usages.impl.UsagePreviewPanel; import com.intellij.util.ConcurrencyUtil; import com.intellij.util.EditSourceOnDoubleClickHandler; import com.intellij.util.OpenSourceUtil; -import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.*; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; import org.jetbrains.annotations.NonNls; @@ -67,8 +67,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import javax.swing.event.TreeSelectionEvent; -import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.MutableTreeNode; import javax.swing.tree.TreePath; @@ -77,9 +75,8 @@ import java.awt.event.InputEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; import java.util.*; +import java.util.HashSet; import java.util.List; import java.util.concurrent.ConcurrentMap; @@ -87,11 +84,13 @@ import java.util.concurrent.ConcurrentMap; * @author max */ public class InspectionResultsView extends JPanel implements Disposable, OccurenceNavigator, DataProvider { + private static final Logger LOG = Logger.getInstance(InspectionResultsView.class); + public static final DataKey DATA_KEY = DataKey.create("inspectionView"); + private static final Key PREVIEW_EDITOR_IS_REUSED_KEY = Key.create("inspection.tool.window.preview.editor.is.reused."); private final Project myProject; private final InspectionTree myTree; - private final Browser myBrowser; private final ConcurrentMap> myGroups = ContainerUtil.newConcurrentMap(); private final OccurenceNavigator myOccurenceNavigator; private volatile InspectionProfile myInspectionProfile; @@ -110,6 +109,7 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren private final InspectionRVContentProvider myProvider; private AnAction myIncludeAction; private AnAction myExcludeAction; + private Editor myPreviewEditor; public InspectionResultsView(@NotNull final Project project, final InspectionProfile inspectionProfile, @@ -129,59 +129,26 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren myOccurenceNavigator = initOccurenceNavigator(); - myBrowser = new Browser(this); - mySplitter = new OnePixelSplitter(false, AnalysisUIOptions.getInstance(myProject).SPLITTER_PROPORTION); mySplitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myTree, SideBorder.LEFT | SideBorder.RIGHT)); - mySplitter.setSecondComponent(myBrowser); - mySplitter.addPropertyChangeListener(new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (Splitter.PROP_PROPORTION.equals(evt.getPropertyName())) { - myGlobalInspectionContext.setSplitterProportion(((Float)evt.getNewValue()).floatValue()); - } + mySplitter.addPropertyChangeListener(evt -> { + if (Splitter.PROP_PROPORTION.equals(evt.getPropertyName())) { + myGlobalInspectionContext.setSplitterProportion(((Float)evt.getNewValue()).floatValue()); } }); add(mySplitter, BorderLayout.CENTER); - myBrowser.addClickListener(new Browser.ClickListener() { - @Override - public void referenceClicked(final Browser.ClickEvent e) { - if (e.getEventType() == Browser.ClickEvent.REF_ELEMENT) { - final RefElement refElement = e.getClickedElement(); - final OpenFileDescriptor descriptor = getOpenFileDescriptor(refElement); - if (descriptor != null) { - FileEditorManager.getInstance(project).openTextEditor(descriptor, false); - } - } - else if (e.getEventType() == Browser.ClickEvent.FILE_OFFSET) { - final VirtualFile file = e.getFile(); - final OpenFileDescriptor descriptor = new OpenFileDescriptor(project, file, e.getStartOffset()); - final Editor editor = FileEditorManager.getInstance(project).openTextEditor(descriptor, true); - if (editor != null) { - final TextAttributes selectionAttributes = - EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES); - HighlightManager.getInstance(project) - .addRangeHighlight(editor, e.getStartOffset(), e.getEndOffset(), selectionAttributes, true, null); - } - } - } - }); - createActionsToolbar(); TreeUtil.selectFirstNode(myTree); } private void initTreeListeners() { - myTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() { - @Override - public void valueChanged(TreeSelectionEvent e) { - syncBrowser(); - if (isAutoScrollMode()) { - OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(InspectionResultsView.this), false); - } + myTree.getSelectionModel().addTreeSelectionListener(e -> { + syncRightPanel(); + if (isAutoScrollMode()) { + OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(InspectionResultsView.this), false); } }); @@ -364,9 +331,9 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren } @Override - public void dispose(){ + public void dispose() { + releaseEditor(myPreviewEditor); mySplitter.dispose(); - myBrowser.dispose(); myInspectionProfile = null; myDisposed = true; } @@ -406,9 +373,18 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren return null; } - private void syncBrowser() { + private void syncRightPanel() { + final Editor oldEditor = myPreviewEditor; if (myTree.getSelectionModel().getSelectionCount() != 1) { - myBrowser.showEmpty(); + if (myTree.getSelectedToolWrapper() == null) { + final JLabel multipleSelectionLabel = new JBLabel(InspectionViewNavigationPanel.getTitleText(false, false)); + multipleSelectionLabel.setVerticalAlignment(SwingConstants.TOP); + multipleSelectionLabel.setBorder(IdeBorderFactory.createEmptyBorder(5, 7, 0, 0)); + mySplitter.setSecondComponent(multipleSelectionLabel); + } + else { + showInRightPanel(myTree.getCommonSelectedElement()); + } } else { TreePath pathSelected = myTree.getSelectionModel().getLeadSelectionPath(); @@ -416,48 +392,111 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren final InspectionTreeNode node = (InspectionTreeNode)pathSelected.getLastPathComponent(); if (node instanceof RefElementNode) { final RefElementNode refElementNode = (RefElementNode)node; - final CommonProblemDescriptor problem = refElementNode.getProblem(); final RefEntity refSelected = refElementNode.getElement(); - if (problem != null) { - showInBrowser(refSelected, problem); - } - else { - showInBrowser(refSelected); - } + showInRightPanel(refSelected); } else if (node instanceof ProblemDescriptionNode) { final ProblemDescriptionNode problemNode = (ProblemDescriptionNode)node; - showInBrowser(problemNode.getElement(), problemNode.getDescriptor()); + showInRightPanel(problemNode.getElement()); } - else if (node instanceof InspectionNode) { - showInBrowser(((InspectionNode)node).getToolWrapper()); + else if (node instanceof InspectionNode || node instanceof InspectionPackageNode || node instanceof InspectionModuleNode) { + showInRightPanel(null); + } + else if (node instanceof InspectionRootNode || node instanceof InspectionGroupNode || node instanceof InspectionSeverityGroupNode) { + mySplitter.setSecondComponent(new InspectionViewNavigationPanel(node, myTree)); } else { - myBrowser.showEmpty(); + LOG.error("Unexpected node: " + node.getClass()); } } } + if (oldEditor != null) { + if (Boolean.TRUE.equals(oldEditor.getUserData(PREVIEW_EDITOR_IS_REUSED_KEY))) { + oldEditor.putUserData(PREVIEW_EDITOR_IS_REUSED_KEY, null); + } + else { + releaseEditor(oldEditor); + } + } } - private void showInBrowser(final RefEntity refEntity) { + private void showInRightPanel(@Nullable final RefEntity refEntity) { Cursor currentCursor = getCursor(); - setCursor(new Cursor(Cursor.WAIT_CURSOR)); - myBrowser.showPageFor(refEntity); - setCursor(currentCursor); + try { + setCursor(new Cursor(Cursor.WAIT_CURSOR)); + final JPanel editorPanel = new JPanel(); + editorPanel.setLayout(new BorderLayout()); + CommonProblemDescriptor[] descriptors = myTree.getSelectedDescriptors(); + editorPanel.add(createBaseRightComponentFor(descriptors, refEntity), BorderLayout.CENTER); + if (descriptors.length > 0) { + final InspectionToolWrapper tool = myTree.getSelectedToolWrapper(); + LOG.assertTrue(tool != null); + editorPanel.add(new QuickFixToolbar(myTree, + myProject, + myPreviewEditor, + myProvider.getQuickFixes(tool, myTree)), + BorderLayout.NORTH); + } + mySplitter.setSecondComponent(editorPanel); + } + finally { + setCursor(currentCursor); + } } - private void showInBrowser(@NotNull InspectionToolWrapper toolWrapper) { - Cursor currentCursor = getCursor(); - setCursor(new Cursor(Cursor.WAIT_CURSOR)); - myBrowser.showDescription(toolWrapper); - setCursor(currentCursor); + private boolean reuseEditorFor(Document document) { + return myPreviewEditor != null && !myPreviewEditor.isDisposed() && myPreviewEditor.getDocument() == document; } - private void showInBrowser(final RefEntity refEntity, CommonProblemDescriptor descriptor) { - Cursor currentCursor = getCursor(); - setCursor(new Cursor(Cursor.WAIT_CURSOR)); - myBrowser.showPageFor(refEntity, descriptor); - setCursor(currentCursor); + private JComponent createBaseRightComponentFor(CommonProblemDescriptor[] descriptors, + RefEntity selectedEntity) { + final int count = descriptors.length; + if (selectedEntity instanceof RefElement && !(((RefElement)selectedEntity).getElement() instanceof PsiDirectory)) { + PsiElement selectedElement = ((RefElement)selectedEntity).getElement(); + if (count == 1) { + final CommonProblemDescriptor descriptor = descriptors[0]; + if (descriptor instanceof ProblemDescriptorBase) { + final PsiElement element = ((ProblemDescriptorBase)descriptor).getPsiElement(); + if (element != null) { + selectedElement = element; + } + } + } + final PsiFile file = selectedElement.getContainingFile(); + final Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file); + + if (reuseEditorFor(document)) { + myPreviewEditor.putUserData(PREVIEW_EDITOR_IS_REUSED_KEY, true); + } + else { + myPreviewEditor = EditorFactory.getInstance().createEditor(document, myProject, file.getVirtualFile(), true); + final EditorSettings settings = myPreviewEditor.getSettings(); + settings.setLineNumbersShown(false); + settings.setLineMarkerAreaShown(false); + settings.setAdditionalColumnsCount(0); + settings.setAdditionalLinesCount(0); + settings.setLeadingWhitespaceShown(true); + settings.setRightMarginShown(true); + settings.setRightMargin(60); + UsagePreviewPanel.highlight(Collections.emptyList(), myPreviewEditor, myProject); + } + + if (count == 1) { + final PsiElement finalSelectedElement = selectedElement; + ApplicationManager.getApplication().invokeLater(() -> { + if (myPreviewEditor != null && !myPreviewEditor.isDisposed()) { + PsiDocumentManager.getInstance(myProject).commitAllDocuments(); + myPreviewEditor.getCaretModel().moveToOffset(finalSelectedElement.getTextOffset()); + myPreviewEditor.getScrollingModel().scrollToCaret(ScrollType.CENTER); + } + }, ModalityState.NON_MODAL); + } + return myPreviewEditor.getComponent(); + } + else if (selectedEntity == null) { + return new InspectionNodeInfo(myTree.getSelectedToolWrapper(), myProject); + } + return new JPanel(); } @NotNull @@ -477,7 +516,7 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren } private void registerActionShortcuts(@NotNull InspectionToolPresentation presentation) { - final QuickFixAction[] fixes = presentation.getQuickFixes(RefEntity.EMPTY_ELEMENTS_ARRAY); + final QuickFixAction[] fixes = presentation.getQuickFixes(RefEntity.EMPTY_ELEMENTS_ARRAY, null); if (fixes != null) { for (QuickFixAction fix : fixes) { fix.registerCustomShortcutSet(fix.getShortcutSet(), this); @@ -833,6 +872,10 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren return myScope; } + public void updateRightPanel() { + syncRightPanel(); + } + private class CloseAction extends AnAction implements DumbAware { private CloseAction() { super(CommonBundle.message("action.close"), null, AllIcons.Actions.Cancel); @@ -905,4 +948,10 @@ public class InspectionResultsView extends JPanel implements Disposable, Occuren } } } + + private static void releaseEditor(@Nullable Editor editor) { + if (editor != null && !editor.isDisposed()) { + EditorFactory.getInstance().releaseEditor(editor); + } + } } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionToolPresentation.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionToolPresentation.java index c7f632575815..fda43559a918 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionToolPresentation.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionToolPresentation.java @@ -67,7 +67,7 @@ public interface InspectionToolPresentation extends ProblemDescriptionsProcessor @NotNull Set getModuleProblems(); @Nullable - QuickFixAction[] getQuickFixes(@NotNull final RefEntity[] refElements); + QuickFixAction[] getQuickFixes(@NotNull final RefEntity[] refElements, @Nullable CommonProblemDescriptor[] descriptors); @NotNull Map getProblemElements(); @NotNull @@ -86,6 +86,8 @@ public interface InspectionToolPresentation extends ProblemDescriptionsProcessor GlobalInspectionContextImpl getContext(); void ignoreProblem(RefEntity refEntity, CommonProblemDescriptor problem, int idx); @Nullable - QuickFixAction[] extractActiveFixes(@NotNull RefEntity[] refElements, @NotNull Map> actions); + QuickFixAction[] extractActiveFixes(@NotNull RefEntity[] refElements, + @NotNull Map descriptorMap, + @Nullable CommonProblemDescriptor[] allowedDescriptors); void exportResults(@NotNull final Element parentNode); } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTree.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTree.java index d1c3c3a2d7f1..ce47e3fff67f 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTree.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionTree.java @@ -109,6 +109,9 @@ public class InspectionTree extends Tree { Object[] nodes = path.getPath(); for (int j = nodes.length - 1; j >= 0; j--) { Object node = nodes[j]; + if (node instanceof InspectionGroupNode) { + return null; + } if (node instanceof InspectionNode) { InspectionToolWrapper wrapper = ((InspectionNode)node).getToolWrapper(); if (toolWrapper == null) { @@ -125,6 +128,36 @@ public class InspectionTree extends Tree { return toolWrapper; } + @Nullable + public RefEntity getCommonSelectedElement() { + final Object node = getCommonSelectedNode(); + return node instanceof RefElementNode ? ((RefElementNode)node).getElement() : null; + } + + @Nullable + private Object getCommonSelectedNode() { + final TreePath[] paths = getSelectionPaths(); + if (paths == null) return null; + final Object[][] resolvedPaths = new Object[paths.length][]; + for (int i = 0; i < paths.length; i++) { + TreePath path = paths[i]; + resolvedPaths[i] = path.getPath(); + } + + Object currentCommonNode = null; + for (int i = 0; i < resolvedPaths[0].length; i++) { + final Object currentNode = resolvedPaths[0][i]; + for (int j = 1; j < resolvedPaths.length; j++) { + final Object o = resolvedPaths[j][i]; + if (!o.equals(currentNode)) { + return currentCommonNode; + } + } + currentCommonNode = currentNode; + } + return currentCommonNode; + } + @NotNull public RefEntity[] getSelectedElements() { TreePath[] selectionPaths = getSelectionPaths(); @@ -164,7 +197,6 @@ public class InspectionTree extends Tree { } public CommonProblemDescriptor[] getSelectedDescriptors() { - final InspectionToolWrapper toolWrapper = getSelectedToolWrapper(); if (getSelectionCount() == 0) return EMPTY_DESCRIPTORS; final TreePath[] paths = getSelectionPaths(); final LinkedHashSet descriptors = new LinkedHashSet(); @@ -177,7 +209,9 @@ public class InspectionTree extends Tree { private static void traverseDescriptors(InspectionTreeNode node, LinkedHashSet descriptors){ if (node instanceof ProblemDescriptionNode) { - descriptors.add(((ProblemDescriptionNode)node).getDescriptor()); + if (node.isValid() && !node.isResolved()) { + descriptors.add(((ProblemDescriptionNode)node).getDescriptor()); + } } for(int i = node.getChildCount() - 1; i >= 0; i--){ traverseDescriptors((InspectionTreeNode)node.getChildAt(i), descriptors); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionViewNavigationPanel.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionViewNavigationPanel.java new file mode 100644 index 000000000000..4579854165ee --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionViewNavigationPanel.java @@ -0,0 +1,60 @@ +/* + * 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://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.codeInspection.ui; + +import com.intellij.ui.IdeBorderFactory; +import com.intellij.ui.components.JBLabel; +import com.intellij.ui.components.labels.LinkLabel; +import com.intellij.util.ui.JBUI; +import com.intellij.util.ui.tree.TreeUtil; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeNode; +import java.awt.*; + +/** + * @author Dmitry Batkovich + */ +public class InspectionViewNavigationPanel extends JPanel { + public InspectionViewNavigationPanel(InspectionTreeNode node, InspectionTree tree) { + setLayout(new BorderLayout()); + setBorder(IdeBorderFactory.createEmptyBorder(5, 7, 0, 0)); + final String titleLabelText = getTitleText(node instanceof InspectionRootNode, true); + add(new JBLabel(titleLabelText), BorderLayout.NORTH); + final JPanel links = new JPanel(); + links.setLayout(new BoxLayout(links, BoxLayout.Y_AXIS)); + links.add(Box.createVerticalStrut(JBUI.scale(10))); + add(BorderLayout.CENTER, links); + for (int i = 0; i < node.getChildCount(); i++) { + final TreeNode child = node.getChildAt(i); + final LinkLabel link = new LinkLabel(child.toString(), null) { + @Override + public void doClick() { + TreeUtil.selectInTree((DefaultMutableTreeNode)child, true, tree); + } + }; + link.setBorder(IdeBorderFactory.createEmptyBorder(1, 17, 3, 1)); + links.add(link); + } + } + + @NotNull + public static String getTitleText(boolean addGroupWord, boolean addColon) { + return "Select inspection " + (addGroupWord ? "group" : "") + " to see problems" + (addColon ? ":" : "."); + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/QuickFixToolbar.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/QuickFixToolbar.java new file mode 100644 index 000000000000..dcc5f4aa49ce --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/QuickFixToolbar.java @@ -0,0 +1,230 @@ +/* + * 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://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.codeInspection.ui; + +import com.intellij.codeInspection.CommonProblemDescriptor; +import com.intellij.codeInspection.QuickFix; +import com.intellij.codeInspection.ex.InspectionToolWrapper; +import com.intellij.codeInspection.ex.QuickFixAction; +import com.intellij.codeInspection.reference.RefElement; +import com.intellij.codeInspection.ui.actions.SuppressActionWrapper; +import com.intellij.codeInspection.ui.actions.occurrences.GoToSubsequentOccurrenceAction; +import com.intellij.icons.AllIcons; +import com.intellij.ide.DataManager; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.actionSystem.ex.ComboBoxAction; +import com.intellij.openapi.actionSystem.ex.CustomComponentAction; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiNamedElement; +import com.intellij.ui.ClickListener; +import com.intellij.ui.IdeBorderFactory; +import com.intellij.ui.SimpleColoredComponent; +import com.intellij.ui.SimpleTextAttributes; +import com.intellij.ui.components.JBLabel; +import com.intellij.util.ui.JBUI; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.tree.TreePath; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.util.*; +import java.util.List; +import java.util.function.Supplier; + +/** + * @author Dmitry Batkovich + */ +public class QuickFixToolbar extends JPanel { + private static final int MAX_FIX_COUNT = 2; + + public QuickFixToolbar(@NotNull InspectionTree tree, + @NotNull Project project, + @Nullable Editor editor, + @Nullable QuickFixAction[] fixes) { + final boolean hasFixes = fixes != null && fixes.length != 0; + CommonProblemDescriptor[] descriptors = tree.getSelectedDescriptors(); + int problemCount = descriptors.length; + final boolean multipleDescriptors = problemCount > 1; + + setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); + List panels = new ArrayList<>(); + for (int i = 0; i < (multipleDescriptors ? 2 : 1); i++) { + final JPanel line = new JPanel(); + line.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); + panels.add((JPanel)add(line)); + } + if (multipleDescriptors || !hasFixes) { + panels.get(0).setBorder(IdeBorderFactory.createEmptyBorder(5, 0, 0, 0)); + } + + //fill(getBulbPlacement(hasFixes), QuickFixToolbar::createBulbIcon, panels); + fill(getDescriptionLabelPlacement(multipleDescriptors), + () -> getLabel(fixes, tree.getSelectionCount() == 1 ? (InspectionTreeNode)tree.getSelectionPath().getLastPathComponent() : null, problemCount), panels); + fill(getFixesPlacement(hasFixes, multipleDescriptors), () -> createFixPanel(fixes), panels); + fill(getSuppressPlacement(multipleDescriptors), () -> createSuppressionCombo(tree.getSelectedToolWrapper() + , tree.getSelectionPath(), project), panels); + fill(multipleDescriptors && editor != null ? 1 : -1, () -> ActionManager.getInstance().createActionToolbar("", GoToSubsequentOccurrenceAction.createNextPreviousActions( + editor, descriptors), true).getComponent(), panels); + } + + @NotNull + private static JComponent getLabel(QuickFixAction[] fixes, InspectionTreeNode targetNode, int problemsCount) { + final String targetName = targetNode instanceof RefElementNode ? ((RefElementNode)targetNode).getElement().getName() : null; + SimpleColoredComponent label = new SimpleColoredComponent(); + boolean hasFixesNonIntersectedFixes = fixes != null && fixes.length == 0; + boolean hasFixes = fixes != null && fixes.length != 0; + label.append((hasFixes ? " Fix " : " ") + problemsCount + " problems" + (targetName == null ? "" : (" in " + targetName)) + ( + hasFixesNonIntersectedFixes + ? ":" : ""), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); + if (hasFixesNonIntersectedFixes) { + label.append(" select a single problem to see its quick fixes"); + } + + if (!hasFixes) { + label.setBorder(IdeBorderFactory.createEmptyBorder(0, 3, 6, 0)); + } + return label; + } + + @NotNull + private static JLabel createBulbIcon() { + final JLabel label = new JLabel(AllIcons.Actions.IntentionBulb); + label.setBorder(IdeBorderFactory.createEmptyBorder(0, 10, 0, 0)); + return label; + } + + private static JComponent createSuppressionCombo(@NotNull final InspectionToolWrapper toolWrapper, + @NotNull final TreePath path, + @NotNull final Project project) { + final ComboBoxAction action = new ComboBoxAction() { + { + getTemplatePresentation().setText("Suppress"); + } + + @NotNull + @Override + protected ActionGroup createPopupActionGroup(JComponent button) { + return new SuppressActionWrapper(project, toolWrapper, path); + } + }; + action.setSmallVariant(false); + return action.createCustomComponent(action.getTemplatePresentation()); + } + + @NotNull + private static JPanel createFixPanel(QuickFixAction[] fixes) { + JPanel fixPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, JBUI.scale(3), JBUI.scale(5))); + if (fixes.length > MAX_FIX_COUNT) { + final ComboBoxAction fixComboBox = new ComboBoxAction() { + { + getTemplatePresentation().setText("Apply quick fixes"); + getTemplatePresentation().setIcon(AllIcons.Actions.CreateFromUsage); + setSmallVariant(false); + } + + @NotNull + @Override + protected DefaultActionGroup createPopupActionGroup(JComponent button) { + final DefaultActionGroup actionGroup = new DefaultActionGroup(); + for (QuickFixAction fix : fixes) { + actionGroup.add(fix); + } + return actionGroup; + } + }; + fixPanel.add(fixComboBox.createCustomComponent(fixComboBox.getTemplatePresentation())); + } + else { + for (QuickFixAction fix : fixes) { + fixPanel.add(createQuickFixButton(fix)); + } + } + return fixPanel; + } + + private static void fill(int row, + @NotNull Supplier componentSupplier, + @NotNull List parent) { + if (row == -1) { + return; + } + final JPanel rowPanel = parent.get(row); + rowPanel.add(componentSupplier.get()); + } + + private static int getSuppressPlacement(boolean multipleDescriptors) { + return multipleDescriptors ? 1 : 0; + } + + private static int getFixesPlacement(boolean hasQuickFixes, boolean multipleDescriptors) { + return hasQuickFixes ? multipleDescriptors ? 1 : 0 : -1; + } + + private static int getDescriptionLabelPlacement(boolean multipleDescriptors) { + return multipleDescriptors ? 0 : -1; + } + + private static int getBulbPlacement(boolean hasQuickFixes) { + return hasQuickFixes ? 0 : -1; + } + + private static JComponent createQuickFixButton(@NotNull QuickFixAction fix) { + final MyCustomComponentLocalQuickFixWrapper action = new MyCustomComponentLocalQuickFixWrapper(fix); + return action.createCustomComponent(action.getTemplatePresentation()); + } + + private static class MyCustomComponentLocalQuickFixWrapper extends AnAction implements CustomComponentAction { + private QuickFixAction myUnderlying; + + public MyCustomComponentLocalQuickFixWrapper(@NotNull QuickFixAction underlying) { + myUnderlying = underlying; + copyFrom(underlying); + } + + + @Override + public JComponent createCustomComponent(Presentation presentation) { + final JButton button = new JButton(presentation.getText()); + button.setIcon(presentation.getIcon()); + new ClickListener() { + @Override + public boolean onClick(@NotNull MouseEvent event, int clickCount) { + actionPerformed(AnActionEvent.createFromAnAction(MyCustomComponentLocalQuickFixWrapper.this, + event, + "LOCAL_QUICK_FIX_WRAPPER_PANEL", + DataManager.getInstance().getDataContext(button))); + return true; + } + }.installOn(button); + return button; + } + + + @Override + public void update(AnActionEvent e) { + myUnderlying.update(e); + } + + @Override + public void actionPerformed(AnActionEvent e) { + myUnderlying.actionPerformed(e); + } + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/RefElementNode.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/RefElementNode.java index bfd717fc518c..1c7642539d05 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/RefElementNode.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/RefElementNode.java @@ -71,11 +71,6 @@ public class RefElementNode extends InspectionTreeNode { return myIcon.getIcon(); } - @Override - public int getProblemCount() { - return Math.max(1, super.getProblemCount()); - } - public String toString() { final RefEntity element = getElement(); if (element == null || !element.isValid()) { diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/SuppressActionWrapper.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/SuppressActionWrapper.java index 20db55e49ae9..3b0399299785 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/SuppressActionWrapper.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/SuppressActionWrapper.java @@ -32,6 +32,7 @@ import com.intellij.codeInspection.ui.RefElementNode; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.Separator; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.diagnostic.Logger; @@ -49,6 +50,8 @@ import org.jetbrains.annotations.Nullable; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; +import java.util.Arrays; +import java.util.Comparator; import java.util.Set; public class SuppressActionWrapper extends ActionGroup { @@ -60,7 +63,7 @@ public class SuppressActionWrapper extends ActionGroup { public SuppressActionWrapper(@NotNull final Project project, @NotNull final InspectionToolWrapper toolWrapper, - @NotNull final TreePath[] paths) { + @NotNull final TreePath... paths) { super(InspectionsBundle.message("suppress.inspection.problem"), false); myProject = project; myManager = (InspectionManagerEx)InspectionManager.getInstance(myProject); @@ -83,14 +86,25 @@ public class SuppressActionWrapper extends ActionGroup { @Override @NotNull - public SuppressTreeAction[] getChildren(@Nullable final AnActionEvent e) { + public AnAction[] getChildren(@Nullable final AnActionEvent e) { final SuppressIntentionAction[] suppressActions = InspectionManagerEx.getSuppressActions(myToolWrapper); if (suppressActions == null || suppressActions.length == 0) return new SuppressTreeAction[0]; - final SuppressTreeAction[] actions = new SuppressTreeAction[suppressActions.length]; + final AnAction[] actions = new AnAction[suppressActions.length + 1]; for (int i = 0; i < suppressActions.length; i++) { final SuppressIntentionAction suppressAction = suppressActions[i]; actions[i] = new SuppressTreeAction(suppressAction); } + actions[suppressActions.length] = Separator.getInstance(); + Arrays.sort(actions, new Comparator() { + @Override + public int compare(AnAction a1, AnAction a2) { + return getWeight(a1) - getWeight(a2); + } + + public int getWeight(AnAction a) { + return a instanceof Separator ? 0 : ((SuppressTreeAction)a).isSuppressAll() ? 1 : -1; + } + }); return actions; } @@ -219,5 +233,9 @@ public class SuppressActionWrapper extends ActionGroup { } return false; } + + public boolean isSuppressAll() { + return mySuppressAction.isSuppressAll(); + } } } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/occurrences/GoToSubsequentOccurrenceAction.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/occurrences/GoToSubsequentOccurrenceAction.java new file mode 100644 index 000000000000..7c9ddf75459a --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/occurrences/GoToSubsequentOccurrenceAction.java @@ -0,0 +1,128 @@ +/* + * 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://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.codeInspection.ui.actions.occurrences; + +import com.intellij.codeInspection.CommonProblemDescriptor; +import com.intellij.codeInspection.ProblemDescriptorBase; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.ActionGroup; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.ScrollType; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; + +/** + * @author Dmitry Batkovich + */ +public class GoToSubsequentOccurrenceAction extends AnAction { + private final boolean myNext; + private OccurrencesManager myManager; + + private GoToSubsequentOccurrenceAction(boolean next, OccurrencesManager manager) { + myNext = next; + myManager = manager; + getTemplatePresentation().setIcon(next ? AllIcons.Actions.NextOccurence : AllIcons.Actions.PreviousOccurence); + getTemplatePresentation().setDescription("Navigate to the " + (next ? "Next" : "Previous") + " Occurrence"); + } + + @Override + public void update(AnActionEvent e) { + e.getPresentation().setEnabledAndVisible(myManager.hasValidSubsequent()); + } + + @Override + public void actionPerformed(AnActionEvent e) { + myManager.selectSubsequent(myNext); + } + + public static ActionGroup createNextPreviousActions(@NotNull Editor editor, @NotNull CommonProblemDescriptor[] descriptors) { + OccurrencesManager manager = new OccurrencesManager(editor, descriptors); + return new DefaultActionGroup(new GoToSubsequentOccurrenceAction(false, manager), new GoToSubsequentOccurrenceAction(true, manager)); + } + + private static class OccurrencesManager { + private final Editor myEditor; + private final ProblemDescriptorBase[] myChildren; + + private int mySelected = -1; + + private OccurrencesManager(@NotNull Editor editor, @NotNull CommonProblemDescriptor[] descriptors) { + myEditor = editor; + myChildren = Arrays.stream(descriptors) + .filter(d -> d instanceof ProblemDescriptorBase) + .map(d -> (ProblemDescriptorBase) d) + .filter(d -> { + PsiElement element = d.getPsiElement(); + return element != null && element.isValid(); + }) + .sorted((o1, o2) -> o1.getPsiElement().getTextOffset() - o2.getPsiElement().getTextOffset()) + .toArray(ProblemDescriptorBase[]::new); + selectSubsequent(true); + } + + public void selectSubsequent(boolean next) { + int subsequentIdz = mySelected; + while (true) { + subsequentIdz += next ? 1 : -1; + subsequentIdz += myChildren.length; + subsequentIdz %= myChildren.length; + if (mySelected == subsequentIdz) { + throw new IllegalStateException("Selection is unavailable"); + } + ProblemDescriptorBase descriptorBase = myChildren[subsequentIdz]; + if (descriptorBase.getPsiElement().isValid()) { + mySelected = subsequentIdz; + ApplicationManager.getApplication().invokeLater(() -> { + if (myEditor == null || myEditor.isDisposed()) return; + PsiElement toSelect = descriptorBase.getPsiElement(); + PsiDocumentManager.getInstance(myEditor.getProject()).commitAllDocuments(); + myEditor.getCaretModel().moveToOffset(toSelect.getTextOffset()); + myEditor.getScrollingModel().scrollToCaret(ScrollType.CENTER); + myEditor.getSelectionModel().setSelection(toSelect.getTextRange().getStartOffset(), toSelect.getTextRange().getEndOffset()); + }, ModalityState.NON_MODAL); + return; + } + } + } + + public boolean hasValidSubsequent() { + if (myEditor.isDisposed()) { + return false; + } + int subsequentIdz = mySelected; + while (true) { + subsequentIdz += 1 + myChildren.length; + subsequentIdz %= myChildren.length; + if (mySelected == subsequentIdz) { + return false; + } + ProblemDescriptorBase descriptorBase = myChildren[subsequentIdz]; + PsiElement element = descriptorBase.getPsiElement(); + if (element != null && element.isValid()) { + return true; + } + } + } + } +} diff --git a/platform/lang-impl/src/com/intellij/ide/actions/GotoActionBase.java b/platform/lang-impl/src/com/intellij/ide/actions/GotoActionBase.java index bdbd798ca787..ecce07af15e0 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/GotoActionBase.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/GotoActionBase.java @@ -25,6 +25,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; import com.intellij.openapi.keymap.KeymapUtil; +import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; @@ -67,6 +68,9 @@ public abstract class GotoActionBase extends AnAction { myHistoryIndex = strings == null || strings.size() <= 1 || !ourLastStrings.containsKey(myInAction) ? 0 : 1; gotoActionPerformed(e); } + catch (ProcessCanceledException e1) { + myInAction = null; + } catch (Throwable t) { LOG.error(t); myInAction = null; diff --git a/platform/lang-impl/src/com/intellij/ide/util/projectWizard/AbstractNewProjectStep.java b/platform/lang-impl/src/com/intellij/ide/util/projectWizard/AbstractNewProjectStep.java index 29e35201fcbb..bb56047a0422 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/projectWizard/AbstractNewProjectStep.java +++ b/platform/lang-impl/src/com/intellij/ide/util/projectWizard/AbstractNewProjectStep.java @@ -57,8 +57,7 @@ public class AbstractNewProjectStep extends DefaultActionGroup implements DumbAw NullableConsumer callback = customization.createCallback(); ProjectSpecificAction projectSpecificAction = customization.createProjectSpecificAction(callback); - - addAll(projectSpecificAction.getChildren(null)); + addProjectSpecificAction(projectSpecificAction); DirectoryProjectGenerator[] generators = customization.getProjectGenerators(); customization.setUpBasicAction(projectSpecificAction, generators); @@ -67,6 +66,10 @@ public class AbstractNewProjectStep extends DefaultActionGroup implements DumbAw addAll(customization.getExtraActions(callback)); } + protected void addProjectSpecificAction(@NotNull final ProjectSpecificAction projectSpecificAction) { + addAll(projectSpecificAction.getChildren(null)); + } + protected static abstract class Customization { @NotNull protected ProjectSpecificAction createProjectSpecificAction(@NotNull final NullableConsumer callback) { diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java index e7eaa673acd0..01ab1f67dace 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java @@ -92,8 +92,6 @@ import java.io.StringReader; import java.util.*; import java.util.List; -import com.intellij.util.containers.Queue; - /** * User: anna * Date: 31-May-2006 @@ -773,9 +771,9 @@ public class SingleInspectionProfilePanel extends JPanel { } // TODO 134099: see IntentionDescriptionPanel#readHTML - private boolean readHTML(String text) { + public static boolean readHTML(JEditorPane browser, String text) { try { - myBrowser.read(new StringReader(text), null); + browser.read(new StringReader(text), null); return true; } catch (IOException ignored) { @@ -783,10 +781,10 @@ public class SingleInspectionProfilePanel extends JPanel { } } - // TODO 134099: see IntentionDescriptionPanel#toHTML - private String toHTML(String text) { - final HintHint hintHint = new HintHint(myBrowser, new Point(0, 0)); - hintHint.setFont(UIUtil.getLabelFont()); + // TODO 134099: see IntentionDescriptionPanel#setHTML + public static String toHTML(JEditorPane browser, String text, boolean miniFontSize) { + final HintHint hintHint = new HintHint(browser, new Point(0, 0)); + hintHint.setFont(miniFontSize ? UIUtil.getLabelFont(UIUtil.FontSize.SMALL) : UIUtil.getLabelFont()); return HintUtil.prepareHintText(text, hintHint); } @@ -806,8 +804,8 @@ public class SingleInspectionProfilePanel extends JPanel { final Descriptor defaultDescriptor = singleNode.getDefaultDescriptor(); final String description = defaultDescriptor.loadDescription(); try { - if (!readHTML(SearchUtil.markup(toHTML(description), myProfileFilter.getFilter()))) { - readHTML(toHTML("" + UNDER_CONSTRUCTION + "")); + if (!readHTML(myBrowser, SearchUtil.markup(toHTML(myBrowser, description, false), myProfileFilter.getFilter()))) { + readHTML(myBrowser, toHTML(myBrowser, "" + UNDER_CONSTRUCTION + "", false)); } } catch (Throwable t) { @@ -819,11 +817,11 @@ public class SingleInspectionProfilePanel extends JPanel { } else { - readHTML(toHTML("Can't find inspection description.")); + readHTML(myBrowser, toHTML(myBrowser, "Can't find inspection description.", false)); } } else { - readHTML(toHTML("Multiple inspections are selected. You can edit them as a single inspection.")); + readHTML(myBrowser, toHTML(myBrowser, "Multiple inspections are selected. You can edit them as a single inspection.", false)); } myOptionsPanel.removeAll(); @@ -1021,7 +1019,7 @@ public class SingleInspectionProfilePanel extends JPanel { private void initOptionsAndDescriptionPanel() { myOptionsPanel.removeAll(); - readHTML(EMPTY_HTML); + readHTML(myBrowser, EMPTY_HTML); myOptionsPanel.validate(); myOptionsPanel.repaint(); } diff --git a/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java b/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java index a22c14f226cd..7471a1657789 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java +++ b/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java @@ -585,7 +585,9 @@ public abstract class BaseRefactoringProcessor implements Runnable { @Override public String getMessage() { - return StringUtil.join(messages, "\n"); + List result = new ArrayList<>(messages); + Collections.sort(result); + return StringUtil.join(result, "\n"); } } diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/ComboBoxAction.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/ComboBoxAction.java index ddd7d1eedc5f..db8c5039514f 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/ComboBoxAction.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/ComboBoxAction.java @@ -81,7 +81,7 @@ public abstract class ComboBoxAction extends AnAction implements CustomComponent DataContext context = e.getDataContext(); Project project = e.getProject(); if (project == null) return; - DefaultActionGroup group = createPopupActionGroup(button); + ActionGroup group = createPopupActionGroup(button); ListPopup popup = JBPopupFactory.getInstance().createActionGroupPopup( myPopupTitle, group, context, false, shouldShowDisabledActions(), false, null, getMaxRows(), getPreselectCondition()); popup.setMinimumSize(new Dimension(getMinWidth(), getMinHeight())); @@ -124,7 +124,7 @@ public abstract class ComboBoxAction extends AnAction implements CustomComponent @NotNull - protected abstract DefaultActionGroup createPopupActionGroup(JComponent button); + protected abstract ActionGroup createPopupActionGroup(JComponent button); protected int getMaxRows() { return 30; @@ -152,7 +152,9 @@ public abstract class ComboBoxAction extends AnAction implements CustomComponent setModel(new MyButtonModel()); setHorizontalAlignment(LEFT); setFocusable(false); - putClientProperty("styleCombo", Boolean.TRUE); + if (isSmallVariant()) { + putClientProperty("styleCombo", Boolean.TRUE); + } Insets margins = getMargin(); setMargin(JBUI.insets(margins.top, 2, margins.bottom, 2)); if (isSmallVariant()) { @@ -277,7 +279,7 @@ public abstract class ComboBoxAction extends AnAction implements CustomComponent } protected JBPopup createPopup(Runnable onDispose) { - DefaultActionGroup group = createPopupActionGroup(this); + ActionGroup group = createPopupActionGroup(this); DataContext context = getDataContext(); ListPopup popup = JBPopupFactory.getInstance().createActionGroupPopup( @@ -410,7 +412,6 @@ public abstract class ComboBoxAction extends AnAction implements CustomComponent final Dimension size = getSize(); if (SystemInfo.isMac && UIUtil.isUnderIntelliJLaF()) { - putClientProperty("styleCombo", Boolean.TRUE); super.paint(g); } else { UISettings.setupAntialiasing(g); diff --git a/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java b/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java index 50238a183fb5..8b8f5e43a4a2 100644 --- a/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java +++ b/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java @@ -570,7 +570,6 @@ public class JBScrollPane extends JScrollPane { // so that if we don't ask after resetting the bounds we may have gotten the wrong answer. if (viewport != null) { viewport.setBounds(bounds); - pane.setComponentZOrder(viewport, pane.getComponentCount() - 1); if (scrollable != null && hsbOpaque && vsbOpaque) { viewTracksViewportWidth = scrollable.getScrollableTracksViewportWidth(); viewTracksViewportHeight = scrollable.getScrollableTracksViewportHeight(); @@ -655,7 +654,6 @@ public class JBScrollPane extends JScrollPane { int overlapY = !hsbOnTop ? 0 : overlapHeight; vsb.setBounds(vsbBounds.x, vsbBounds.y + overlapY, vsbBounds.width, vsbBounds.height - overlapHeight); vsb.putClientProperty(Alignment.class, vsbOnLeft ? Alignment.LEFT : Alignment.RIGHT); - pane.setComponentZOrder(vsb, 0); } // Modify the bounds of the translucent scroll bar. if (!vsbOpaque) { @@ -714,6 +712,20 @@ public class JBScrollPane extends JScrollPane { vsbOnLeft ? rowHeadBounds.width : vsbBounds.width, hsbOnTop ? hsbBounds.height : colHeadBounds.height); } + if (!vsbOpaque && vsbNeeded || !hsbOpaque && hsbNeeded) { + fixComponentZOrder(vsb, 0); + fixComponentZOrder(viewport, -1); + } + } + + private static void fixComponentZOrder(Component component, int index) { + if (component != null) { + Container parent = component.getParent(); + synchronized (parent.getTreeLock()) { + if (index < 0) index += parent.getComponentCount(); + parent.setComponentZOrder(component, index); + } + } } private void adjustForVSB(Rectangle bounds, Insets insets, Rectangle vsbBounds, boolean vsbOpaque, boolean vsbOnLeft) { diff --git a/platform/platform-impl/src/com/intellij/concurrency/JobLauncherImpl.java b/platform/platform-impl/src/com/intellij/concurrency/JobLauncherImpl.java index ddcb275f1391..41a35bc2e87a 100644 --- a/platform/platform-impl/src/com/intellij/concurrency/JobLauncherImpl.java +++ b/platform/platform-impl/src/com/intellij/concurrency/JobLauncherImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 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. @@ -96,7 +96,7 @@ public class JobLauncherImpl extends JobLauncher { Boolean result = processImmediatelyIfTooFew(things, wrapper, runInReadAction, thingProcessor); if (result != null) return result.booleanValue(); - ApplierCompleter applier = new ApplierCompleter(null, runInReadAction, wrapper, things, thingProcessor, 0, things.size(), null); + ApplierCompleter applier = new ApplierCompleter<>(null, runInReadAction, wrapper, things, thingProcessor, 0, things.size(), null); try { pool.invoke(applier); if (applier.throwable != null) throw applier.throwable; @@ -109,10 +109,7 @@ public class JobLauncherImpl extends JobLauncher { // then task2 calls checkCancel() and get here return false; } - catch (RuntimeException e) { - throw e; - } - catch (Error e) { + catch (RuntimeException | Error e) { throw e; } catch (Throwable e) { @@ -125,7 +122,7 @@ public class JobLauncherImpl extends JobLauncher { // if {@code things} are too few to be processed in the real pool, returns TRUE if processed successfully, FALSE if not // returns null if things need to be processed in the real pool private static Boolean processImmediatelyIfTooFew(@NotNull final List things, - final ProgressIndicator progress, + @NotNull final ProgressIndicator progress, boolean runInReadAction, @NotNull final Processor thingProcessor) { // commit can be invoked from within write action @@ -136,24 +133,16 @@ public class JobLauncherImpl extends JobLauncher { if (things.size() <= 1 || JobSchedulerImpl.CORES_COUNT <= CORES_FORK_THRESHOLD) { final AtomicBoolean result = new AtomicBoolean(true); - Runnable runnable = new Runnable() { - @Override - public void run() { - ProgressManager.getInstance().executeProcessUnderProgress(new Runnable() { - @Override - public void run() { - //noinspection ForLoopReplaceableByForEach - for (int i = 0; i < things.size(); i++) { - T thing = things.get(i); - if (!thingProcessor.process(thing)) { - result.set(false); - break; - } - } - } - }, progress); + Runnable runnable = () -> ProgressManager.getInstance().executeProcessUnderProgress(() -> { + //noinspection ForLoopReplaceableByForEach + for (int i = 0; i < things.size(); i++) { + T thing = things.get(i); + if (!thingProcessor.process(thing)) { + result.set(false); + break; + } } - }; + }, progress); if (runInReadAction) { if (!ApplicationManagerEx.getApplicationEx().tryRunReadAction(runnable)) return false; } @@ -318,35 +307,32 @@ public class JobLauncherImpl extends JobLauncher { @Override public Boolean call() throws Exception { - ProgressManager.getInstance().executeProcessUnderProgress(new Runnable() { - @Override - public void run() { - try { - while (true) { - progress.checkCanceled(); - T element = failedToProcess.poll(); - if (element == null) element = things.take(); + ProgressManager.getInstance().executeProcessUnderProgress(() -> { + try { + while (true) { + progress.checkCanceled(); + T element = failedToProcess.poll(); + if (element == null) element = things.take(); - if (element == tombStone) { - things.offer(element); - result = true; + if (element == tombStone) { + things.offer(element); + result = true; + break; + } + try { + if (!thingProcessor.process(element)) { + result = false; break; } - try { - if (!thingProcessor.process(element)) { - result = false; - break; - } - } - catch (RuntimeException e) { - failedToProcess.add(element); - throw e; - } + } + catch (RuntimeException e) { + failedToProcess.add(element); + throw e; } } - catch (InterruptedException e) { - throw new RuntimeException(e); - } + } + catch (InterruptedException e) { + throw new RuntimeException(e); } }, progress); return result; @@ -372,7 +358,7 @@ public class JobLauncherImpl extends JobLauncher { } } - List> tasks = new ArrayList>(); + List> tasks = new ArrayList<>(); for (int i = 0; i < JobSchedulerImpl.CORES_COUNT; i++) { tasks.add(pool.submit(new MyTask(i))); } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java b/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java index 8d2aca5b48ae..32aa407010f9 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java @@ -218,6 +218,59 @@ public class Switcher extends AnAction implements DumbAware { final Alarm myAlarm; final SwitcherSpeedSearch mySpeedSearch; final String myTitle; + + + private class MyFocusTraversalPolicy extends FocusTraversalPolicy { + + @Override + public Component getComponentAfter(Container aContainer, Component aComponent) { + return aComponent == toolWindows ? files : toolWindows; + } + + @Override + public Component getComponentBefore(Container aContainer, Component aComponent) { + return aComponent == toolWindows ? files : toolWindows; + } + + @Override + public Component getFirstComponent(Container aContainer) { + return toolWindows; + } + + @Override + public Component getLastComponent(Container aContainer) { + return files; + } + + @Override + public Component getDefaultComponent(Container aContainer) { + return files; + } + } + + private static void exchangeSelectionState (JBList toClear, JBList toSelect) { + if (toSelect.getModel().getSize() > 0) { + int index = Math.min(toClear.getSelectedIndex(), toSelect.getModel().getSize() - 1); + toSelect.setSelectedIndex(index); + toSelect.ensureIndexIsVisible(index); + toClear.clearSelection(); + } + } + + private class MyToolWindowsListFocusListener extends FocusAdapter { + @Override + public void focusGained(FocusEvent e) { + exchangeSelectionState(files, toolWindows); + } + } + + private class MyFilesListFocusListener extends FocusAdapter { + @Override + public void focusGained(FocusEvent e) { + exchangeSelectionState(toolWindows, files); + } + } + final ClickListener myClickListener = new ClickListener() { @Override public boolean onClick(@NotNull MouseEvent e, int clickCount) { @@ -287,6 +340,7 @@ public class Switcher extends AnAction implements DumbAware { } toolWindows = new JBList(twModel); + toolWindows.addFocusListener(new MyToolWindowsListFocusListener()); if (pinned) { new NameFilteringListModel(toolWindows, new Function() { @NotNull @@ -526,6 +580,7 @@ public class Switcher extends AnAction implements DumbAware { ScrollingUtil.installActions(files); files.addMouseListener(this); files.addMouseMotionListener(this); + files.addFocusListener(new MyFilesListFocusListener()); myClickListener.installOn(files); ScrollingUtil.ensureSelectionExists(files); @@ -569,12 +624,6 @@ public class Switcher extends AnAction implements DumbAware { }).createPopup(); if (isPinnedMode()) { - new AnAction(null, null, null) { - @Override - public void actionPerformed(@NotNull AnActionEvent e) { - changeSelection(); - } - }.registerCustomShortcutSet(CustomShortcutSet.fromString("TAB"), this, myPopup); new AnAction(null, null, null) { @Override public void actionPerformed(@NotNull AnActionEvent e) { @@ -600,6 +649,22 @@ public class Switcher extends AnAction implements DumbAware { } myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, myPopup); myPopup.showInCenterOf(window); + + Container popupFocusAncestor = myPopup.getContent().getFocusCycleRootAncestor(); + + popupFocusAncestor.setFocusTraversalPolicy(new MyFocusTraversalPolicy()); + + addFocusTraversalKeys(popupFocusAncestor, KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, "RIGHT"); + addFocusTraversalKeys(popupFocusAncestor, KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, "LEFT"); + + } + + private static void addFocusTraversalKeys (Container focusCycleRoot, int focusTraversalType, String keyStroke) { + Set focusTraversalKeySet = focusCycleRoot.getFocusTraversalKeys(focusTraversalType); + + Set set = new HashSet(focusTraversalKeySet); + set.add(KeyStroke.getKeyStroke(keyStroke)); + focusCycleRoot.setFocusTraversalKeys(focusTraversalType, set); } @NotNull @@ -653,12 +718,6 @@ public class Switcher extends AnAction implements DumbAware { if (ctrl && isAutoHide() || enter) { navigate(e); } - else if (e.getKeyCode() == VK_LEFT) { - goLeft(); - } - else if (e.getKeyCode() == VK_RIGHT) { - goRight(); - } } KeyEvent lastEvent; @@ -673,18 +732,6 @@ public class Switcher extends AnAction implements DumbAware { closeTabOrToolWindow(); break; } - if (e.getKeyCode() == ALT_KEY) { - changeSelection(); - } - } - - private void changeSelection() { - if (isFilesSelected()) { - goLeft(); - } - else { - goRight(); - } } private void closeTabOrToolWindow() { @@ -721,7 +768,6 @@ public class Switcher extends AnAction implements DumbAware { } }, 300); if (jList.getModel().getSize() == 1) { - goLeft(); removeElementAt(jList, selectedIndex); this.remove(jList); this.remove(separator); @@ -793,38 +839,10 @@ public class Switcher extends AnAction implements DumbAware { return getSelectedList() == toolWindows; } - private void goRight() { - if ((isFilesSelected() || !isFilesVisible()) && isAutoHide()) { - cancel(); - } - else { - if (files.getModel().getSize() > 0) { - final int index = Math.min(toolWindows.getSelectedIndex(), files.getModel().getSize() - 1); - files.setSelectedIndex(index); - files.ensureIndexIsVisible(index); - toolWindows.getSelectionModel().clearSelection(); - IdeFocusManager.findInstanceByComponent(files).requestFocus(files, true); - } - } - } - private void cancel() { myPopup.cancel(); } - private void goLeft() { - if (isToolWindowsSelected() && isAutoHide()) { - cancel(); - } - else { - if (toolWindows.getModel().getSize() > 0) { - toolWindows.setSelectedIndex(Math.min(files.getSelectedIndex(), toolWindows.getModel().getSize() - 1)); - files.getSelectionModel().clearSelection(); - IdeFocusManager.findInstanceByComponent(toolWindows).requestFocus(toolWindows, true); - } - } - } - public void go(boolean forward) { JBList selected = getSelectedList(); JList list = selected; diff --git a/platform/platform-resources/src/META-INF/JsonPlugin.xml b/platform/platform-resources/src/META-INF/JsonPlugin.xml index 1ece0fb40d13..e766c91dc76e 100644 --- a/platform/platform-resources/src/META-INF/JsonPlugin.xml +++ b/platform/platform-resources/src/META-INF/JsonPlugin.xml @@ -70,6 +70,7 @@ + diff --git a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml index 86ec8d4c1e32..60c4c11a28a1 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml @@ -262,6 +262,6 @@ - + diff --git a/platform/platform-resources/src/checkedPlugins.txt b/platform/platform-resources/src/checkedPlugins.txt index 3ea9ed760205..b133a211a5c6 100644 --- a/platform/platform-resources/src/checkedPlugins.txt +++ b/platform/platform-resources/src/checkedPlugins.txt @@ -27,4 +27,15 @@ SourceSafe TFS ro.redeul.google.go com.intellij.plugins.html.instantEditing -com.jetbrains.intellij.api.watcher \ No newline at end of file +com.jetbrains.intellij.api.watcher +com.jetbrains.php.framework +com.intellij.phing +com.intellij.apacheConfig +com.jetbrains.plugins.ini4idea +com.jetbrains.php.gae +com.jetbrains.php.drupal +com.jetbrains.php.wordPress +org.jetbrains.plugins.phpstorm-remote-interpreter +com.jetbrains.php.behat +com.jetbrains.php.blade +com.jetbrains.performancePlugin \ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/progress/impl/ProgressIndicatorTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/progress/impl/ProgressIndicatorTest.java index 035ca5d93eb8..2466ab02a621 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/progress/impl/ProgressIndicatorTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/progress/impl/ProgressIndicatorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 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. @@ -15,6 +15,8 @@ */ package com.intellij.openapi.progress.impl; +import com.intellij.codeInsight.daemon.impl.DaemonProgressIndicator; +import com.intellij.concurrency.SensitiveProgressWrapper; import com.intellij.ide.util.DelegatingProgressIndicator; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; @@ -377,6 +379,19 @@ public class ProgressIndicatorTest extends LightPlatformTestCase { } } + public void testSOEUnderExtremelyNestedWrappedIndicator() { + ProgressIndicator indicator = new DaemonProgressIndicator(); + for (int i=0;i<10000;i++) { + indicator = new SensitiveProgressWrapper(indicator); + } + ProgressManager.getInstance().executeProcessUnderProgress(() -> { + ProgressIndicator progressIndicator = ProgressIndicatorProvider.getGlobalProgressIndicator(); + assertTrue(progressIndicator instanceof SensitiveProgressWrapper); + progressIndicator.checkCanceled(); + progressIndicator.isCanceled(); + }, indicator); + } + public void testBombedIndicator() { final int count = 10; new BombedProgressIndicator(count).runBombed(() -> { diff --git a/platform/script-debugger/backend/src/WorkerManager.kt b/platform/script-debugger/backend/src/WorkerManager.kt index 90459bc2e2cf..882dbd963641 100644 --- a/platform/script-debugger/backend/src/WorkerManager.kt +++ b/platform/script-debugger/backend/src/WorkerManager.kt @@ -19,5 +19,5 @@ package org.jetbrains.debugger * @author Konstantin.Ulitin */ interface WorkerManager { - val activeWorkers: Set + val activeWorkers: Map } \ No newline at end of file diff --git a/platform/script-debugger/backend/src/WorkerManagerBase.kt b/platform/script-debugger/backend/src/WorkerManagerBase.kt index bd14aa0143d9..d6cd091707ba 100644 --- a/platform/script-debugger/backend/src/WorkerManagerBase.kt +++ b/platform/script-debugger/backend/src/WorkerManagerBase.kt @@ -21,5 +21,5 @@ import java.util.* * @author Konstantin.Ulitin */ abstract class WorkerManagerBase : WorkerManager { - override val activeWorkers: Set = Collections.emptySet() + override val activeWorkers: Map = Collections.emptyMap() } \ No newline at end of file diff --git a/platform/script-debugger/backend/src/org/jetbrains/debugger/WebWorker.kt b/platform/script-debugger/backend/src/org/jetbrains/debugger/WebWorker.kt new file mode 100644 index 000000000000..73290b00f9d8 --- /dev/null +++ b/platform/script-debugger/backend/src/org/jetbrains/debugger/WebWorker.kt @@ -0,0 +1,25 @@ +/* + * 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://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 org.jetbrains.debugger + + +/** + * @author Konstantin.Ulitin + */ +interface WebWorker { + val id: String + val url: String +} \ No newline at end of file diff --git a/platform/usageView/src/com/intellij/usages/impl/UsagePreviewPanel.java b/platform/usageView/src/com/intellij/usages/impl/UsagePreviewPanel.java index 5fee98914ee5..64f968b9d60e 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsagePreviewPanel.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsagePreviewPanel.java @@ -18,6 +18,7 @@ package com.intellij.usages.impl; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.colors.EditorColors; @@ -108,22 +109,30 @@ public class UsagePreviewPanel extends UsageContextPanelBase { revalidate(); } - final Editor editor = myEditor; - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - if (myProject.isDisposed()) return; - highlight(infos, editor); - } - }); + highlight(infos, myEditor, myProject); } private static final Key IN_PREVIEW_USAGE_FLAG = Key.create("IN_PREVIEW_USAGE_FLAG"); - private void highlight(@NotNull List infos, @NotNull Editor editor) { - if (editor != myEditor) return; //already disposed - PsiDocumentManager.getInstance(myProject).commitAllDocuments(); - MarkupModel markupModel = myEditor.getMarkupModel(); + + public static void highlight(@NotNull final List infos, @NotNull final Editor editor, @NotNull final Project project) { + @SuppressWarnings("UnnecessaryLocalVariable") final Editor editor1 = editor; + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + if (project.isDisposed()) return; + //noinspection ConstantConditions + if (editor1 == editor && !editor.isDisposed()) { + doHighlight(infos, editor, project); + } + } + }, ModalityState.NON_MODAL); + } + + private static void doHighlight(@NotNull List infos, @NotNull Editor editor, @NotNull Project project) { + PsiDocumentManager.getInstance(project).commitAllDocuments(); + + MarkupModel markupModel = editor.getMarkupModel(); for (RangeHighlighter highlighter : markupModel.getAllHighlighters()) { if (highlighter.getUserData(IN_PREVIEW_USAGE_FLAG) != null) { highlighter.dispose(); @@ -154,15 +163,15 @@ public class UsagePreviewPanel extends UsageContextPanelBase { } } // highlight injected element in host document textrange - textRange = InjectedLanguageManager.getInstance(myProject).injectedToHost(psiElement, textRange); + textRange = InjectedLanguageManager.getInstance(project).injectedToHost(psiElement, textRange); RangeHighlighter highlighter = markupModel.addRangeHighlighter(textRange.getStartOffset(), textRange.getEndOffset(), HighlighterLayer.ADDITIONAL_SYNTAX, attributes, HighlighterTargetArea.EXACT_RANGE); highlighter.putUserData(IN_PREVIEW_USAGE_FLAG, Boolean.TRUE); - myEditor.getCaretModel().moveToOffset(textRange.getEndOffset()); + editor.getCaretModel().moveToOffset(textRange.getEndOffset()); } - myEditor.getScrollingModel().scrollToCaret(ScrollType.CENTER); + editor.getScrollingModel().scrollToCaret(ScrollType.CENTER); } private static final Key PREVIEW_EDITOR_FLAG = Key.create("PREVIEW_EDITOR_FLAG"); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogHashMapImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogHashMapImpl.java index bb3f4dcbcd36..1a5b50f26bdc 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogHashMapImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogHashMapImpl.java @@ -24,7 +24,6 @@ import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.ThrowableComputable; -import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.CommonProcessors; @@ -34,7 +33,10 @@ import com.intellij.util.io.IOUtil; import com.intellij.util.io.KeyDescriptor; import com.intellij.util.io.Page; import com.intellij.util.io.PersistentEnumerator; -import com.intellij.vcs.log.*; +import com.intellij.vcs.log.CommitId; +import com.intellij.vcs.log.Hash; +import com.intellij.vcs.log.VcsLogHashMap; +import com.intellij.vcs.log.VcsLogProvider; import com.intellij.vcs.log.impl.HashImpl; import com.intellij.vcs.log.impl.VcsRootsRegistry; import org.jetbrains.annotations.NotNull; @@ -80,20 +82,19 @@ public class VcsLogHashMapImpl implements Disposable, VcsLogHashMap { private final PersistentEnumerator myPersistentEnumerator; public VcsLogHashMapImpl(@NotNull final Project project, @NotNull Map logProviders) throws IOException { - cleanupOldNaming(project, logProviders); String logId = calcLogId(project, logProviders); final File mapFile = new File(LOG_CACHE_APP_DIR, logId + "." + VERSION); if (!mapFile.exists()) { IOUtil.deleteAllFilesStartingWith(new File(LOG_CACHE_APP_DIR, logId)); } - Disposer.register(project, this); myPersistentEnumerator = IOUtil.openCleanOrResetBroken(new ThrowableComputable, IOException>() { @Override public PersistentEnumerator compute() throws IOException { return new PersistentEnumerator(mapFile, new MyCommitIdKeyDescriptor(project), Page.PAGE_SIZE); } }, mapFile); + Disposer.register(project, this); } @NotNull @@ -102,13 +103,6 @@ public class VcsLogHashMapImpl implements Disposable, VcsLogHashMap { return project.getLocationHash() + "." + Integer.toHexString(hashcode); } - // TODO remove in IDEA 15 - private static void cleanupOldNaming(@NotNull Project project, @NotNull Map providers) { - int hashcode = calcLogProvidersHash(providers); - String oldLogId = project.getName() + "." + hashcode; - FileUtil.delete(new File(LOG_CACHE, oldLogId)); - } - private static int calcLogProvidersHash(@NotNull final Map logProviders) { List sortedRoots = ContainerUtil.sorted(logProviders.keySet(), new Comparator() { @Override @@ -196,7 +190,7 @@ public class VcsLogHashMapImpl implements Disposable, VcsLogHashMap { } private static class MyCommitIdKeyDescriptor implements KeyDescriptor { - private final VcsRootsRegistry myRootsRegistry; + @NotNull private final VcsRootsRegistry myRootsRegistry; public MyCommitIdKeyDescriptor(@NotNull Project project) { myRootsRegistry = ServiceManager.getService(project, VcsRootsRegistry.class); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/BranchesPanel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/BranchesPanel.java index de0f93728497..0c62639d7d86 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/BranchesPanel.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/BranchesPanel.java @@ -35,20 +35,20 @@ public class BranchesPanel extends JPanel { private static final int TOP = 2; private static final int BOTTOM = 3; @NotNull private final VcsLogDataHolder myDataHolder; - @NotNull private final VcsLogUiImpl myUI; + @NotNull private final VcsLogUiImpl myUi; @NotNull private final VcsRefPainter myReferencePainter; @NotNull private LinkedHashMap> myRefGroups; @Nullable private Collection myRoots = null; - public BranchesPanel(@NotNull VcsLogDataHolder dataHolder, @NotNull VcsLogUiImpl UI, @NotNull RefsModel initialRefsModel) { + public BranchesPanel(@NotNull VcsLogDataHolder dataHolder, @NotNull VcsLogUiImpl ui, @NotNull RefsModel initialRefsModel) { super(new FlowLayout(FlowLayout.LEADING, BIG_ROOTS_GAP - 2 * SMALL_ROOTS_GAP, 0)); setBorder(new EmptyBorder(TOP, SMALL_ROOTS_GAP, BOTTOM, SMALL_ROOTS_GAP)); myDataHolder = dataHolder; - myUI = UI; + myUi = ui; myRefGroups = getRefsToDisplayOnPanel(initialRefsModel); - myReferencePainter = new VcsRefPainter(myUI.getColorManager(), true); + myReferencePainter = new VcsRefPainter(myUi.getColorManager(), true); recreateComponents(); } @@ -129,7 +129,7 @@ public class BranchesPanel extends JPanel { myGroups = groups; for (RefGroup group : myGroups) { - add(new ReferenceGroupComponent(group, myReferencePainter, myUI, root)); + add(new ReferenceGroupComponent(group, myReferencePainter, myUi, root)); } } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/VcsLogGraphTable.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/VcsLogGraphTable.java index ff1335f7b534..4d3f9c58d8b7 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/VcsLogGraphTable.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/VcsLogGraphTable.java @@ -83,7 +83,7 @@ public class VcsLogGraphTable extends JBTable implements DataProvider, CopyProvi private static final int MAX_ROWS_TO_CALC_WIDTH = 1000; private static final int MAX_ROWS_TO_CALC_OFFSET = 100; - @NotNull private final VcsLogUiImpl myUI; + @NotNull private final VcsLogUiImpl myUi; private final VcsLogDataHolder myLogDataHolder; private final MyDummyTableCellEditor myDummyEditor = new MyDummyTableCellEditor(); @NotNull private final TableCellRenderer myDummyRenderer = new DefaultTableCellRenderer(); @@ -104,13 +104,13 @@ public class VcsLogGraphTable extends JBTable implements DataProvider, CopyProvi } }; - public VcsLogGraphTable(@NotNull VcsLogUiImpl UI, @NotNull final VcsLogDataHolder logDataHolder, @NotNull VisiblePack initialDataPack) { + public VcsLogGraphTable(@NotNull VcsLogUiImpl ui, @NotNull final VcsLogDataHolder logDataHolder, @NotNull VisiblePack initialDataPack) { super(); - myUI = UI; + myUi = ui; myLogDataHolder = logDataHolder; myGraphCommitCellRenderer = new GraphCommitCellRender(logDataHolder, myGraphCellPainter, this); - setDefaultRenderer(VirtualFile.class, new RootCellRenderer(myUI)); + setDefaultRenderer(VirtualFile.class, new RootCellRenderer(myUi)); setDefaultRenderer(GraphCommitCell.class, myGraphCommitCellRenderer); setDefaultRenderer(String.class, new StringCellRenderer()); @@ -125,7 +125,7 @@ public class VcsLogGraphTable extends JBTable implements DataProvider, CopyProvi PopupHandler.installPopupHandler(this, VcsLogActionPlaces.POPUP_ACTION_GROUP, VcsLogActionPlaces.VCS_LOG_TABLE_PLACE); ScrollingUtil.installActions(this, false); - GraphTableModel model = new GraphTableModel(initialDataPack, myLogDataHolder, myUI); + GraphTableModel model = new GraphTableModel(initialDataPack, myLogDataHolder, myUi); setModel(model); initColumnSize(); } @@ -193,10 +193,10 @@ public class VcsLogGraphTable extends JBTable implements DataProvider, CopyProvi private void setRootColumnSize(TableColumn column) { int rootWidth; - if (!myUI.isMultipleRoots()) { + if (!myUi.isMultipleRoots()) { rootWidth = 0; } - else if (!myUI.isShowRootNames()) { + else if (!myUi.isShowRootNames()) { rootWidth = ROOT_INDICATOR_WIDTH; } else { @@ -231,7 +231,7 @@ public class VcsLogGraphTable extends JBTable implements DataProvider, CopyProvi return "" + ((VirtualFile)at).getPresentableUrl() + "
Click to " + - (myUI.isShowRootNames() ? "collapse" : "expand") + + (myUi.isShowRootNames() ? "collapse" : "expand") + ""; } } @@ -363,7 +363,7 @@ public class VcsLogGraphTable extends JBTable implements DataProvider, CopyProvi private boolean expandOrCollapseRoots(@NotNull MouseEvent e) { TableColumn column = getRootColumnOrNull(e); if (column != null) { - myUI.setShowRootNames(!myUI.isShowRootNames()); + myUi.setShowRootNames(!myUi.isShowRootNames()); return true; } return false; @@ -400,7 +400,7 @@ public class VcsLogGraphTable extends JBTable implements DataProvider, CopyProvi } } - myUI.repaintUI(); // in case of repaintUI doing something more than just repainting this table in some distant future + myUi.repaintUI(); // in case of repaintUI doing something more than just repainting this table in some distant future if (answer == null) { return; diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java index f0177df4a6d0..8631ba5e0515 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java @@ -45,9 +45,9 @@ public class GraphTableModel extends AbstractTableModel { private boolean myMoreRequested; - public GraphTableModel(@NotNull VisiblePack dataPack, @NotNull VcsLogDataHolder dataHolder, @NotNull VcsLogUiImpl UI) { + public GraphTableModel(@NotNull VisiblePack dataPack, @NotNull VcsLogDataHolder dataHolder, @NotNull VcsLogUiImpl ui) { myLogDataHolder = dataHolder; - myUi = UI; + myUi = ui; myDataPack = dataPack; myDataHolder = dataHolder; } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/ChangeModifierFix.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/ChangeModifierFix.java index 0e114abf03f4..d6a494b60fc8 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/ChangeModifierFix.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/fixes/ChangeModifierFix.java @@ -23,6 +23,7 @@ import com.intellij.psi.PsiModifierList; import com.intellij.psi.PsiModifierListOwner; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.VisibilityUtil; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.InspectionGadgetsFix; import org.jetbrains.annotations.NonNls; @@ -40,8 +41,7 @@ public class ChangeModifierFix extends InspectionGadgetsFix { @Override @NotNull public String getName() { - return InspectionGadgetsBundle.message("change.modifier.quickfix", - modifierText); + return InspectionGadgetsBundle.message("change.modifier.quickfix", VisibilityUtil.toPresentableText(modifierText)); } @NotNull diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/MethodCallInLoopConditionInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/MethodCallInLoopConditionInspectionBase.java index cfd662295820..6a0062f0cd6f 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/MethodCallInLoopConditionInspectionBase.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/MethodCallInLoopConditionInspectionBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 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. @@ -15,13 +15,21 @@ */ package com.siyeh.ig.j2me; +import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel; import com.intellij.psi.*; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.MethodCallUtils; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; public class MethodCallInLoopConditionInspectionBase extends BaseInspection { + + public boolean ignoreIterationMethods = true; + @Override @NotNull public String getDisplayName() { @@ -39,12 +47,18 @@ public class MethodCallInLoopConditionInspectionBase extends BaseInspection { return true; } + @Nullable + @Override + public JComponent createOptionsPanel() { + return new SingleCheckboxOptionsPanel("Ignore iteration method calls", this, "ignoreIterationMethods"); + } + @Override public BaseInspectionVisitor buildVisitor() { return new MethodCallInLoopConditionVisitor(); } - private static class MethodCallInLoopConditionVisitor extends BaseInspectionVisitor { + private class MethodCallInLoopConditionVisitor extends BaseInspectionVisitor { @Override public void visitForStatement(@NotNull PsiForStatement statement) { @@ -80,9 +94,17 @@ public class MethodCallInLoopConditionInspectionBase extends BaseInspection { final PsiElementVisitor visitor = new JavaRecursiveElementWalkingVisitor() { @Override - public void visitMethodCallExpression( - @NotNull PsiMethodCallExpression expression) { + public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) { super.visitMethodCallExpression(expression); + if (ignoreIterationMethods) { + final PsiMethod method = expression.resolveMethod(); + if (MethodCallUtils.isCallToMethod(expression, CommonClassNames.JAVA_UTIL_ITERATOR, PsiType.BOOLEAN, "hasNext") || + MethodCallUtils.isCallToMethod(expression, "java.util.ListIterator", PsiType.BOOLEAN, "hasPrevious") || + MethodCallUtils.isCallToMethod(expression, "java.sql.ResultSet", PsiType.BOOLEAN, "next") || + MethodCallUtils.isCallToMethod(expression, "java.util.Enumeration", PsiType.BOOLEAN, "hasMoreElements")) { + return; + } + } registerMethodCallError(expression); } }; diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/j2me/MethodCallInLoopConditionInspection.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/j2me/MethodCallInLoopConditionInspection.java deleted file mode 100644 index 46ddf66f9933..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/j2me/MethodCallInLoopConditionInspection.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.siyeh.igtest.j2me; - -public class MethodCallInLoopConditionInspection { - public void foo() { - for (int i = 0; i < bar(); i++) { - - } - while (bar() != 4) { - foo(); - } - - do { - foo(); - } - while (bar() != 4); - - } - - private int bar() { - return 3; - } -} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/j2me/method_call_in_loop_condition/MethodCallInLoopCondition.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/j2me/method_call_in_loop_condition/MethodCallInLoopCondition.java new file mode 100644 index 000000000000..c8a2cba02a09 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/j2me/method_call_in_loop_condition/MethodCallInLoopCondition.java @@ -0,0 +1,52 @@ +package com.siyeh.igtest.j2me; + +import java.util.Iterator; +import java.util.ListIterator; +import java.util.Enumeration; +import java.sql.ResultSet; +import java.sql.SQLException; + +public class MethodCallInLoopCondition { + public void foo() { + for (int i = 0; i < bar(); i++) { + + } + while (bar() != 4) { + foo(); + } + + do { + foo(); + } + while (bar() != 4); + + } + + private int bar() { + return 3; + } + + void a(Iterator it) { + while (it.hasNext()) { + String s = it.next(); + } + } + + void b(ListIterator it) { + while (it.hasPrevious()) { + String s = it.previous(); + } + } + + void c(Enumeration e) { + while (e.hasMoreElements()) { + e.nextElement(); + } + } + + void d (ResultSet rs) throws SQLException { + while (rs.next()) { + rs.getInt(1); + } + } +} diff --git a/plugins/InspectionGadgets/testsrc/com/intellij/codeInspection/visibility/AccessCanBeTightenedInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/intellij/codeInspection/visibility/AccessCanBeTightenedInspectionTest.java index 38e229346c87..5b6e8c0f0670 100644 --- a/plugins/InspectionGadgets/testsrc/com/intellij/codeInspection/visibility/AccessCanBeTightenedInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/intellij/codeInspection/visibility/AccessCanBeTightenedInspectionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 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. @@ -27,7 +27,7 @@ public class AccessCanBeTightenedInspectionTest extends LightInspectionTestCase "class C {\n" + " final int /*Access can be private*/fd/**/ = 0;\n" + " /*Access can be private*/public/**/ int fd2;\n" + - " /*Access can be packageLocal*/public/**/ int forSubClass;\n" + + " /*Access can be package local*/public/**/ int forSubClass;\n" + " @Override\n" + " public int hashCode() {\n" + " return fd + fd2;\n" + // use field @@ -58,7 +58,7 @@ public class AccessCanBeTightenedInspectionTest extends LightInspectionTestCase "@interface Ann{ String value(); }\n" + "@Ann(value = C.VAL\n)" + "class C {\n" + - " /*Access can be packageLocal*/public/**/ static final String VAL = \"xx\";\n" + + " /*Access can be package local*/public/**/ static final String VAL = \"xx\";\n" + "}"); } diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/j2me/MethodCallInLoopConditionInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/j2me/MethodCallInLoopConditionInspectionTest.java new file mode 100644 index 000000000000..3fd3b83d36af --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/j2me/MethodCallInLoopConditionInspectionTest.java @@ -0,0 +1,36 @@ +/* + * 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://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.siyeh.ig.j2me; + +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; + +/** + * @author Bas Leijdekkers + */ +public class MethodCallInLoopConditionInspectionTest extends LightInspectionTestCase { + + public void testMethodCallInLoopCondition() { + doTest(); + } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new MethodCallInLoopConditionInspection(); + } +} \ No newline at end of file diff --git a/plugins/testng_rt/src/org/testng/IDEARemoteTestNG.java b/plugins/testng_rt/src/org/testng/IDEARemoteTestNG.java index 3d43424974f0..85e8c97bfaf6 100644 --- a/plugins/testng_rt/src/org/testng/IDEARemoteTestNG.java +++ b/plugins/testng_rt/src/org/testng/IDEARemoteTestNG.java @@ -22,6 +22,7 @@ import org.testng.xml.XmlInclude; import org.testng.xml.XmlSuite; import org.testng.xml.XmlTest; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -53,10 +54,11 @@ public class IDEARemoteTestNG extends TestNG { try { if (myParam != null) { for (XmlClass aClass : test.getXmlClasses()) { + List includes = new ArrayList(); for (XmlInclude include : aClass.getIncludedMethods()) { - final XmlInclude xmlInclude = new XmlInclude(include.getName(), Collections.singletonList(Integer.parseInt(myParam)), 0); - aClass.setIncludedMethods(Collections.singletonList(xmlInclude)); + includes.add(new XmlInclude(include.getName(), Collections.singletonList(Integer.parseInt(myParam)), 0)); } + aClass.setIncludedMethods(includes); } } } diff --git a/python/educational-core/src/com/jetbrains/edu/courseFormat/AnswerPlaceholder.java b/python/educational-core/src/com/jetbrains/edu/courseFormat/AnswerPlaceholder.java index ae3230b5e980..979013f97076 100644 --- a/python/educational-core/src/com/jetbrains/edu/courseFormat/AnswerPlaceholder.java +++ b/python/educational-core/src/com/jetbrains/edu/courseFormat/AnswerPlaceholder.java @@ -23,13 +23,16 @@ public class AnswerPlaceholder { private int myIndex = -1; private String myTaskText; private MyInitialState myInitialState; + private StudyStatus myStatus = StudyStatus.Uninitialized; @Transient private TaskFile myTaskFile; public void initAnswerPlaceholder(final TaskFile file, boolean isRestarted) { if (!isRestarted) { setInitialState(new MyInitialState(getLine(), getLength(), getStart())); + myStatus = file.getTask().getStatus(); } + setTaskFile(file); } @@ -136,6 +139,14 @@ public class AnswerPlaceholder { length = myInitialState.myLength; } + public StudyStatus getStatus() { + return myStatus; + } + + public void setStatus(StudyStatus status) { + myStatus = status; + } + public static class MyInitialState { public int myLine = -1; public int myLength = -1; diff --git a/python/educational-core/src/com/jetbrains/edu/courseFormat/StudyStatus.java b/python/educational-core/src/com/jetbrains/edu/courseFormat/StudyStatus.java index 767a3c9fdeb7..e8c11503f565 100644 --- a/python/educational-core/src/com/jetbrains/edu/courseFormat/StudyStatus.java +++ b/python/educational-core/src/com/jetbrains/edu/courseFormat/StudyStatus.java @@ -1,5 +1,5 @@ package com.jetbrains.edu.courseFormat; public enum StudyStatus { - Unchecked, Solved, Failed + Unchecked, Solved, Failed, Uninitialized } diff --git a/python/educational-core/src/com/jetbrains/edu/courseFormat/Task.java b/python/educational-core/src/com/jetbrains/edu/courseFormat/Task.java index e98905dcb4bb..b20ea6374ae1 100644 --- a/python/educational-core/src/com/jetbrains/edu/courseFormat/Task.java +++ b/python/educational-core/src/com/jetbrains/edu/courseFormat/Task.java @@ -24,6 +24,7 @@ public class Task implements StudyItem { // index is visible to user number of task from 1 to task number private int myIndex; + private StudyStatus myStatus = StudyStatus.Uninitialized; private int myStepicId; @@ -49,6 +50,7 @@ public class Task implements StudyItem { */ public void initTask(final Lesson lesson, boolean isRestarted) { setLesson(lesson); + if (!isRestarted) myStatus = StudyStatus.Unchecked; for (TaskFile taskFile : getTaskFiles().values()) { taskFile.initTaskFile(this, isRestarted); } @@ -204,4 +206,12 @@ public class Task implements StudyItem { public int getStepicId() { return myStepicId; } + + public StudyStatus getStatus() { + return myStatus; + } + + public void setStatus(StudyStatus status) { + myStatus = status; + } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java index de23e2ec4288..afd86b186c46 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java @@ -34,10 +34,10 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb private Course myCourse; private OldCourse myOldCourse; public int VERSION = 2; - public Map myStudyStatusMap = new HashMap(); - public Map myTaskStatusMap = new HashMap(); - public Map> myUserTests = new HashMap>(); - public List myInvisibleFiles = new ArrayList(); + public Map myStudyStatusMap = new HashMap<>(); + public Map myTaskStatusMap = new HashMap<>(); + public Map> myUserTests = new HashMap<>(); + public List myInvisibleFiles = new ArrayList<>(); private StudyTaskManager() { } @@ -52,16 +52,13 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb } public void setStatus(AnswerPlaceholder placeholder, StudyStatus status) { - if (myStudyStatusMap == null) { - myStudyStatusMap = new HashMap(); - } - myStudyStatusMap.put(placeholder, status); + placeholder.setStatus(status); } public void addUserTest(@NotNull final Task task, UserTest userTest) { List userTests = myUserTests.get(task); if (userTests == null) { - userTests = new ArrayList(); + userTests = new ArrayList<>(); myUserTests.put(task, userTests); } userTests.add(userTest); @@ -86,29 +83,27 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb public void setStatus(Task task, StudyStatus status) { + task.setStatus(status); for (TaskFile taskFile : task.getTaskFiles().values()) { setStatus(taskFile, status); } } public void setStatus(TaskFile file, StudyStatus status) { - if (file.getAnswerPlaceholders().isEmpty()) { - if (myTaskStatusMap == null) { - myTaskStatusMap = new HashMap(); - } - myTaskStatusMap.put(file, status); - } for (AnswerPlaceholder answerPlaceholder : file.getAnswerPlaceholders()) { setStatus(answerPlaceholder, status); } } public StudyStatus getStatus(AnswerPlaceholder placeholder) { - StudyStatus status = myStudyStatusMap.get(placeholder); + StudyStatus status = placeholder.getStatus(); + if (status != StudyStatus.Uninitialized) return status; + + status = myStudyStatusMap.get(placeholder); if (status == null) { status = StudyStatus.Unchecked; - myStudyStatusMap.put(placeholder, status); } + placeholder.setStatus(status); return status; } @@ -124,15 +119,21 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb } public StudyStatus getStatus(@NotNull final Task task) { + StudyStatus taskStatus = task.getStatus(); + if (taskStatus != StudyStatus.Uninitialized) return taskStatus; + for (TaskFile taskFile : task.getTaskFiles().values()) { StudyStatus taskFileStatus = getStatus(taskFile); if (taskFileStatus == StudyStatus.Unchecked) { + task.setStatus(StudyStatus.Unchecked); return StudyStatus.Unchecked; } if (taskFileStatus == StudyStatus.Failed) { + task.setStatus(StudyStatus.Failed); return StudyStatus.Failed; } } + task.setStatus(StudyStatus.Solved); return StudyStatus.Solved; } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyBrowserWindow.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyBrowserWindow.java index ced05bf7ed30..a4bcce6f0e5a 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyBrowserWindow.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyBrowserWindow.java @@ -217,16 +217,10 @@ class StudyBrowserWindow extends JFrame { myEngine.setJavaScriptEnabled(true); myEngine.getLoadWorker().cancel(); ev.preventDefault(); - - ApplicationManager.getApplication().invokeLater(() -> { - final String href = getLink((Element)ev.getTarget()); - if (href == null) return; - final StudyBrowserWindow studyBrowserWindow = new StudyBrowserWindow(false, true); - studyBrowserWindow.addBackAndOpenButtons(); - studyBrowserWindow.load(href); - studyBrowserWindow.setVisible(true); - }); - + final String href = getLink((Element)ev.getTarget()); + if (href == null) return; + BrowserUtil.browse(href); + } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudySwingToolWindow.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudySwingToolWindow.java index 294eeff042be..56ca088e282a 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudySwingToolWindow.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudySwingToolWindow.java @@ -16,6 +16,7 @@ package com.jetbrains.edu.learning.ui; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.EditorColorsScheme; +import com.intellij.ui.BrowserHyperlinkListener; import com.intellij.ui.ColorUtil; import com.intellij.util.ui.UIUtil; @@ -53,6 +54,7 @@ public class StudySwingToolWindow extends StudyToolWindow { } myTaskTextPane.setBorder(new EmptyBorder(15, 20, 0, 100)); myTaskTextPane.setText(taskText); + myTaskTextPane.addHyperlinkListener(BrowserHyperlinkListener.INSTANCE); return myTaskTextPane; } diff --git a/python/helpers/python-skeletons/django/forms/__init__.py b/python/helpers/python-skeletons/django/forms/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/helpers/python-skeletons/django/forms/forms.py b/python/helpers/python-skeletons/django/forms/forms.py new file mode 100644 index 000000000000..9f4b7f794348 --- /dev/null +++ b/python/helpers/python-skeletons/django/forms/forms.py @@ -0,0 +1,22 @@ +from django.forms.forms import BoundField +class BaseForm(object): + """ + This is the main implementation of all the Form logic. Note that this + class is different than Form. See the comments by the Form class for more + information. Any improvements to the form API should be made to *this* + class, not to the Form class. + + """ + + def __iter__(self): + """ + + :rtype: collections.Iterator[BoundField] + """ + pass + + def __getitem__(self, index): + """ + :rtype: BoundField + """ + pass \ No newline at end of file diff --git a/python/helpers/python-skeletons/django/forms/formsets.py b/python/helpers/python-skeletons/django/forms/formsets.py new file mode 100644 index 000000000000..2f229065ce19 --- /dev/null +++ b/python/helpers/python-skeletons/django/forms/formsets.py @@ -0,0 +1,25 @@ +from django.forms import Form +class BaseFormSet(object): + """ + A collection of instances of the same Form class. + + """ + + def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, + initial=None, error_class=ErrorList, form_kwargs=None): + """ + :rtype: BaseFormSet[T <= Form] + """ + + def __iter__(self): + """ + + :rtype: collections.Iterator[T] + """ + pass + + def __getitem__(self, index): + """ + :rtype: T + """ + pass diff --git a/python/psi-api/src/com/jetbrains/python/codeInsight/PyCustomMember.java b/python/psi-api/src/com/jetbrains/python/codeInsight/PyCustomMember.java index 543d635050d5..488d6038b997 100644 --- a/python/psi-api/src/com/jetbrains/python/codeInsight/PyCustomMember.java +++ b/python/psi-api/src/com/jetbrains/python/codeInsight/PyCustomMember.java @@ -15,6 +15,7 @@ */ package com.jetbrains.python.codeInsight; +import com.google.common.base.Preconditions; import com.intellij.extapi.psi.ASTWrapperPsiElement; import com.intellij.icons.AllIcons; import com.intellij.openapi.util.Key; @@ -27,6 +28,7 @@ import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyFunction; import com.jetbrains.python.psi.PyPsiFacade; import com.jetbrains.python.psi.PyTypedElement; +import com.jetbrains.python.psi.types.PyClassType; import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NotNull; @@ -35,6 +37,9 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; /** + * Note: if you use {@link #myTypeName} to override real field, be sure to use + * {@link com.jetbrains.python.psi.types.PyOverridingClassMembersProvider} + * * @author Dennis.Ushakov */ public class PyCustomMember extends UserDataHolderBase { @@ -55,6 +60,8 @@ public class PyCustomMember extends UserDataHolderBase { * Force resolving to {@link MyInstanceElement} even if element is function */ private boolean myAlwaysResolveToCustomElement; + private Icon myIcon = AllIcons.Nodes.Method; + private PyCustomMemberTypeInfo myCustomTypeInfo; public PyCustomMember(@NotNull final String name, @Nullable final String type, final boolean resolveToInstance) { myName = name; @@ -166,7 +173,7 @@ public class PyCustomMember extends UserDataHolderBase { if (myTarget != null) { return myTarget.getIcon(0); } - return AllIcons.Nodes.Method; + return myIcon; } @Nullable @@ -241,6 +248,26 @@ public class PyCustomMember extends UserDataHolderBase { return ((MyInstanceElement)element).getThis().equals(this); } + /** + * @param icon icon to use (will be used method icon otherwise) + */ + public PyCustomMember withIcon(@NotNull final Icon icon) { + myIcon = icon; + return this; + } + + /** + * Adds custom info to type if class has {@link #myTypeName} set. + * Info could be later obtained by key. + * + * @param customInfo custom info to add + */ + public PyCustomMember withCustomTypeInfo(@NotNull final PyCustomMemberTypeInfo customInfo) { + Preconditions.checkState(myTypeName != null, "Cant add custom type info if no type provided"); + myCustomTypeInfo = customInfo; + return this; + } + private class MyInstanceElement extends ASTWrapperPsiElement implements PyTypedElement { private final PyClass myClass; private final PsiElement myContext; @@ -260,7 +287,11 @@ public class PyCustomMember extends UserDataHolderBase { return myTypeCallback.fun(myContext); } else if (myClass != null) { - return PyPsiFacade.getInstance(getProject()).createClassType(myClass, !myResolveToInstance); + final PyClassType type = PyPsiFacade.getInstance(getProject()).createClassType(myClass, !myResolveToInstance); + if (myCustomTypeInfo != null) { + myCustomTypeInfo.fill(type); + } + return type; } return null; } diff --git a/python/psi-api/src/com/jetbrains/python/codeInsight/PyCustomMemberTypeInfo.java b/python/psi-api/src/com/jetbrains/python/codeInsight/PyCustomMemberTypeInfo.java new file mode 100644 index 000000000000..5fda8d4d2393 --- /dev/null +++ b/python/psi-api/src/com/jetbrains/python/codeInsight/PyCustomMemberTypeInfo.java @@ -0,0 +1,55 @@ +/* + * 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://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.jetbrains.python.codeInsight; + +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.UserDataHolder; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Info to add to type of custom member. + * + * @author Ilya.Kazakevich + */ +public class PyCustomMemberTypeInfo { + @NotNull + private final Map, K> myCustomInfo = new HashMap, K>(); + + public PyCustomMemberTypeInfo(@NotNull final Key key, @NotNull final K value) { + this(Collections.singleton(Pair.create(key, value))); + } + + public PyCustomMemberTypeInfo(@NotNull final Iterable, K>> customInfo) { + for (final Pair, K> pair : customInfo) { + myCustomInfo.put(pair.first, pair.second); + } + } + + public PyCustomMemberTypeInfo(@NotNull final Map, K> customInfo) { + myCustomInfo.putAll(customInfo); + } + + void fill(@NotNull final UserDataHolder typeToFill) { + for (final Map.Entry, K> entry : myCustomInfo.entrySet()) { + typeToFill.putUserData(entry.getKey(), entry.getValue()); + } + } +} diff --git a/python/psi-api/src/com/jetbrains/python/nameResolver/NameResolverTools.java b/python/psi-api/src/com/jetbrains/python/nameResolver/NameResolverTools.java index 4c1bd04080ab..ea1e3a479662 100644 --- a/python/psi-api/src/com/jetbrains/python/nameResolver/NameResolverTools.java +++ b/python/psi-api/src/com/jetbrains/python/nameResolver/NameResolverTools.java @@ -27,6 +27,7 @@ import com.intellij.psi.util.QualifiedName; import com.intellij.util.Function; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.resolve.PyResolveContext; +import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -108,8 +109,8 @@ public final class NameResolverTools { * Same as {@link #isName(PyElement, FQNamesProvider...)} for call expr, but first checks name. * Aliases not supported, but much lighter that way * - * @param call expr - * @param function names to check + * @param call expr + * @param function names to check * @return true if callee is correct */ public static boolean isCalleeShortCut(@NotNull final PyCallExpression call, @@ -142,7 +143,8 @@ public final class NameResolverTools { /** * Checks if some string contains last component one of name - * @param text test to check + * + * @param text test to check * @param names */ public static boolean isContainsName(@NotNull final String text, @NotNull final FQNamesProvider names) { @@ -153,15 +155,32 @@ public final class NameResolverTools { } return false; } + /** * Checks if some file contains last component one of name - * @param file file to check + * + * @param file file to check * @param names */ public static boolean isContainsName(@NotNull final PsiFile file, @NotNull final FQNamesProvider names) { return isContainsName(file.getText(), names); } + /** + * Check if class has parent with some name + * @param child class to check + */ + public static boolean isSubclass(@NotNull final PyClass child, + @NotNull final FQNamesProvider parentName, + @NotNull final TypeEvalContext context) { + for (final String nameToCheck : parentName.getNames()) { + if (child.isSubclass(nameToCheck, context)) { + return true; + } + } + return false; + } + /** * Looks for call of some function */ diff --git a/python/python-community-configure/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java b/python/python-community-configure/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java index f8124626d520..df01a1c3130d 100644 --- a/python/python-community-configure/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java +++ b/python/python-community-configure/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java @@ -15,6 +15,7 @@ */ package com.jetbrains.python.configuration; +import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.options.ConfigurationException; @@ -31,6 +32,7 @@ import com.intellij.openapi.ui.ComboBox; import com.intellij.openapi.ui.FixedSizeButton; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.CollectionComboBoxModel; @@ -71,6 +73,8 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable { private static final String SHOW_ALL = PyBundle.message("active.sdk.dialog.show.all.item"); private Set myInitialSdkSet; + private Disposable myDisposable = null; + public PyActiveSdkConfigurable(@NotNull Project project) { myModule = null; myProject = project; @@ -149,14 +153,21 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable { c.weightx = 0.0; myMainPanel.add(myDetailsButton, c); + final PyCustomSdkUiProvider customUiProvider = PyCustomSdkUiProvider.getInstance(); + if (customUiProvider != null) { + myDisposable = Disposer.newDisposable(); + customUiProvider.customizeActiveSdkPanel(myProject, mySdkCombo, myMainPanel, c, myDisposable); + } + c.insets = new Insets(2,2,0,2); c.gridx = 0; - c.gridy = 1; + c.gridy++; c.gridwidth = 3; + c.weightx = 0.0; myMainPanel.add(emptyLabel, c); c.gridx = 0; - c.gridy = 2; + c.gridy++; c.weighty = 1.; c.gridwidth = 3; c.gridheight = GridBagConstraints.RELATIVE; @@ -165,7 +176,7 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable { c.gridheight = GridBagConstraints.REMAINDER; c.gridx = 0; - c.gridy = 3; + c.gridy++; c.gridwidth = 3; c.weighty = 0.; c.fill = GridBagConstraints.HORIZONTAL; @@ -374,6 +385,9 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable { public void disposeUIResources() { myProjectSdksModel.removeListener(mySdkModelListener); myInterpreterList.disposeModel(); + if (myDisposable != null) { + Disposer.dispose(myDisposable); + } } private class MySdkModelListener implements SdkModel.Listener { diff --git a/python/src/META-INF/python-core-common.xml b/python/src/META-INF/python-core-common.xml index d4ce10ae8af3..30bb68914ed7 100644 --- a/python/src/META-INF/python-core-common.xml +++ b/python/src/META-INF/python-core-common.xml @@ -611,6 +611,7 @@ + diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index da279f743a53..51fd13b7e041 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -890,6 +890,8 @@ sdk.gen.stubs.for.binary.modules=Generate stubs for binary module {0} # Active SDK configurable and related dialogs active.sdk.dialog.show.all.item=Show All active.sdk.dialog.project.interpreter=Project Interpreter: +active.sdk.dialog.project.interpreter.path.mappings=Path mappings: +active.sdk.dialog.project.interpreter.path.mappings.default.project.error=Open or create project to configure mappings sdk.details.step.add.local=Add Local sdk.details.step.add.remote=Add Remote diff --git a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java index c9bf4b266bcd..5f68c45df0bd 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java @@ -312,7 +312,7 @@ public class PyTargetExpressionImpl extends PyBaseElementImplemptyMap(), context); } diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index 368c9ee05bf9..4e42df6e4a60 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -368,15 +368,12 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { @Nullable @Override public PyType getReturnType(@NotNull TypeEvalContext context) { - if (isDefinition()) { - return new PyClassTypeImpl(getPyClass(), false); - } - return null; + return getCallType(context, null); } @Nullable @Override - public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyCallSiteExpression callSite) { + public PyType getCallType(@NotNull TypeEvalContext context, @Nullable PyCallSiteExpression callSite) { if (!isDefinition()) { final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context); final List resolveResults = resolveMember(PyNames.CALL, callSite, AccessDirection.READ, resolveContext); @@ -392,6 +389,8 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { return PyUnionType.union(result); } + } else { + return new PyClassTypeImpl(getPyClass(), false); } return null; diff --git a/python/src/com/jetbrains/python/psi/types/PyCollectionTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyCollectionTypeImpl.java index 933df88216f9..e89d85dab256 100644 --- a/python/src/com/jetbrains/python/psi/types/PyCollectionTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyCollectionTypeImpl.java @@ -16,6 +16,7 @@ package com.jetbrains.python.psi.types; import com.intellij.psi.PsiElement; +import com.jetbrains.python.psi.PyCallSiteExpression; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyPsiFacade; import org.jetbrains.annotations.NotNull; @@ -34,6 +35,22 @@ public class PyCollectionTypeImpl extends PyClassTypeImpl implements PyCollectio myElementTypes = elementTypes; } + + @Nullable + @Override + public PyType getReturnType(@NotNull final TypeEvalContext context) { + if (isDefinition()) { + return new PyCollectionTypeImpl(getPyClass(), false, myElementTypes); + } + return null; + } + + @Nullable + @Override + public PyType getCallType(@NotNull final TypeEvalContext context, @Nullable final PyCallSiteExpression callSite) { + return getReturnType(context); + } + @NotNull @Override public List getElementTypes(@NotNull TypeEvalContext context) { @@ -52,6 +69,11 @@ public class PyCollectionTypeImpl extends PyClassTypeImpl implements PyCollectio return new PyCollectionTypeImpl(pyClass, isDefinition, elementTypes); } + @Override + public PyClassType toInstance() { + return myIsDefinition ? new PyCollectionTypeImpl(myClass, false, myElementTypes) : this; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -81,4 +103,4 @@ public class PyCollectionTypeImpl extends PyClassTypeImpl implements PyCollectio } return result; } -} +} \ No newline at end of file diff --git a/python/src/com/jetbrains/python/sdk/PyCustomSdkUiProvider.java b/python/src/com/jetbrains/python/sdk/PyCustomSdkUiProvider.java new file mode 100644 index 000000000000..350b6d190613 --- /dev/null +++ b/python/src/com/jetbrains/python/sdk/PyCustomSdkUiProvider.java @@ -0,0 +1,44 @@ +/* + * 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://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.jetbrains.python.sdk; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.ComboBox; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; + +public abstract class PyCustomSdkUiProvider { + public final static ExtensionPointName EP_NAME = + ExtensionPointName.create("Pythonid.pyCustomSdkUiProvider"); + + @Nullable + public static PyCustomSdkUiProvider getInstance() { + if (EP_NAME.getExtensions().length > 0) { + return EP_NAME.getExtensions()[0]; + } + else { + return null; + } + } + + public abstract void customizeActiveSdkPanel(@NotNull Project project, @NotNull ComboBox mySdkCombo, @NotNull JPanel myMainPanel, + @NotNull GridBagConstraints c, @NotNull Disposable disposable); +} diff --git a/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java b/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java index ae436c2f45d2..f9381c9fc09c 100644 --- a/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java +++ b/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java @@ -17,6 +17,8 @@ package com.jetbrains.python.fixtures; import com.google.common.base.Joiner; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupEx; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ex.QuickFixWrapper; import com.intellij.execution.actions.ConfigurationContext; @@ -439,5 +441,30 @@ public abstract class PyTestCase extends UsefulTestCase { //noinspection ConstantConditions return getCommonCodeStyleSettings().getIndentOptions(); } + + /** + * When you have more than one completion variant, you may use this method providing variant to choose. + * It only works for one caret (multiple carets not supported) and since it puts tab after completion, be sure to limit + * line somehow (i.e. with comment). + *
+ * Example: "user.n[caret]." There are "name" and "nose" fields. + * By calling this function with "nose" you will end with "user.nose ". + */ + protected final void completeCaretWithMultipleVariants(@NotNull final String... desiredVariants) { + final LookupElement[] lookupElements = myFixture.completeBasic(); + final LookupEx lookup = myFixture.getLookup(); + if (lookupElements != null && lookupElements.length > 1) { + // More than one element returned, check directly because completion can't work in this case + for (final LookupElement element : lookupElements) { + final String suggestedString = element.getLookupString(); + if (Arrays.asList(desiredVariants).contains(suggestedString)) { + myFixture.getLookup().setCurrentItem(element); + lookup.setCurrentItem(element); + myFixture.completeBasicAllCarets('\t'); + return; + } + } + } + } } diff --git a/xml/relaxng/src/org/intellij/plugins/relaxNG/inspections/BaseInspection.java b/xml/relaxng/src/org/intellij/plugins/relaxNG/inspections/BaseInspection.java index 03916fce7322..df6768e5f5f3 100644 --- a/xml/relaxng/src/org/intellij/plugins/relaxNG/inspections/BaseInspection.java +++ b/xml/relaxng/src/org/intellij/plugins/relaxNG/inspections/BaseInspection.java @@ -140,6 +140,11 @@ public abstract class BaseInspection extends XmlSuppressableInspectionTool { public String getFamilyName() { return action.getFamilyName(); } + + @Override + public boolean isSuppressAll() { + return action.isSuppressAll(); + } }; } }, SuppressQuickFix.EMPTY_ARRAY); @@ -222,5 +227,10 @@ public abstract class BaseInspection extends XmlSuppressableInspectionTool { } protected abstract PsiElement getTarget(PsiElement element); + + @Override + public boolean isSuppressAll() { + return false; + } } } diff --git a/xml/xml-psi-api/src/com/intellij/codeInspection/XmlSuppressableInspectionTool.java b/xml/xml-psi-api/src/com/intellij/codeInspection/XmlSuppressableInspectionTool.java index a78c969b0424..34c670b74dfa 100644 --- a/xml/xml-psi-api/src/com/intellij/codeInspection/XmlSuppressableInspectionTool.java +++ b/xml/xml-psi-api/src/com/intellij/codeInspection/XmlSuppressableInspectionTool.java @@ -82,6 +82,11 @@ public abstract class XmlSuppressableInspectionTool extends LocalInspectionTool public void setShouldBeAppliedToInjectionHost(@NotNull ThreeState shouldBeAppliedToInjectionHost) { myShouldBeAppliedToInjectionHost = shouldBeAppliedToInjectionHost; } + + @Override + public boolean isSuppressAll() { + return false; + } } public static class SuppressTagStatic extends XmlSuppressFix {