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 975794d9419f..007fbdc98cf1 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 @@ -1181,7 +1181,7 @@ public class HighlightMethodUtil { continue; } } - if (JavaGenericsUtil.isRawToGeneric(currentType, otherSuperReturnType)) continue; + if (currentMethod.getTypeParameters().length > 0 && JavaGenericsUtil.isRawToGeneric(currentType, otherSuperReturnType)) continue; } return createIncompatibleReturnTypeMessage(currentMethod, otherSuperMethod, otherSuperReturnType, currentType, JavaErrorMessages.message("unrelated.overriding.methods.return.types"), diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java index 66dfd93bd79c..db56e8be3dc8 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java @@ -684,10 +684,6 @@ public class HighlightUtil extends HighlightUtilBase { String message = JavaErrorMessages.message("underscore.lambda.identifier"); return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(identifier).descriptionAndTooltip(message).create(); } - else { - String message = JavaErrorMessages.message("underscore.identifier"); - return HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING).range(identifier).descriptionAndTooltip(message).create(); - } } return null; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/compiler/JavacQuirksInspectionVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/compiler/JavacQuirksInspectionVisitor.java index 740a4101cf90..63d4c891d255 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/compiler/JavacQuirksInspectionVisitor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/compiler/JavacQuirksInspectionVisitor.java @@ -15,9 +15,11 @@ */ package com.intellij.codeInspection.compiler; +import com.intellij.codeInsight.daemon.JavaErrorMessages; import com.intellij.codeInspection.InspectionsBundle; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.patterns.ElementPattern; +import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; @@ -63,4 +65,14 @@ public class JavacQuirksInspectionVisitor extends JavaElementVisitor { }); } } + + @Override + public void visitIdentifier(PsiIdentifier identifier) { + super.visitIdentifier(identifier); + if (PsiUtil.getLanguageLevel(identifier).isAtLeast(LanguageLevel.JDK_1_8)) { + if ("_".equals(identifier.getText())) { + myHolder.registerProblem(identifier, JavaErrorMessages.message("underscore.identifier")); + } + } + } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspectionBase.java index 83fd6c56f28d..fa2789880371 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspectionBase.java @@ -198,6 +198,18 @@ public class UncheckedWarningLocalInspectionBase extends BaseJavaBatchLocalInspe } } + @Override + public void visitMethodReferenceExpression(PsiMethodReferenceExpression expression) { + super.visitMethodReferenceExpression(expression); + if (IGNORE_UNCHECKED_CALL) return; + final JavaResolveResult result = expression.advancedResolve(false); + final String description = getUncheckedCallDescription(result); + if (description != null) { + final PsiElement referenceNameElement = expression.getReferenceNameElement(); + registerProblem(description, referenceNameElement != null ? referenceNameElement : expression, myGenerifyFixes); + } + } + @Override public void visitCallExpression(PsiCallExpression callExpression) { super.visitCallExpression(callExpression); @@ -383,8 +395,9 @@ public class UncheckedWarningLocalInspectionBase extends BaseJavaBatchLocalInspe @Nullable private String getUncheckedCallDescription(JavaResolveResult resolveResult) { - final PsiMethod method = (PsiMethod)resolveResult.getElement(); - if (method == null) return null; + final PsiElement element = resolveResult.getElement(); + if (!(element instanceof PsiMethod)) return null; + final PsiMethod method = (PsiMethod)element; final PsiSubstitutor substitutor = resolveResult.getSubstitutor(); if (!PsiUtil.isRawSubstitutor(method, substitutor)) return null; final PsiParameter[] parameters = method.getParameterList().getParameters(); diff --git a/java/java-psi-api/src/com/intellij/psi/LambdaHighlightingUtil.java b/java/java-psi-api/src/com/intellij/psi/LambdaHighlightingUtil.java index 178001bc424c..503328ab46c5 100644 --- a/java/java-psi-api/src/com/intellij/psi/LambdaHighlightingUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/LambdaHighlightingUtil.java @@ -36,7 +36,7 @@ public class LambdaHighlightingUtil { @Nullable public static String checkInterfaceFunctional(@NotNull PsiClass psiClass, String interfaceNonFunctionalMessage) { if (psiClass instanceof PsiTypeParameter) return null; //should be logged as cyclic inference - final List signatures = LambdaUtil.findFunctionCandidates(psiClass); + final List signatures = LambdaUtil.findFunctionCandidates(psiClass); if (signatures == null) return interfaceNonFunctionalMessage; if (signatures.isEmpty()) return "No target method found"; if (signatures.size() == 1) { @@ -97,15 +97,23 @@ public class LambdaHighlightingUtil { @Nullable public static String checkInterfaceFunctional(PsiType functionalInterfaceType) { if (functionalInterfaceType instanceof PsiIntersectionType) { + int count = 0; for (PsiType type : ((PsiIntersectionType)functionalInterfaceType).getConjuncts()) { - if (checkInterfaceFunctional(type) == null) return null; + if (checkInterfaceFunctional(type) == null) { + count++; + } } + + if (count > 1) { + return "Multiple non-overriding abstract methods found in " + functionalInterfaceType.getPresentableText(); + } + return null; } final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType); final PsiClass aClass = resolveResult.getElement(); if (aClass != null) { if (aClass instanceof PsiTypeParameter) return null; //should be logged as cyclic inference - final List signatures = LambdaUtil.findFunctionCandidates(aClass); + final List signatures = LambdaUtil.findFunctionCandidates(aClass); if (signatures != null && signatures.size() == 1) { final MethodSignature functionalMethod = signatures.get(0); if (functionalMethod.getTypeParameters().length > 0) return "Target method is generic"; diff --git a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java index e56114029361..fcf5e8945e7a 100644 --- a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java @@ -103,9 +103,7 @@ public class LambdaUtil { public static boolean isFunctionalType(PsiType type) { if (type instanceof PsiIntersectionType) { - for (PsiType type1 : ((PsiIntersectionType)type).getConjuncts()) { - if (isFunctionalType(type1)) return true; - } + return extractFunctionalConjunct((PsiIntersectionType)type) != null; } return isFunctionalClass(PsiUtil.resolveGenericsClassInType(type).getElement()); } @@ -114,7 +112,7 @@ public class LambdaUtil { public static boolean isFunctionalClass(PsiClass aClass) { if (aClass != null) { if (aClass instanceof PsiTypeParameter) return false; - final List signatures = findFunctionCandidates(aClass); + final List signatures = findFunctionCandidates(aClass); return signatures != null && signatures.size() == 1; } return false; @@ -144,7 +142,7 @@ public class LambdaUtil { @Nullable static MethodSignature getFunction(PsiClass psiClass) { if (psiClass == null) return null; - final List functions = findFunctionCandidates(psiClass); + final List functions = findFunctionCandidates(psiClass); if (functions != null && functions.size() == 1) { return functions.get(0); } @@ -178,12 +176,13 @@ public class LambdaUtil { } @Nullable - private static List hasSubsignature(List signatures) { - for (MethodSignature signature : signatures) { + private static List hasSubsignature(List signatures) { + for (HierarchicalMethodSignature signature : signatures) { boolean subsignature = true; - for (MethodSignature methodSignature : signatures) { + for (HierarchicalMethodSignature methodSignature : signatures) { if (!signature.equals(methodSignature)) { - if (!MethodSignatureUtil.isSubsignature(signature, methodSignature)) { + if (!MethodSignatureUtil.isSubsignature(signature, methodSignature) && + !skipMethod(signature, methodSignature)) { subsignature = false; break; } @@ -194,10 +193,20 @@ public class LambdaUtil { return signatures; } + private static boolean skipMethod(HierarchicalMethodSignature signature, + HierarchicalMethodSignature methodSignature) { + //not generic + if (methodSignature.getTypeParameters().length == 0) { + return false; + } + //foreign class + return signature.getMethod().getContainingClass() != methodSignature.getMethod().getContainingClass(); + } + @Nullable - public static List findFunctionCandidates(PsiClass psiClass) { + public static List findFunctionCandidates(PsiClass psiClass) { if (psiClass != null && psiClass.isInterface() && !psiClass.isAnnotationType()) { - final List methods = new ArrayList(); + final List methods = new ArrayList(); final Collection visibleSignatures = psiClass.getVisibleSignatures(); for (HierarchicalMethodSignature signature : visibleSignatures) { final PsiMethod psiMethod = signature.getMethod(); @@ -286,9 +295,8 @@ public class LambdaUtil { } else if (parent instanceof PsiTypeCastExpression) { final PsiType castType = ((PsiTypeCastExpression)parent).getType(); if (castType instanceof PsiIntersectionType) { - for (PsiType conjunctType : ((PsiIntersectionType)castType).getConjuncts()) { - if (getFunctionalInterfaceMethod(conjunctType) != null) return conjunctType; - } + final PsiType conjunct = extractFunctionalConjunct((PsiIntersectionType)castType); + if (conjunct != null) return conjunct; } return castType; } @@ -363,6 +371,19 @@ public class LambdaUtil { return null; } + @Nullable + private static PsiType extractFunctionalConjunct(PsiIntersectionType type) { + PsiType conjunct = null; + for (PsiType conjunctType : type.getConjuncts()) { + final PsiMethod interfaceMethod = getFunctionalInterfaceMethod(conjunctType); + if (interfaceMethod != null) { + if (conjunct != null && !conjunct.equals(conjunctType)) return null; + conjunct = conjunctType; + } + } + return conjunct; + } + private static PsiType getFunctionalInterfaceTypeByContainingLambda(@NotNull PsiLambdaExpression parentLambda) { final PsiType parentInterfaceType = parentLambda.getFunctionalInterfaceType(); return parentInterfaceType != null ? getFunctionalInterfaceReturnType(parentInterfaceType) : null; diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java index 361462430105..9d9c373159f9 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java @@ -1070,7 +1070,7 @@ public class InferenceSession { session.addConstraint(new StrictSubtypingConstraint(tReturnType, sReturnType)); return true; } else { - return TypeConversionUtil.isAssignable(sReturnType, tReturnType); + return sReturnType != null && tReturnType != null && TypeConversionUtil.isAssignable(tReturnType, sReturnType); } } } @@ -1115,7 +1115,7 @@ public class InferenceSession { session.addConstraint(new StrictSubtypingConstraint(tReturnType, sReturnType)); return true; } else { - return TypeConversionUtil.isAssignable(sReturnType, tReturnType); + return sReturnType != null && tReturnType != null && TypeConversionUtil.isAssignable(tReturnType, sReturnType); } } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/InputOutputConstraintFormula.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/InputOutputConstraintFormula.java index 6ff17092dfd6..67097df574a0 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/InputOutputConstraintFormula.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/InputOutputConstraintFormula.java @@ -65,7 +65,10 @@ public abstract class InputOutputConstraintFormula implements ConstraintFormula } } - collectReturnTypeVariables(session, psiExpression, substitutor.substitute(interfaceMethod.getReturnType()), result); + final PsiType returnType = interfaceMethod.getReturnType(); + if (returnType != null) { + collectReturnTypeVariables(session, psiExpression, substitutor.substitute(returnType), result); + } return result; } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/MethodReferenceResolver.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/MethodReferenceResolver.java index cf25710008bc..62ddc0d97722 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/MethodReferenceResolver.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/MethodReferenceResolver.java @@ -156,7 +156,11 @@ class MethodReferenceResolver implements ResolveCache.PolyVariantResolver m(List ls); + } + + interface B { + Iterable m(List l); + } + + interface AB extends A, B {} +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/advHighlighting/Underscore.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/advHighlighting/Underscore.java new file mode 100644 index 000000000000..50c8d6ce9116 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/advHighlighting/Underscore.java @@ -0,0 +1,3 @@ +class Test { + void _(){} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/FunctionalInterfaceAnnotation3.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/FunctionalInterfaceAnnotation3.java new file mode 100644 index 000000000000..6e392f2d74d6 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/FunctionalInterfaceAnnotation3.java @@ -0,0 +1,8 @@ +interface I { + void m(T t); + void m(K k); + } + +@FunctionalInterface +interface IEx extends I { } + diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/GenericNotGenericInterfaceMethod.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/GenericNotGenericInterfaceMethod.java new file mode 100644 index 000000000000..71b286a5268e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/GenericNotGenericInterfaceMethod.java @@ -0,0 +1,43 @@ +class Test { + + interface A { + void m(); + } + + interface B { + void m(); + } + + interface C extends A, B { } + + { + C c = ()-> {}; + } +} + +class Test1 { + + interface F { + void m(); + } + + { + F f = this::g; + } + + void g() {} +} + +class Test2 { + + interface F { + void m(); + void a(); + } + + { + F f = () -> g(); + } + + void g() {} +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/InferredFromCast.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/InferredFromCast.java new file mode 100644 index 000000000000..e31cf5a86ffd --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/InferredFromCast.java @@ -0,0 +1,14 @@ +class Test { + + interface A { + int m(); + } + + interface B { + int m(int i); + } + + public static void main(String[] args) { + A a = ()-> ((B)i -> i).m(3); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/Intersection.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/Intersection.java new file mode 100644 index 000000000000..767b54788948 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/Intersection.java @@ -0,0 +1,35 @@ +import java.io.Serializable; + +class Test { + + interface I { + void foo(); + } + + interface A { + void bar(int i); + } + + { + Object o1 = (Serializable & I) () -> {}; + Object o2 = (I & Serializable) () -> {}; + Object o3 = (I & Runnable) () -> {}; + Object o4 = (A & Runnable) () -> {}; + Object o5 = (Runnable & A) () -> {}; + } +} + +class Test1 { + + interface A { + void foo(); + } + + interface B { + void foo(); + } + + { + Object c0 = (A & B) ()->{}; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/NoBoxingInLambdaFormalParams.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/NoBoxingInLambdaFormalParams.java new file mode 100644 index 000000000000..30e096ff8de1 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/NoBoxingInLambdaFormalParams.java @@ -0,0 +1,8 @@ +class LambdaConv10 { + + interface I { public R call( T t); } + + { + I in = (int i) -> 2 * i; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/ReferencedFromSelf.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/ReferencedFromSelf.java new file mode 100644 index 000000000000..5b029df7ce4a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/ReferencedFromSelf.java @@ -0,0 +1,13 @@ +class Test { + + Runnable r; + { r = r::run; } + Runnable r1; + { r1 = () -> r1.run(); } + + + { + Runnable r = () -> r.run(); + Runnable r1 = r1::run; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/Underscores.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/Underscores.java index 2d89ec317ed9..154a75af1b23 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/Underscores.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/Underscores.java @@ -1,10 +1,5 @@ class C { void test() { - { - I _ = new I() { public void f(int i) { } }; - accept(_); - } - { accept(_ -> System.out.println(_)); accept((int _) -> System.out.println(_)); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/incomplete/MissedFunctionalInterfaceMethodReturnType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/incomplete/MissedFunctionalInterfaceMethodReturnType.java new file mode 100644 index 000000000000..574268764010 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/incomplete/MissedFunctionalInterfaceMethodReturnType.java @@ -0,0 +1,35 @@ +class Test { + + interface A { + A f(); + } + interface B {} + + static abstract class C implements A, B {} + static abstract class D implements A, B {} + + interface I { + m(T arg); + } + + void bar(C c) { + foo(c, x -> x.f()); + foo(c, x -> x); + } + + void foo(T t1, I t3) {} + +} + +class Test2 { + + interface F { + m(); + } + + void g() {} + + { + F f = this::g; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/QualifiersInStaticContext.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/QualifiersInStaticContext.java new file mode 100644 index 000000000000..483237105eb2 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/QualifiersInStaticContext.java @@ -0,0 +1,11 @@ +public class Test { + interface I { + void foo(); + } + + private Object o; + + public static void main(String[] args){ + I i = o::wait; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/BoxingAndOverloadResolution.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/BoxingAndOverloadResolution.java new file mode 100644 index 000000000000..36db3014c415 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/BoxingAndOverloadResolution.java @@ -0,0 +1,33 @@ +class Test { + private static void m(int i) {System.out.println(i);} + private static void m(Integer i) {System.out.println(i);} + + interface I { + void foo(int p); + } + + static { + I s = Test::m; + System.out.println(s); + } +} + +class Test2 { + + static void m(Integer i) { } + + interface I1 { + void m(int x); + } + + interface I2 { + void m(Integer x); + } + + static void call(I1 i1) { System.out.println(i1); } + static void call(I2 i2) { System.out.println(i2); } + + static { + call(Test2::m); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/DifferentParamsLength.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/DifferentParamsLength.java new file mode 100644 index 000000000000..951858bae638 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/DifferentParamsLength.java @@ -0,0 +1,29 @@ +class Test { + + interface I0 { + void m(); + } + + interface I1 { + void m(Object a); + } + + interface I2 { + void m(Object a1, Object a2); + } + + interface IVarargs { + void m(Object... as); + } + + void call(I0 p) { } + void call(I1 p) { } + void call(I2 p) { } + void call(IVarargs p) { } + + void test() { + call(() -> { }); + call(p1 -> { }); + call((p1, p2) -> {}); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/IncompleteMethodInInterface.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/IncompleteMethodInInterface.java new file mode 100644 index 000000000000..8d71a82cc6b4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/IncompleteMethodInInterface.java @@ -0,0 +1,12 @@ +class Test { + interface I { Object in + voke(); } + interface IStr { String foo(); } + + public static void call(IStr str) {} + public static void call(I i) { } + + public static void main(String[] args) { + call(()-> null); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/MostSpecificByReturnType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/MostSpecificByReturnType.java new file mode 100644 index 000000000000..5588c3e623a1 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/MostSpecificByReturnType.java @@ -0,0 +1,16 @@ +class Test { + interface I { Object invoke(); } + interface IStr { String foo(); } + + private static void call(IStr str) { + System.out.println(str); + } + + private static void call(I i) { + System.out.println(i); + } + + public static void main(String[] args) { + call(()-> null); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/NoReturnTypeResolutionForThrownException.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/NoReturnTypeResolutionForThrownException.java new file mode 100644 index 000000000000..81429131d099 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/NoReturnTypeResolutionForThrownException.java @@ -0,0 +1,14 @@ +class Test { + + interface I1 {String m();} + interface I2 {void m();} + + void call(I1 p) { } + void call(I2 p) { } + + void test() { + call(() -> { throw new RuntimeException(); }); + call(() -> { if (true) return ""; throw new RuntimeException(); }); + call(() -> { if (true) return; throw new RuntimeException(); }); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/LiftedIntersectionType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/LiftedIntersectionType.java new file mode 100644 index 000000000000..de9d517da449 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/LiftedIntersectionType.java @@ -0,0 +1,22 @@ +class Test { + + interface A { + A f(); + } + interface B {} + + static abstract class C implements A, B {} + static abstract class D implements A, B {} + + interface I { + T m(T arg); + } + + void bar(C c) { + foo(c, x -> x.f()); + foo(c, x -> x); + } + + void foo(T t1, I t3) {} + +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/OnlyLambdaAtTypeParameterPlace.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/OnlyLambdaAtTypeParameterPlace.java new file mode 100644 index 000000000000..aee4c8462cdf --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/OnlyLambdaAtTypeParameterPlace.java @@ -0,0 +1,13 @@ +class Test { + interface I { + void m(); + } + + void call(T t) {} + void call1(T t) {} + + { + call(() -> {}); + call1(() -> {}); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/AfterDistinctOps.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/AfterDistinctOps.java index ee24b8c2ba93..9976acbfca3d 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/AfterDistinctOps.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/AfterDistinctOps.java @@ -5,7 +5,7 @@ class BBB { static void f() { - TerminalOp> reduceOp = BBB.>makeRef(LinkedHashSet::new, LinkedHashSet::add, LinkedHashSet::addAll); + TerminalOp> reduceOp = BBB.>makeRef(LinkedHashSet::new, LinkedHashSet::add, LinkedHashSet::addAll); } public static TerminalOp makeRef(U seed, BiFunction reducer, BinaryOperator combiner) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/RefOnStaticInterfaceMethod.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/RefOnStaticInterfaceMethod.java new file mode 100644 index 000000000000..6687f96d69a1 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/RefOnStaticInterfaceMethod.java @@ -0,0 +1,13 @@ +interface I { + static void a() {} +} + +interface J { + void foo(); +} + +class Test { + { + J j = I::a; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/UncheckedMethodReference.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/UncheckedMethodReference.java new file mode 100644 index 000000000000..4b451f2eed9b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/UncheckedMethodReference.java @@ -0,0 +1,15 @@ +import java.util.*; + +class Test { + + interface I { + void m(List l, T el); + } + + { + I i1 = List::add; + System.out.println(i1); + I i2 = List::add; + System.out.println(i2); + } +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java index f4e40bde2970..c0ef03ce72b3 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java @@ -341,6 +341,7 @@ public class GenericsHighlightingTest extends LightDaemonAnalyzerTestCase { public void testIDEA120563() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } public void testIDEA121400() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } public void testIDEA123316() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } + public void testUnrelatedReturnInTypeArgs() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } public void testIDEA123352() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } public void testIDEA123518() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } public void testIDEA64103() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/FunctionalExpressionIncompleteHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/FunctionalExpressionIncompleteHighlightingTest.java new file mode 100644 index 000000000000..3d609159404a --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/FunctionalExpressionIncompleteHighlightingTest.java @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.daemon.lambda; + +import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase; +import com.intellij.codeInspection.LocalInspectionTool; +import com.intellij.codeInspection.unusedSymbol.UnusedSymbolLocalInspection; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.testFramework.IdeaTestUtil; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class FunctionalExpressionIncompleteHighlightingTest extends LightDaemonAnalyzerTestCase { + @NonNls static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/lambda/incomplete"; + + @NotNull + @Override + protected LocalInspectionTool[] configureLocalInspectionTools() { + return new LocalInspectionTool[]{ + new UnusedSymbolLocalInspection(), + }; + } + + public void testMissedFunctionalInterfaceMethodReturnType() throws Exception { + doTest(); + } + + private void doTest() { + doTest(false); + } + + private void doTest(final boolean checkWarnings) { + doTestNewInference(BASE_PATH + "/" + getTestName(false) + ".java", checkWarnings, false); + } + + @Override + protected Sdk getProjectJDK() { + return IdeaTestUtil.getMockJdk18(); + } +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java index aa18173074fa..a3a326cfcf81 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java @@ -78,6 +78,7 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase { public void testAmbiguitySpecificReturn() { doTest(true); } public void testFunctionalInterfaceAnnotation() { doTest(); } public void testFunctionalInterfaceAnnotation2() { doTest(); } + public void testFunctionalInterfaceAnnotation3() { doTest(); } public void testAmbiguityReturnValueResolution() { doTest(); } public void testAmbiguityReturnValueResolution1() { doTest(); } public void testAmbiguityReturnValueResolution2() { doTest(true); } @@ -100,6 +101,11 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase { public void testBreakContinueInside() {doTest();} public void testSameLambdaParamNames() {doTest();} public void testIDEA123308() {doTest();} + public void testIntersection() {doTest();} + public void testNoBoxingInLambdaFormalParams() {doTest();} + public void testGenericNotGenericInterfaceMethod() {doTest();} + public void testInferredFromCast() {doTest();} + public void testReferencedFromSelf() {doTest();} private void doTest() { doTest(false); diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LightAdvHighlightingJdk8Test.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LightAdvHighlightingJdk8Test.java new file mode 100644 index 000000000000..630acb0404b7 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LightAdvHighlightingJdk8Test.java @@ -0,0 +1,52 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.daemon.lambda; + +import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase; +import com.intellij.codeInspection.LocalInspectionTool; +import com.intellij.codeInspection.compiler.JavacQuirksInspection; +import com.intellij.codeInspection.unusedSymbol.UnusedSymbolLocalInspection; +import com.intellij.openapi.projectRoots.JavaSdkVersion; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.testFramework.IdeaTestUtil; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class LightAdvHighlightingJdk8Test extends LightDaemonAnalyzerTestCase { + @NonNls static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/lambda/advHighlighting"; + + @NotNull + @Override + protected LocalInspectionTool[] configureLocalInspectionTools() { + return new LocalInspectionTool[]{ + new JavacQuirksInspection(), + }; + } + + public void testUnderscore() throws Exception { + doTest(); + } + + private void doTest() { + IdeaTestUtil.setTestVersion(JavaSdkVersion.JDK_1_8, getModule(), getTestRootDisposable()); + doTestNewInference(BASE_PATH + "/" + getTestName(false) + ".java", true, false); + } + + @Override + protected Sdk getProjectJDK() { + return IdeaTestUtil.getMockJdk18(); + } +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MethodRefHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MethodRefHighlightingTest.java index 1d09f51a07c9..b5c69c1358f9 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MethodRefHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MethodRefHighlightingTest.java @@ -99,6 +99,10 @@ public class MethodRefHighlightingTest extends LightDaemonAnalyzerTestCase { doTest(); } + public void testQualifiersInStaticContext() throws Exception { + doTest(); + } + private void doTest() { doTest(false); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MostSpecificResolutionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MostSpecificResolutionTest.java index a46036cb98d4..e2a14117d4f2 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MostSpecificResolutionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MostSpecificResolutionTest.java @@ -75,6 +75,26 @@ public class MostSpecificResolutionTest extends LightDaemonAnalyzerTestCase { doTest(); } + public void testIncompleteMethodInInterface() throws Exception { + doTest(false); + } + + public void testMostSpecificByReturnType() throws Exception { + doTest(); + } + + public void testDifferentParamsLength() throws Exception { + doTest(false); + } + + public void testNoReturnTypeResolutionForThrownException() throws Exception { + doTest(false); + } + + public void testBoxingAndOverloadResolution() throws Exception { + doTest(); + } + private void doTest() { doTest(true); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewLambdaHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewLambdaHighlightingTest.java index 5e21a82fa200..f5f286a17e31 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewLambdaHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewLambdaHighlightingTest.java @@ -198,6 +198,14 @@ public class NewLambdaHighlightingTest extends LightDaemonAnalyzerTestCase { doTest(); } + public void testOnlyLambdaAtTypeParameterPlace() throws Exception { + doTest(); + } + + public void testLiftedIntersectionType() throws Exception { + doTest(); + } + private void doTest() { doTest(false); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java index 54ee87c1af8b..4dbe40750de2 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java @@ -237,6 +237,14 @@ public class NewMethodRefHighlightingTest extends LightDaemonAnalyzerTestCase { doTest(); } + public void testRefOnStaticInterfaceMethod() throws Exception { + doTest(); + } + + public void testUncheckedMethodReference() throws Exception { + doTest(true); + } + private void doTest() { doTest(false); } diff --git a/platform/lang-impl/src/com/intellij/application/options/ModuleAwareProjectConfigurable.java b/platform/lang-impl/src/com/intellij/application/options/ModuleAwareProjectConfigurable.java index 71b7dc7e7169..f7d8ab8e7df1 100644 --- a/platform/lang-impl/src/com/intellij/application/options/ModuleAwareProjectConfigurable.java +++ b/platform/lang-impl/src/com/intellij/application/options/ModuleAwareProjectConfigurable.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -16,6 +16,7 @@ package com.intellij.application.options; import com.intellij.openapi.module.Module; +import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SearchableConfigurable; import com.intellij.openapi.options.UnnamedConfigurable; @@ -34,6 +35,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import javax.swing.border.EmptyBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import java.awt.*; @@ -44,7 +46,8 @@ import java.util.Map; /** * @author yole */ -public abstract class ModuleAwareProjectConfigurable implements SearchableConfigurable { +public abstract class ModuleAwareProjectConfigurable implements SearchableConfigurable, + Configurable.NoScroll { @NotNull private final Project myProject; private final String myDisplayName; @@ -113,7 +116,8 @@ public abstract class ModuleAwareProjectConfigurable map = getInspectionWrappersMap(localTools); scope.accept(new PsiElementVisitor() { @Override @@ -336,7 +337,7 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp final VirtualFile virtualFile = file.getVirtualFile(); if (virtualFile == null) return; - if (myView == null) { + if (myView == null && !headlessEnvironment) { throw new ProcessCanceledException(); } diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindInProjectTask.java b/platform/lang-impl/src/com/intellij/find/impl/FindInProjectTask.java index df47eead5c5b..90d2cc6385bd 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindInProjectTask.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindInProjectTask.java @@ -121,7 +121,12 @@ class FindInProjectTask { try { myProgress.setIndeterminate(true); myProgress.setText("Scanning indexed files..."); - final Set filesForFastWordSearch = getFilesForFastWordSearch(); + final Set filesForFastWordSearch = ApplicationManager.getApplication().runReadAction(new Computable>() { + @Override + public Set compute() { + return getFilesForFastWordSearch(); + } + }); myProgress.setIndeterminate(false); searchInFiles(filesForFastWordSearch, processPresentation, consumer); diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java index be3519eedff3..986c533bcb75 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java @@ -54,7 +54,8 @@ public class CustomizeIDEWizardDialog extends DialogWrapper implements ActionLis myNavigationLabel.setEnabled(false); myFooterLabel.setEnabled(false); init(); - initCurrentStep(); + initCurrentStep(true); + setSize(400, 300); System.setProperty(StartupActionScriptManager.STARTUP_WIZARD_MODE, "true"); } @@ -125,7 +126,7 @@ public class CustomizeIDEWizardDialog extends DialogWrapper implements ActionLis } if (e.getSource() == myBackButton) { myIndex--; - initCurrentStep(); + initCurrentStep(false); return; } if (e.getSource() == myNextButton) { @@ -134,7 +135,7 @@ public class CustomizeIDEWizardDialog extends DialogWrapper implements ActionLis return; } myIndex++; - initCurrentStep(); + initCurrentStep(true); } } @@ -148,8 +149,9 @@ public class CustomizeIDEWizardDialog extends DialogWrapper implements ActionLis super.doOKAction(); } - private void initCurrentStep() { + private void initCurrentStep(boolean forward) { final AbstractCustomizeWizardStep myCurrentStep = mySteps.get(myIndex); + myCurrentStep.beforeShown(forward); myCardLayout.swipe(myContentPanel, myCurrentStep.getTitle(), JBCardLayout.SwipeDirection.AUTO, new Runnable() { @Override public void run() { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/BackspaceAction.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/BackspaceAction.java index ad5d0c2fb9b2..5a378d4981c0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/BackspaceAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/BackspaceAction.java @@ -78,12 +78,7 @@ public class BackspaceAction extends EditorAction { private static void doBackSpaceAtCaret(@NotNull Editor editor) { if(editor.getSelectionModel().hasSelection()) { - int newOffset = editor.getSelectionModel().getSelectionStart(); - if (EditorActionUtil.canEditAtOffset(editor, newOffset)) { - editor.getCaretModel().moveToOffset(newOffset); - editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); - EditorModificationUtil.deleteSelectedText(editor); - } + doBackspaceAction(editor); return; } @@ -131,4 +126,13 @@ public class BackspaceAction extends EditorAction { } } } + + static void doBackspaceAction(@NotNull Editor editor) { + int newOffset = editor.getSelectionModel().getSelectionStart(); + if (EditorActionUtil.canEditAtOffset(editor, newOffset)) { + editor.getCaretModel().moveToOffset(newOffset); + editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); + EditorModificationUtil.deleteSelectedText(editor); + } + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/DeleteToWordEndAction.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/DeleteToWordEndAction.java index c261c6c761ae..5785f01feb33 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/DeleteToWordEndAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/DeleteToWordEndAction.java @@ -14,23 +14,22 @@ * limitations under the License. */ -/* - * Created by IntelliJ IDEA. - * User: max - * Date: May 14, 2002 - * Time: 7:18:30 PM - * To change template for new class use - * Code Style | Class Templates options (Tools | IDE Options). - */ package com.intellij.openapi.editor.actions; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.EditorModificationUtil; import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler; import com.intellij.openapi.ide.CopyPasteManager; +/* + * Created by IntelliJ IDEA. + * User: max + * Date: May 14, 2002 + * Time: 7:18:30 PM + */ public class DeleteToWordEndAction extends TextComponentEditorAction { public DeleteToWordEndAction() { super(new Handler(false)); @@ -60,6 +59,12 @@ public class DeleteToWordEndAction extends TextComponentEditorAction { if (myNegateCamelMode) { camelMode = !camelMode; } + + if (editor.getSelectionModel().hasSelection()) { + EditorModificationUtil.deleteSelectedText(editor); + return; + } + deleteToWordEnd(editor, camelMode); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/DeleteToWordStartAction.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/DeleteToWordStartAction.java index 76e251631864..b4839abc0266 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/DeleteToWordStartAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/DeleteToWordStartAction.java @@ -24,9 +24,7 @@ */ package com.intellij.openapi.editor.actions; -import com.intellij.openapi.editor.CaretModel; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.actionSystem.DataContext; @@ -81,6 +79,10 @@ public class DeleteToWordStartAction extends TextComponentEditorAction { @Override public void executeWriteAction(Editor editor, DataContext dataContext) { CommandProcessor.getInstance().setCurrentCommandGroupId(EditorActionUtil.DELETE_COMMAND_GROUP); + if (editor.getSelectionModel().hasSelection()) { + BackspaceAction.doBackspaceAction(editor); + return; + } deleteToWordStart(editor); } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java index b66fcf46abb7..58f315f0246c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java @@ -363,21 +363,23 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { return true; } - boolean doNotExecuteBecauseAppIsInactive = - !myApp.isActive() && !canExecuteOnInactiveApplication(cmd) && Registry.is("actionSystem.suspendFocusTransferIfApplicationInactive"); + if (!Registry.is("focus.fix.lost.cursor")) { + boolean doNotExecuteBecauseAppIsInactive = + !myApp.isActive() && !canExecuteOnInactiveApplication(cmd) && Registry.is("actionSystem.suspendFocusTransferIfApplicationInactive"); - if (doNotExecuteBecauseAppIsInactive) { - if (myCallbackOnActivation != null) { - myCallbackOnActivation.setRejected(); - if (myFocusCommandOnAppActivation != null) { - resetCommand(myFocusCommandOnAppActivation, true); + if (doNotExecuteBecauseAppIsInactive) { + if (myCallbackOnActivation != null) { + myCallbackOnActivation.setRejected(); + if (myFocusCommandOnAppActivation != null) { + resetCommand(myFocusCommandOnAppActivation, true); + } } + + myFocusCommandOnAppActivation = cmd; + myCallbackOnActivation = result; + + return true; } - - myFocusCommandOnAppActivation = cmd; - myCallbackOnActivation = result; - - return true; } return false; @@ -1152,6 +1154,7 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { @Override public boolean isFocusTransferEnabled() { + if (Registry.is("focus.fix.lost.cursor")) return true; return myApp.isActive() || !Registry.is("actionSystem.suspendFocusTransferIfApplicationInactive"); } diff --git a/platform/platform-impl/src/com/intellij/ui/FocusTrackback.java b/platform/platform-impl/src/com/intellij/ui/FocusTrackback.java index d22fa1960c67..9ad0931c85b9 100644 --- a/platform/platform-impl/src/com/intellij/ui/FocusTrackback.java +++ b/platform/platform-impl/src/com/intellij/ui/FocusTrackback.java @@ -25,6 +25,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.ExpirableRunnable; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.wm.FocusCommand; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.ex.LayoutFocusTraversalPolicyExt; @@ -452,7 +453,8 @@ public class FocusTrackback { } public void cleanParentWindow() { - if (myParentWindow != null) { + if (!Registry.is("focus.fix.lost.cursor")) return; + if (myParentWindow != null) { try { Method tmpLost = Window.class.getDeclaredMethod("setTemporaryLostComponent", Component.class); tmpLost.setAccessible(true); diff --git a/platform/platform-resources-en/src/misc/registry.properties b/platform/platform-resources-en/src/misc/registry.properties index 491eeb48bdda..5b67947c5fcd 100644 --- a/platform/platform-resources-en/src/misc/registry.properties +++ b/platform/platform-resources-en/src/misc/registry.properties @@ -390,3 +390,6 @@ editor.richcopy.debug=false editor.richcopy.max.size.megabytes=10 allow.dialog.based.popups=false allow.dialog.based.popups.description=Allows to use a JDialog as popup toplevel + +focus.fix.lost.cursor=true +focus.fix.lost.cursor.description=See IDEA-79312 diff --git a/platform/platform-tests/testData/editor/multiCaret/EditorDeleteToWordEnd-after.txt b/platform/platform-tests/testData/editor/multiCaret/EditorDeleteToWordEnd-after.txt index 59aa5d7e5b28..0e8f561530eb 100644 --- a/platform/platform-tests/testData/editor/multiCaret/EditorDeleteToWordEnd-after.txt +++ b/platform/platform-tests/testData/editor/multiCaret/EditorDeleteToWordEnd-after.txt @@ -1,2 +1,2 @@ -stextot text -an lyet anot line \ No newline at end of file +stextor text +aner lyet aher line \ No newline at end of file diff --git a/platform/platform-tests/testData/editor/multiCaret/EditorDeleteToWordEndInDifferentHumpsMode-after.txt b/platform/platform-tests/testData/editor/multiCaret/EditorDeleteToWordEndInDifferentHumpsMode-after.txt index 74c13eafb852..1b3f82a9edf6 100644 --- a/platform/platform-tests/testData/editor/multiCaret/EditorDeleteToWordEndInDifferentHumpsMode-after.txt +++ b/platform/platform-tests/testData/editor/multiCaret/EditorDeleteToWordEndInDifferentHumpsMode-after.txt @@ -1,2 +1,2 @@ -AbDabraHumDump HumpDu -YetAnotHumpedWord \ No newline at end of file +AbDabraHumump Hump + \ No newline at end of file diff --git a/platform/platform-tests/testData/editor/multiCaret/EditorDeleteToWordStart-after.txt b/platform/platform-tests/testData/editor/multiCaret/EditorDeleteToWordStart-after.txt index 7c5d3fcdc330..b66dd51f46f7 100644 --- a/platform/platform-tests/testData/editor/multiCaret/EditorDeleteToWordStart-after.txt +++ b/platform/platform-tests/testData/editor/multiCaret/EditorDeleteToWordStart-after.txt @@ -1,3 +1,3 @@ some t -ther t -other lineyet another line \ No newline at end of file +oer tt + lineyet another line \ No newline at end of file diff --git a/platform/platform-tests/testData/editor/multiCaret/EditorDeleteToWordStartInDifferentHumpsMode-after.txt b/platform/platform-tests/testData/editor/multiCaret/EditorDeleteToWordStartInDifferentHumpsMode-after.txt index eefa20be21eb..33dc0f78161e 100644 --- a/platform/platform-tests/testData/editor/multiCaret/EditorDeleteToWordStartInDifferentHumpsMode-after.txt +++ b/platform/platform-tests/testData/editor/multiCaret/EditorDeleteToWordStartInDifferentHumpsMode-after.txt @@ -1,3 +1,3 @@ aCa -pDump Humpmp -YetherHumpedWord \ No newline at end of file +Humump Hump + \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/InterfaceMayBeAnnotatedFunctionalInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/InterfaceMayBeAnnotatedFunctionalInspection.java index e4f77e0fbea9..b9f43f708b77 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/InterfaceMayBeAnnotatedFunctionalInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/InterfaceMayBeAnnotatedFunctionalInspection.java @@ -75,7 +75,7 @@ public class InterfaceMayBeAnnotatedFunctionalInspection extends BaseInspection if (LambdaHighlightingUtil.checkInterfaceFunctional(aClass) != null) { return; } - final List candidates = LambdaUtil.findFunctionCandidates(aClass); + final List candidates = LambdaUtil.findFunctionCandidates(aClass); if (candidates == null || candidates.size() != 1) { return; } diff --git a/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java b/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java index 82f2cf79ad6d..3641c51f1627 100644 --- a/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java +++ b/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java @@ -531,7 +531,7 @@ public class GitHistoryUtils { AUTHOR_NAME, AUTHOR_EMAIL); h.setStdoutSuppressed(true); h.addParameters(parser.getPretty(), "--encoding=UTF-8"); - h.addParameters("--full-history", "--sparse"); + h.addParameters("--full-history"); h.addParameters("--date-order"); h.addParameters(parameters); h.endOptions(); diff --git a/plugins/git4idea/src/git4idea/log/GitLogProvider.java b/plugins/git4idea/src/git4idea/log/GitLogProvider.java index 07afb4eeb09b..5f03de5b9c81 100644 --- a/plugins/git4idea/src/git4idea/log/GitLogProvider.java +++ b/plugins/git4idea/src/git4idea/log/GitLogProvider.java @@ -165,7 +165,9 @@ public class GitLogProvider implements VcsLogProvider { return Collections.emptyList(); } - return GitHistoryUtils.readCommits(myProject, root, userRegistry, GitHistoryUtils.LOG_ALL); + List parameters = new ArrayList(GitHistoryUtils.LOG_ALL); + parameters.add("--sparse"); + return GitHistoryUtils.readCommits(myProject, root, userRegistry, parameters); } @NotNull @@ -315,6 +317,7 @@ public class GitLogProvider implements VcsLogProvider { // note: structure filter must be the last parameter, because it uses "--" which separates parameters from paths if (filterCollection.getStructureFilter() != null) { + filterParameters.add("--simplify-merges"); filterParameters.add("--"); for (VirtualFile file : filterCollection.getStructureFilter().getFiles(root)) { filterParameters.add(file.getPath()); diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgBranchPopupActions.java b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgBranchPopupActions.java index 62d5981e55f7..45dd12bd9c81 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgBranchPopupActions.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgBranchPopupActions.java @@ -25,7 +25,6 @@ import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; import com.intellij.util.Function; import com.intellij.util.PlatformIcons; @@ -48,7 +47,8 @@ import org.zmlx.hg4idea.util.HgErrorUtil; import java.util.*; -import static org.zmlx.hg4idea.util.HgUtil.*; +import static org.zmlx.hg4idea.util.HgUtil.getNamesWithoutHashes; +import static org.zmlx.hg4idea.util.HgUtil.getNewBranchNameFromUser; public class HgBranchPopupActions { @@ -62,8 +62,8 @@ public class HgBranchPopupActions { ActionGroup createActions(@Nullable DefaultActionGroup toInsert) { DefaultActionGroup popupGroup = new DefaultActionGroup(null, false); - popupGroup.addAction(new HgNewBranchAction(myProject, Collections.singletonList(myRepository), myRepository.getRoot())); - popupGroup.addAction(new HgNewBookmarkAction(myProject, Collections.singletonList(myRepository), myRepository.getRoot())); + popupGroup.addAction(new HgNewBranchAction(myProject, Collections.singletonList(myRepository), myRepository)); + popupGroup.addAction(new HgNewBookmarkAction(myProject, Collections.singletonList(myRepository), myRepository)); popupGroup.addAction(new HgShowUnnamedHeadsForCurrentBranchAction(myProject, myRepository)); if (toInsert != null) { popupGroup.addAll(toInsert); @@ -92,24 +92,24 @@ public class HgBranchPopupActions { } private static class HgNewBranchAction extends NewBranchAction { - @NotNull final VirtualFile myPreselectedRepo; + @NotNull final HgRepository myPreselectedRepo; - HgNewBranchAction(@NotNull Project project, @NotNull List repositories, @NotNull VirtualFile preselectedRepo) { + HgNewBranchAction(@NotNull Project project, @NotNull List repositories, @NotNull HgRepository preselectedRepo) { super(project, repositories); myPreselectedRepo = preselectedRepo; } @Override public void actionPerformed(AnActionEvent e) { - final String name = getNewBranchNameFromUser(myProject, "Create New Branch"); + final String name = getNewBranchNameFromUser(myPreselectedRepo, "Create New Branch"); if (name == null) { return; } try { - new HgBranchCreateCommand(myProject, myPreselectedRepo, name).execute(new HgCommandResultHandler() { + new HgBranchCreateCommand(myProject, myPreselectedRepo.getRoot(), name).execute(new HgCommandResultHandler() { @Override public void process(@Nullable HgCommandResult result) { - getRepositoryManager(myProject).updateRepository(myPreselectedRepo); + myPreselectedRepo.update(); if (HgErrorUtil.hasErrorsInCommandExecution(result)) { new HgCommandResultNotifier(myProject) .notifyError(result, "Creation failed", "Branch creation [" + name + "] failed"); @@ -124,11 +124,11 @@ public class HgBranchPopupActions { } private static class HgNewBookmarkAction extends DumbAwareAction { - protected final List myRepositories; - protected Project myProject; - @NotNull final VirtualFile myPreselectedRepo; + @NotNull protected final List myRepositories; + @NotNull protected Project myProject; + @NotNull final HgRepository myPreselectedRepo; - HgNewBookmarkAction(@NotNull Project project, @NotNull List repositories, @NotNull VirtualFile preselectedRepo) { + HgNewBookmarkAction(@NotNull Project project, @NotNull List repositories, @NotNull HgRepository preselectedRepo) { super("New Bookmark", "Create new bookmark", null); myProject = project; myRepositories = repositories; @@ -146,12 +146,12 @@ public class HgBranchPopupActions { @Override public void actionPerformed(AnActionEvent e) { - final HgBookmarkDialog bookmarkDialog = new HgBookmarkDialog(myProject); + final HgBookmarkDialog bookmarkDialog = new HgBookmarkDialog(myPreselectedRepo); bookmarkDialog.show(); if (bookmarkDialog.isOK()) { try { final String name = bookmarkDialog.getName(); - new HgBookmarkCommand(myProject, myPreselectedRepo, name).createBookmark(bookmarkDialog.isActive()); + new HgBookmarkCommand(myProject, myPreselectedRepo.getRoot(), name).createBookmark(bookmarkDialog.isActive()); } catch (HgCommandException exception) { HgAbstractGlobalAction.handleException(myProject, exception); diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java index 84b070ea35c6..9554e9c59165 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java @@ -38,6 +38,8 @@ import org.zmlx.hg4idea.util.HgUtil; import java.text.SimpleDateFormat; import java.util.*; +import static org.zmlx.hg4idea.util.HgUtil.TIP_REFERENCE; + public class HgLogProvider implements VcsLogProvider { private static final Logger LOG = Logger.getInstance(HgLogProvider.class); @@ -47,8 +49,6 @@ public class HgLogProvider implements VcsLogProvider { @NotNull private final VcsLogRefManager myRefSorter; @NotNull private final VcsLogObjectsFactory myVcsObjectsFactory; - private static final String RECENT_HEAD = "tip"; - public HgLogProvider(@NotNull Project project, @NotNull HgRepositoryManager repositoryManager, @NotNull VcsLogObjectsFactory factory) { myProject = project; myRepositoryManager = repositoryManager; @@ -119,7 +119,7 @@ public class HgLogProvider implements VcsLogProvider { } String currentRevision = repository.getCurrentRevision(); if (currentRevision != null) { // null => fresh repository - refs.add(myVcsObjectsFactory.createRef(myVcsObjectsFactory.createHash(currentRevision), RECENT_HEAD, HgRefManager.HEAD, root)); + refs.add(myVcsObjectsFactory.createRef(myVcsObjectsFactory.createHash(currentRevision), TIP_REFERENCE, HgRefManager.HEAD, root)); } for (HgNameWithHashInfo tagInfo : tags) { refs.add(myVcsObjectsFactory.createRef(tagInfo.getHash(), tagInfo.getName(), HgRefManager.TAG, root)); @@ -172,7 +172,7 @@ public class HgLogProvider implements VcsLogProvider { boolean atLeastOneBranchExists = false; for (String branchName : filterCollection.getBranchFilter().getBranchNames()) { - if (branchName.equals(RECENT_HEAD) || branchExists(repository, branchName)) { + if (branchName.equals(TIP_REFERENCE) || branchExists(repository, branchName)) { filterParameters.add(prepareParameter("branch", branchName)); atLeastOneBranchExists = true; } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgBookmarkDialog.java b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgBookmarkDialog.java index 6c471f1af4fd..8addb557d301 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgBookmarkDialog.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgBookmarkDialog.java @@ -1,9 +1,7 @@ package org.zmlx.hg4idea.ui; -import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.ui.ValidationInfo; -import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.DocumentAdapter; import com.intellij.ui.components.JBCheckBox; import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBTextField; @@ -11,8 +9,11 @@ import com.intellij.util.ui.GridBag; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.zmlx.hg4idea.repo.HgRepository; +import org.zmlx.hg4idea.util.HgReferenceValidator; import javax.swing.*; +import javax.swing.event.DocumentEvent; import java.awt.*; import static com.intellij.util.ui.UIUtil.DEFAULT_HGAP; @@ -22,11 +23,13 @@ import static com.intellij.util.ui.UIUtil.DEFAULT_VGAP; * @author Nadya Zabrodina */ public class HgBookmarkDialog extends DialogWrapper { + @NotNull private HgRepository myRepository; @NotNull private JBTextField myBookmarkName; @NotNull private JBCheckBox myActiveCheckbox; - public HgBookmarkDialog(@Nullable Project project) { - super(project, false); + public HgBookmarkDialog(@NotNull HgRepository repository) { + super(repository.getProject(), false); + myRepository = repository; setTitle("Create Bookmark"); setResizable(false); init(); @@ -62,6 +65,12 @@ public class HgBookmarkDialog extends DialogWrapper { JLabel icon = new JLabel(UIUtil.getQuestionIcon(), SwingConstants.LEFT); myBookmarkName = new JBTextField(13); + myBookmarkName.getDocument().addDocumentListener(new DocumentAdapter() { + @Override + public void textChanged(DocumentEvent e) { + validateFields(); + } + }); JBLabel bookmarkLabel = new JBLabel("Bookmark name:"); bookmarkLabel.setLabelFor(myBookmarkName); @@ -75,6 +84,19 @@ public class HgBookmarkDialog extends DialogWrapper { return contentPanel; } + private void validateFields() { + HgReferenceValidator validator = HgReferenceValidator.newInstance(myRepository); + String name = getName(); + if (!validator.checkInput(name)) { + String message = validator.getErrorText(name); + setErrorText(message == null ? "You have to specify bookmark name." : message); + setOKActionEnabled(false); + return; + } + setErrorText(null); + setOKActionEnabled(true); + } + public boolean isActive() { return !myActiveCheckbox.isSelected(); } @@ -83,14 +105,4 @@ public class HgBookmarkDialog extends DialogWrapper { public String getName() { return myBookmarkName.getText(); } - - @Override - @Nullable - protected ValidationInfo doValidate() { - String message = "You have to specify bookmark name."; - if (StringUtil.isEmptyOrSpaces(getName())) { - return new ValidationInfo(message, myBookmarkName); - } - return null; - } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgCommonDialogWithChoices.java b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgCommonDialogWithChoices.java index 4c7ff6214e94..2c28d4c500ed 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgCommonDialogWithChoices.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgCommonDialogWithChoices.java @@ -31,6 +31,8 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Collection; +import static org.zmlx.hg4idea.util.HgUtil.TIP_REFERENCE; + public class HgCommonDialogWithChoices extends DialogWrapper { @@ -119,7 +121,8 @@ public class HgCommonDialogWithChoices extends DialogWrapper { HgRepository repo = hgRepositorySelectorComponent.getRepository(); branchSelector.setModel(new DefaultComboBoxModel(repo.getOpenedBranches().toArray())); DefaultComboBoxModel tagComboBoxModel = new DefaultComboBoxModel(HgUtil.getNamesWithoutHashes(repo.getTags()).toArray()); - tagComboBoxModel.addElement("tip"); //HgRepository does not store 'tip' tag because it is internal and not included in tags file + tagComboBoxModel + .addElement(TIP_REFERENCE); //HgRepository does not store 'tip' tag because it is internal and not included in tags file tagSelector.setModel(tagComboBoxModel); bookmarkSelector.setModel(new DefaultComboBoxModel(HgUtil.getNamesWithoutHashes(repo.getBookmarks()).toArray())); update(); diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgTagDialog.java b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgTagDialog.java index df4bb6101829..3a986e519267 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgTagDialog.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgTagDialog.java @@ -14,11 +14,12 @@ package org.zmlx.hg4idea.ui; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.DocumentAdapter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.zmlx.hg4idea.repo.HgRepository; +import org.zmlx.hg4idea.util.HgReferenceValidator; import javax.swing.*; import javax.swing.event.DocumentEvent; @@ -34,17 +35,10 @@ public class HgTagDialog extends DialogWrapper { public HgTagDialog(@NotNull Project project, @NotNull Collection repos, @Nullable HgRepository selectedRepo) { super(project, false); hgRepositorySelectorComponent.setTitle("Select repository to tag"); - DocumentListener documentListener = new DocumentListener() { - public void insertUpdate(DocumentEvent e) { - update(); - } - - public void removeUpdate(DocumentEvent e) { - update(); - } - - public void changedUpdate(DocumentEvent e) { - update(); + DocumentListener documentListener = new DocumentAdapter() { + @Override + protected void textChanged(DocumentEvent e) { + validateFields(); } }; @@ -67,19 +61,23 @@ public class HgTagDialog extends DialogWrapper { private void setRoots(@NotNull Collection repos, @Nullable HgRepository selectedRepo) { hgRepositorySelectorComponent.setRoots(repos); hgRepositorySelectorComponent.setSelectedRoot(selectedRepo); - update(); } protected JComponent createCenterPanel() { return contentPanel; } - private void update() { - setOKActionEnabled(validateOptions()); - } - - private boolean validateOptions() { - return !StringUtil.isEmptyOrSpaces(tagTxt.getText()); + private void validateFields() { + HgReferenceValidator validator = HgReferenceValidator.newInstance(hgRepositorySelectorComponent.getRepository()); + String name = getTagName(); + if (!validator.checkInput(name)) { + String message = validator.getErrorText(name); + setErrorText(message == null ? "You have to specify tag name." : message); + setOKActionEnabled(false); + return; + } + setErrorText(null); + setOKActionEnabled(true); } @Override diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgReferenceValidator.java b/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgReferenceValidator.java new file mode 100644 index 000000000000..27dadb5a05ae --- /dev/null +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgReferenceValidator.java @@ -0,0 +1,82 @@ +/* + * Copyright 2000-2014 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.zmlx.hg4idea.util; + +import com.intellij.openapi.ui.InputValidatorEx; +import com.intellij.openapi.util.text.StringUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.zmlx.hg4idea.repo.HgRepository; + +import java.util.Collection; +import java.util.regex.Pattern; + +import static org.zmlx.hg4idea.util.HgUtil.TIP_REFERENCE; + +public class HgReferenceValidator implements InputValidatorEx { + + private final HgRepository myRepository; + private String myErrorText; + + private static final Pattern ILLEGAL = Pattern.compile( + ".*:.*|" + // colon character is not allowed + "[0-9]*" // reference name couldn't contain only digits + ); + + private HgReferenceValidator(@NotNull HgRepository repository) { + myRepository = repository; + } + + public static HgReferenceValidator newInstance(@NotNull HgRepository repository) { + return new HgReferenceValidator(repository); + } + + @Override + public boolean checkInput(@Nullable String name) { + if (StringUtil.isEmptyOrSpaces(name)) { + return false; + } + if (ILLEGAL.matcher(name).matches()) { + myErrorText = "Invalid name for branch/tag"; + return false; + } + return !isReservedWord(name) && !hasConflictsWithAnotherBranch(name); + } + + @Override + public boolean canClose(@Nullable String name) { + return checkInput(name); + } + + boolean isReservedWord(@Nullable String name) { + myErrorText = TIP_REFERENCE.equals(name) ? String.format("The name \'%s\' is reserved.", name) : null; + return myErrorText != null; + } + + boolean hasConflictsWithAnotherBranch(@Nullable String name) { + Collection branches = myRepository.getBranches().keySet(); + String currentBranch = myRepository.getCurrentBranch(); // branches set doesn't contain uncommitted branch -> need an addition check + myErrorText = currentBranch.equals(name) || branches.contains(name) + ? String.format("A branch with the \'%s\' name already exists", name) : null; + return myErrorText != null; + } + + @Nullable + @Override + public String getErrorText(@Nullable String inputString) { + return myErrorText; + } +} diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgUtil.java b/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgUtil.java index ad1ad6197ed7..d0c52d774d8b 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgUtil.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgUtil.java @@ -72,6 +72,7 @@ public abstract class HgUtil { public static final int MANY_FILES = 100; private static final Logger LOG = Logger.getInstance(HgUtil.class); public static final String DOT_HG = ".hg"; + public static final String TIP_REFERENCE = "tip"; public static File copyResourceToTempFile(String basename, String extension) throws IOException { final InputStream in = HgUtil.class.getClassLoader().getResourceAsStream("python/" + basename + extension); @@ -323,14 +324,17 @@ public abstract class HgUtil { } /** - * Shows a message dialog to enter the name of new branch. - * @return name of new branch or {@code null} if user has cancelled the dialog. - */ - @Nullable - public static String getNewBranchNameFromUser(@NotNull Project project, - @NotNull String dialogTitle) { - return Messages.showInputDialog(project, "Enter the name of new branch:", dialogTitle, Messages.getQuestionIcon()); - } + * Shows a message dialog to enter the name of new branch. + * + * @return name of new branch or {@code null} if user has cancelled the dialog. + */ + @Nullable + public static String getNewBranchNameFromUser(@NotNull HgRepository repository, + @NotNull String dialogTitle) { + return Messages.showInputDialog(repository.getProject(), "Enter the name of new branch:", dialogTitle, Messages.getQuestionIcon(), "", + HgReferenceValidator.newInstance(repository)); + } + /** * Checks is a merge operation is in progress on the given repository. * Actually gets the number of parents of the current revision. If there are 2 parents, then a merge is going on. Otherwise there is diff --git a/plugins/hg4idea/testSrc/hg4idea/test/validator/HgReferenceValidatorTest.java b/plugins/hg4idea/testSrc/hg4idea/test/validator/HgReferenceValidatorTest.java new file mode 100644 index 000000000000..3bec11a46585 --- /dev/null +++ b/plugins/hg4idea/testSrc/hg4idea/test/validator/HgReferenceValidatorTest.java @@ -0,0 +1,116 @@ +/* + * Copyright 2000-2014 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 hg4idea.test.validator; + +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ui.UIUtil; +import hg4idea.test.HgPlatformTest; +import hg4idea.test.HgTestUtil; +import org.jetbrains.annotations.NotNull; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.zmlx.hg4idea.repo.HgRepository; +import org.zmlx.hg4idea.util.HgReferenceValidator; +import org.zmlx.hg4idea.util.HgUtil; + +import java.util.Collection; + +import static com.intellij.openapi.vcs.Executor.cd; +import static com.intellij.openapi.vcs.Executor.echo; +import static hg4idea.test.HgExecutor.hg; + +@RunWith(Parameterized.class) +public class HgReferenceValidatorTest extends HgPlatformTest { + + private HgReferenceValidator myValidator; + private static final String BRANCH_NAME = "Abranch"; + private static final String UNCOMMITTED_BRANCH = "uncommitted new branch"; + + @NotNull private final String myBranchName; + private final boolean myExpected; + + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + HgTestUtil.updateDirectoryMappings(myProject, myRepository); + HgRepository hgRepository = HgUtil.getRepositoryManager(myProject).getRepositoryForRoot(myRepository); + assertNotNull(hgRepository); + myValidator = HgReferenceValidator.newInstance(hgRepository); + cd(myRepository); + hg("branch '" + BRANCH_NAME + "'"); + String firstFile = "file.txt"; + echo(firstFile, BRANCH_NAME); + hg("commit -m 'createdBranch " + BRANCH_NAME + "' "); + hg("branch '" + UNCOMMITTED_BRANCH + "'"); + hgRepository.update(); + } + + @Override + @After + public void tearDown() throws Exception { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + HgReferenceValidatorTest.super.tearDown(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + @SuppressWarnings({"JUnitTestCaseWithNonTrivialConstructors", "UnusedParameters"}) + public HgReferenceValidatorTest(@NotNull String name, @NotNull String branchName, boolean expected) { + myBranchName = branchName; + myExpected = expected; + } + + + @Parameterized.Parameters(name = "{0}") + public static Collection createData() { + return ContainerUtil.newArrayList(new Object[][]{ + {"WORD", "branch", true}, + {"UNDERSCORED_WORD", "new_branch", true}, + {"HIERARCHY", "user/branch", true}, + {"HIERARCHY_2", "user/branch/sub_branch", true}, + {"BEGINS_WITH_SLASH", "/branch", true}, + {"WITH_DOTS", "complex.branch.name", true}, + {"WITH_WHITESPACES", "branch with whitespaces", true}, + {"WITH_SPECIAL_CHARS", "bra~nch-^%$", true}, + {"NOT_RESERVED", "TIP", true}, + {"CONTAINS_COLON", "bra:nch", false}, + {"ONLY_DIGITS", "876876", false}, + {"START_WITH_COLON", ":branch", false}, + {"ENDS_WITH_COLON", "branch:", false}, + {"RESERVED_WORD", "tip", false}, + {"BRANCH_CONFLICT", BRANCH_NAME, false}, + {"UNCOMMITTED_BRANCH_CONFLICT", UNCOMMITTED_BRANCH, false}, + }); + } + + @Test + public void testValid() { + assertEquals(" Wrong validation for " + myBranchName, myExpected, myValidator.checkInput(myBranchName)); + assertEquals(" Should be valid " + myBranchName, myExpected, myValidator.canClose(myBranchName)); + } +} diff --git a/python/resources/pycharm_core_logo.png b/python/resources/pycharm_core_logo.png index 4fc68fae7b26..aa19d19984c6 100644 Binary files a/python/resources/pycharm_core_logo.png and b/python/resources/pycharm_core_logo.png differ diff --git a/python/resources/pycharm_core_logo@2x.png b/python/resources/pycharm_core_logo@2x.png index 9dfe7d323ce6..97912f54bd9b 100644 Binary files a/python/resources/pycharm_core_logo@2x.png and b/python/resources/pycharm_core_logo@2x.png differ