diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java index 9dced73577b8..f82c948ee070 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java @@ -477,6 +477,7 @@ public class NullableStuffInspectionBase extends BaseJavaBatchLocalInspectionToo private boolean isNonAnnotatedOverridingNotNull(PsiMethod method, PsiMethod superMethod) { return REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL && !(method.getReturnType() instanceof PsiPrimitiveType) && + !method.isConstructor() && !getNullityManager(method).hasNullability(method) && isNotNullNotInferred(superMethod, true, IGNORE_EXTERNAL_SUPER_NOTNULL); } diff --git a/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java b/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java index 85fd8b31e184..1fea6cf90851 100644 --- a/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java @@ -985,8 +985,8 @@ public class ExpectedTypesProvider { PsiSubstitutor substitutor; if (candidateInfo instanceof MethodCandidateInfo) { final MethodCandidateInfo info = (MethodCandidateInfo)candidateInfo; - substitutor = MethodCandidateInfo.ourOverloadGuard - .doPreventingRecursion(argumentList, false, () -> info.inferTypeArguments(policy, args, true)); + substitutor = MethodCandidateInfo.ourOverloadGuard.doPreventingRecursion(argumentList, false, + () -> info.inferSubstitutorFromArgs(policy, args)); if (!info.isStaticsScopeCorrect() && !method.hasModifierProperty(PsiModifier.STATIC)) continue; } else { @@ -999,8 +999,7 @@ public class ExpectedTypesProvider { if (leftArgs != null && candidateInfo instanceof MethodCandidateInfo) { substitutor = MethodCandidateInfo.ourOverloadGuard.doPreventingRecursion(argumentList, false, - () -> ((MethodCandidateInfo)candidateInfo) - .inferTypeArguments(policy, leftArgs, true)); + () -> ((MethodCandidateInfo)candidateInfo).inferSubstitutorFromArgs(policy, leftArgs)); if (substitutor != null) { inferMethodCallArgumentTypes(argument, forCompletion, leftArgs, index, method, substitutor, array); } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java index e1c23442c8d7..0f3714a768a1 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java @@ -806,7 +806,8 @@ public class JavaCompletionUtil { PsiElement composite = leaf == null ? null : leaf.getParent(); if (composite instanceof PsiMethodReferenceExpression && LambdaHighlightingUtil.insertSemicolon(composite.getParent())) { insertAdditionalSemicolon = false; - } else if (composite instanceof PsiReferenceExpression) { + } + else if (composite instanceof PsiReferenceExpression) { PsiElement parent = composite.getParent(); if (parent instanceof PsiMethodCallExpression) { parent = parent.getParent(); @@ -814,6 +815,9 @@ public class JavaCompletionUtil { if (parent instanceof PsiLambdaExpression && !LambdaHighlightingUtil.insertSemicolonAfter((PsiLambdaExpression)parent)) { insertAdditionalSemicolon = false; } + if (parent instanceof PsiMethodReferenceExpression && LambdaHighlightingUtil.insertSemicolon(parent.getParent())) { + insertAdditionalSemicolon = false; + } } if (insertAdditionalSemicolon) { toInsert = TailType.SEMICOLON; diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java index 66d7649bc0b7..bbc61536e296 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java @@ -159,7 +159,7 @@ public class JavaInheritorsGetter extends CompletionProvider()", parameters.getOriginalFile()); + .createStatementFromText(canonicalText + " v = new " + erasedText + "<>()", parameters.getPosition()); final PsiVariable declaredVar = (PsiVariable)((PsiDeclarationStatement)statement).getDeclaredElements()[0]; final PsiNewExpression initializer = (PsiNewExpression)declaredVar.getInitializer(); final boolean hasDefaultConstructorOrNoGenericsOne = PsiDiamondTypeImpl.hasDefaultConstructor(psiClass) || diff --git a/java/java-impl/src/com/intellij/codeInspection/miscGenerics/RedundantArrayForVarargsCallInspection.java b/java/java-impl/src/com/intellij/codeInspection/miscGenerics/RedundantArrayForVarargsCallInspection.java index d88b4e59e407..07c9b046bada 100644 --- a/java/java-impl/src/com/intellij/codeInspection/miscGenerics/RedundantArrayForVarargsCallInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/miscGenerics/RedundantArrayForVarargsCallInspection.java @@ -102,8 +102,11 @@ public class RedundantArrayForVarargsCallInspection extends GenericsInspectionTo } PsiExpression lastArg = args[args.length - 1]; PsiParameter lastParameter = parameters[args.length - 1]; + if (!lastParameter.isVarArgs()) { + return; + } PsiType lastParamType = lastParameter.getType(); - LOG.assertTrue(lastParamType instanceof PsiEllipsisType); + LOG.assertTrue(lastParamType instanceof PsiEllipsisType, lastParamType); if (!(lastArg instanceof PsiNewExpression)) { return; } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java b/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java index 9cbb79d2cf0d..070cbc98a541 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java @@ -32,6 +32,8 @@ import com.intellij.codeInsight.navigation.NavigationUtil; import com.intellij.ide.util.PsiClassListCellRenderer; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.TransactionGuard; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; @@ -477,9 +479,23 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { Util.analyzeExpression(myExpr, new ArrayList<>(), classMemberRefs, new ArrayList<>()); } + showDialog(method, methodToSearchFor, occurences, replaceAllOccurrences, delegate, initializerType, mustBeFinal, + classMemberRefs, createNameSuggestionGenerator(myExpr, propName, myProject, enteredName)); + } + } + + private void showDialog(PsiMethod method, + PsiMethod methodToSearchFor, + PsiExpression[] occurences, + boolean replaceAllOccurrences, + boolean delegate, + PsiType initializerType, + boolean mustBeFinal, + List classMemberRefs, NameSuggestionsGenerator nameSuggestionGenerator) { + TransactionGuard.getInstance().submitTransactionAndWait(() -> { final IntroduceParameterDialog dialog = new IntroduceParameterDialog(myProject, classMemberRefs, occurences, myLocalVar, myExpr, - createNameSuggestionGenerator(myExpr, propName, myProject, enteredName), + nameSuggestionGenerator, createTypeSelectorManager(occurences, initializerType), methodToSearchFor, method, getParamsToRemove(method, occurences), mustBeFinal); dialog.setReplaceAllOccurrences(replaceAllOccurrences); dialog.setGenerateDelegate(delegate); @@ -489,9 +505,9 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { myEditor.getSelectionModel().removeSelection(); } }; - SwingUtilities.invokeLater(cleanSelectionRunnable); + ApplicationManager.getApplication().invokeLater(cleanSelectionRunnable, ModalityState.any()); } - } + }); } private TypeSelectorManagerImpl createTypeSelectorManager(PsiExpression[] occurences, PsiType initializerType) { diff --git a/java/java-impl/src/com/intellij/refactoring/typeMigration/rules/RootTypeConversionRule.java b/java/java-impl/src/com/intellij/refactoring/typeMigration/rules/RootTypeConversionRule.java index 7c4551fe8466..75f4c8f2092a 100644 --- a/java/java-impl/src/com/intellij/refactoring/typeMigration/rules/RootTypeConversionRule.java +++ b/java/java-impl/src/com/intellij/refactoring/typeMigration/rules/RootTypeConversionRule.java @@ -118,8 +118,7 @@ public class RootTypeConversionRule extends TypeConversionRule { } final PsiType migrationType = methodTypeParamsSubstitutor.substitute(type); - if (!originalType.equals(migrationType) && - !TypeConversionUtil.areTypesAssignmentCompatible(migrationType, actualParams[i])) { + if (!originalType.equals(migrationType) && !areParametersAssignable(migrationType, i, actualParams)) { labeler.migrateExpressionType(actualParams[i], migrationType, context, false, true); } } @@ -139,6 +138,19 @@ public class RootTypeConversionRule extends TypeConversionRule { return null; } + private static boolean areParametersAssignable(PsiType migrationType, int paramId, PsiExpression[] actualParams) { + if (migrationType instanceof PsiEllipsisType) { + for (int i = paramId; i < actualParams.length; i++) { + if (!TypeConversionUtil.areTypesAssignmentCompatible(migrationType, actualParams[i])) { + return false; + } + } + return true; + } else { + return TypeConversionUtil.areTypesAssignmentCompatible(migrationType, actualParams[paramId]); + } + } + private static class MyStaticMethodConversionDescriptor extends TypeConversionDescriptorBase { private final @NotNull String myTargetClassQName; diff --git a/java/java-psi-api/src/com/intellij/codeInsight/daemon/impl/analysis/JavaGenericsUtil.java b/java/java-psi-api/src/com/intellij/codeInsight/daemon/impl/analysis/JavaGenericsUtil.java index 86ca9fb3d218..ef8845ae0a57 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/daemon/impl/analysis/JavaGenericsUtil.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/daemon/impl/analysis/JavaGenericsUtil.java @@ -100,14 +100,21 @@ public class JavaGenericsUtil { } PsiMethod psiMethod = (PsiMethod)resolve; - if (!psiMethod.isVarArgs()) { + PsiParameter[] parameters = psiMethod.getParameterList().getParameters(); + + int parametersCount = parameters.length; + if (parametersCount == 0) { return false; } + PsiParameter varargParameter = parameters[parametersCount - 1]; + if (!varargParameter.isVarArgs()) { + return false; + } + if (AnnotationUtil.isAnnotated(psiMethod, "java.lang.SafeVarargs", false, false)) { return false; } - int parametersCount = psiMethod.getParameterList().getParametersCount(); - PsiParameter varargParameter = psiMethod.getParameterList().getParameters()[parametersCount - 1]; + PsiType componentType = ((PsiEllipsisType)varargParameter.getType()).getComponentType(); if (isReifiableType(resolveResult.getSubstitutor().substitute(componentType))) { return false; diff --git a/java/java-tests/testData/codeInsight/completion/smartType/DiamondCollapsedWithMethodTypeParameter-out.java b/java/java-tests/testData/codeInsight/completion/smartType/DiamondCollapsedWithMethodTypeParameter-out.java new file mode 100644 index 000000000000..c7a1ec9bed6b --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/DiamondCollapsedWithMethodTypeParameter-out.java @@ -0,0 +1,8 @@ +import java.util.ArrayList; + +class Test { + + ArrayList foo () { + return new ArrayList<>(); + } +} diff --git a/java/java-tests/testData/codeInsight/completion/smartType/DiamondCollapsedWithMethodTypeParameter.java b/java/java-tests/testData/codeInsight/completion/smartType/DiamondCollapsedWithMethodTypeParameter.java new file mode 100644 index 000000000000..29c836a8d914 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/DiamondCollapsedWithMethodTypeParameter.java @@ -0,0 +1,8 @@ +import java.util.ArrayList; + +class Test { + + ArrayList foo () { + return new Arr + } +} diff --git a/java/java-tests/testData/codeInsight/completion/smartType/StaticMethodReferenceInContextWithTypeArgs-out.java b/java/java-tests/testData/codeInsight/completion/smartType/StaticMethodReferenceInContextWithTypeArgs-out.java new file mode 100644 index 000000000000..9067d7052499 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/StaticMethodReferenceInContextWithTypeArgs-out.java @@ -0,0 +1,12 @@ + +import java.util.function.Consumer; + +class Test { + private static void _f(Consumer c) {} + + private static void bar(final String s) {} + + public static void main(String[] argArr) { + Test._f(Test::bar); + } +} diff --git a/java/java-tests/testData/codeInsight/completion/smartType/StaticMethodReferenceInContextWithTypeArgs.java b/java/java-tests/testData/codeInsight/completion/smartType/StaticMethodReferenceInContextWithTypeArgs.java new file mode 100644 index 000000000000..067b08d7bcba --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/StaticMethodReferenceInContextWithTypeArgs.java @@ -0,0 +1,12 @@ + +import java.util.function.Consumer; + +class Test { + private static void _f(Consumer c) {} + + private static void bar(final String s) {} + + public static void main(String[] argArr) { + Test._f(b); + } +} diff --git a/java/java-tests/testData/inspection/nullableProblems/OverriddenViaMethodReference.java b/java/java-tests/testData/inspection/nullableProblems/OverriddenViaMethodReference.java index 5e97019c96b4..3e39e73adf34 100644 --- a/java/java-tests/testData/inspection/nullableProblems/OverriddenViaMethodReference.java +++ b/java/java-tests/testData/inspection/nullableProblems/OverriddenViaMethodReference.java @@ -18,6 +18,10 @@ class P2 { }); test(this::getNull); test(this::getPrimitive); + test(this::getNonAnnotated); + + test(WithImplicitConstructor::new); + test(WithExplicitConstructor::new); } @Nullable @@ -29,3 +33,6 @@ class P2 { private void test(final NonnullInterface function) { } } + +class WithImplicitConstructor {} +class WithExplicitConstructor { WithExplicitConstructor() {} } \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType17CompletionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType17CompletionTest.java index cee5987f8c5b..2336adced00a 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType17CompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType17CompletionTest.java @@ -42,6 +42,10 @@ public class SmartType17CompletionTest extends LightFixtureCompletionTestCase { doTest(); } + public void testDiamondCollapsedWithMethodTypeParameter() throws Exception { + doTest(); + } + public void testDiamondNotCollapsed() throws Exception { doTest(); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java index b890597c2e6f..ff29b8d4f58b 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java @@ -156,6 +156,10 @@ public class SmartType18CompletionTest extends LightFixtureCompletionTestCase { } public void testStaticMethodReference() { doTest(false); } + public void testStaticMethodReferenceInContextWithTypeArgs() { + configureByTestName(); + checkResultByFile("/" + getTestName(false) + "-out.java"); + } public void testOuterMethodReference() { doTest(true); } public void testNoAnonymousOuterMethodReference() { doAntiTest(); } diff --git a/java/typeMigration/test/com/intellij/refactoring/TypeMigrationTest.java b/java/typeMigration/test/com/intellij/refactoring/TypeMigrationTest.java index ad7548e1e444..9c8145e51faf 100644 --- a/java/typeMigration/test/com/intellij/refactoring/TypeMigrationTest.java +++ b/java/typeMigration/test/com/intellij/refactoring/TypeMigrationTest.java @@ -862,6 +862,10 @@ public class TypeMigrationTest extends TypeMigrationTestBase { doTestMethodType("migrationMethod", PsiType.VOID); } + public void testGenericEllipsis() { + doTestFieldType("migrationField", myJavaFacade.getElementFactory().createTypeFromText("Test", null)); + } + private void doTestReturnType(final String methodName, final String migrationType) { start(new RulesProvider() { @Override diff --git a/java/typeMigration/testData/refactoring/typeMigration/genericEllipsis/after/Test.items b/java/typeMigration/testData/refactoring/typeMigration/genericEllipsis/after/Test.items new file mode 100644 index 000000000000..a00e0a47d582 --- /dev/null +++ b/java/typeMigration/testData/refactoring/typeMigration/genericEllipsis/after/Test.items @@ -0,0 +1,10 @@ +Types: +PsiField:migrationField : Test +PsiMethodCallExpression:migrationField.method() : void +PsiReferenceExpression:migrationField : Test + +Conversions: +migrationField.method() -> $ + +New expression type changes: +Fails: diff --git a/java/typeMigration/testData/refactoring/typeMigration/genericEllipsis/after/test.java b/java/typeMigration/testData/refactoring/typeMigration/genericEllipsis/after/test.java new file mode 100644 index 000000000000..f3f5ea781739 --- /dev/null +++ b/java/typeMigration/testData/refactoring/typeMigration/genericEllipsis/after/test.java @@ -0,0 +1,11 @@ +class Test { + private static Test migrationField; + + private static void m() { + migrationField.method(); + } + + void method(X... xes) { + + } +} diff --git a/java/typeMigration/testData/refactoring/typeMigration/genericEllipsis/before/test.java b/java/typeMigration/testData/refactoring/typeMigration/genericEllipsis/before/test.java new file mode 100644 index 000000000000..001d53331836 --- /dev/null +++ b/java/typeMigration/testData/refactoring/typeMigration/genericEllipsis/before/test.java @@ -0,0 +1,11 @@ +class Test { + private static Test migrationField; + + private static void m() { + migrationField.method(); + } + + void method(X... xes) { + + } +} diff --git a/json/gen/com/intellij/json/_JsonLexer.java b/json/gen/com/intellij/json/_JsonLexer.java index bc148d9b3e6b..32bbee8bf0a5 100644 --- a/json/gen/com/intellij/json/_JsonLexer.java +++ b/json/gen/com/intellij/json/_JsonLexer.java @@ -119,28 +119,28 @@ public class _JsonLexer implements FlexLexer { /* The ZZ_CMAP_A table has 920 entries */ static final char ZZ_CMAP_A[] = zzUnpackCMap( "\11\20\1\3\1\2\2\1\1\2\6\20\4\0\1\3\1\20\1\7\1\0\1\20\2\0\1\11\2\20\1\6\1"+ - "\17\1\25\1\12\1\15\1\4\1\13\11\14\1\26\5\0\5\20\1\16\5\20\1\23\1\10\1\24\1"+ - "\0\1\20\1\0\1\34\3\20\1\32\1\33\5\20\1\35\1\20\1\37\3\20\1\30\1\36\1\27\1"+ - "\31\5\20\1\21\1\0\1\22\7\20\1\5\2\20\1\3\1\0\4\20\4\0\1\20\2\0\1\20\7\0\1"+ - "\20\4\0\1\20\5\0\7\20\1\0\2\20\4\0\4\20\16\0\5\20\7\0\1\20\1\0\1\20\1\0\5"+ - "\20\1\0\2\20\2\0\4\20\10\0\1\20\1\0\3\20\1\0\1\20\1\0\4\20\1\0\13\20\1\0\1"+ - "\20\2\0\6\20\1\0\7\20\1\0\1\20\15\0\1\20\1\0\2\20\1\0\2\20\1\0\4\20\10\0\1"+ - "\20\4\0\4\20\1\0\4\20\1\0\13\20\2\0\4\20\2\0\11\20\6\0\10\20\2\0\2\20\1\0"+ - "\3\20\1\0\4\20\2\0\6\20\1\0\1\20\3\0\4\20\2\0\5\20\2\0\4\20\5\0\2\20\1\0\4"+ - "\20\4\0\2\20\1\0\2\20\1\0\2\20\1\0\2\20\2\0\1\20\1\0\3\20\2\0\3\20\3\0\4\20"+ - "\1\0\1\20\7\0\3\20\1\0\2\20\1\0\5\20\1\0\3\20\2\0\1\20\11\0\2\20\1\0\6\20"+ - "\3\0\3\20\1\0\4\20\3\0\2\20\1\0\1\20\1\0\2\20\3\0\2\20\3\0\1\20\6\0\3\20\3"+ - "\0\3\20\5\0\2\20\2\0\2\20\5\0\1\20\1\0\5\20\1\0\4\20\1\0\1\20\4\0\1\20\4\0"+ - "\6\20\1\0\1\20\3\0\2\20\5\0\2\20\1\0\1\20\2\0\2\20\1\0\1\20\2\0\1\20\3\0\3"+ - "\20\1\0\1\20\1\0\1\20\5\0\1\20\1\0\1\20\1\0\1\20\4\0\5\20\1\0\4\20\1\3\10"+ - "\20\1\0\2\20\4\0\4\20\3\0\1\20\3\0\3\20\5\0\5\20\1\0\1\20\1\0\1\20\1\0\1\20"+ - "\1\0\1\20\2\0\3\20\1\0\2\20\13\3\5\20\2\1\5\20\1\3\4\0\1\20\12\0\1\3\1\0\1"+ - "\20\3\0\3\20\1\0\5\20\2\0\1\20\1\0\4\20\1\0\1\20\5\0\5\20\4\0\1\20\1\0\1\3"+ - "\4\0\3\20\1\0\2\20\2\0\3\20\2\0\5\20\2\0\6\20\1\0\3\20\1\0\2\20\2\0\2\20\1"+ - "\0\2\20\1\0\2\20\2\0\3\20\3\0\2\20\3\0\2\20\2\0\3\20\4\0\3\20\1\0\2\20\1\0"+ - "\2\20\3\0\1\20\2\0\5\20\1\0\2\20\1\0\3\20\2\0\1\20\4\0\1\20\2\0\2\20\2\0\4"+ - "\20\1\0\4\20\1\0\1\20\1\0\5\20\1\0\4\20\2\0\1\20\1\0\1\20\5\0\1\20\1\0\1\20"+ - "\1\0\3\20"); + "\17\1\25\1\12\1\15\1\4\1\13\11\14\1\26\1\0\3\20\1\0\5\20\1\16\5\20\1\23\1"+ + "\10\1\24\2\20\1\0\1\34\3\20\1\32\1\33\5\20\1\35\1\20\1\37\3\20\1\30\1\36\1"+ + "\27\1\31\5\20\1\21\1\0\1\22\7\20\1\5\2\20\1\3\1\0\4\20\4\0\1\20\2\0\1\20\7"+ + "\0\1\20\4\0\1\20\5\0\7\20\1\0\2\20\4\0\4\20\16\0\5\20\7\0\1\20\1\0\1\20\1"+ + "\0\5\20\1\0\2\20\2\0\4\20\10\0\1\20\1\0\3\20\1\0\1\20\1\0\4\20\1\0\13\20\1"+ + "\0\1\20\2\0\6\20\1\0\7\20\1\0\1\20\15\0\1\20\1\0\2\20\1\0\2\20\1\0\4\20\10"+ + "\0\1\20\4\0\4\20\1\0\4\20\1\0\13\20\2\0\4\20\2\0\11\20\6\0\10\20\2\0\2\20"+ + "\1\0\3\20\1\0\4\20\2\0\6\20\1\0\1\20\3\0\4\20\2\0\5\20\2\0\4\20\5\0\2\20\1"+ + "\0\4\20\4\0\2\20\1\0\2\20\1\0\2\20\1\0\2\20\2\0\1\20\1\0\3\20\2\0\3\20\3\0"+ + "\4\20\1\0\1\20\7\0\3\20\1\0\2\20\1\0\5\20\1\0\3\20\2\0\1\20\11\0\2\20\1\0"+ + "\6\20\3\0\3\20\1\0\4\20\3\0\2\20\1\0\1\20\1\0\2\20\3\0\2\20\3\0\1\20\6\0\3"+ + "\20\3\0\3\20\5\0\2\20\2\0\2\20\5\0\1\20\1\0\5\20\1\0\4\20\1\0\1\20\4\0\1\20"+ + "\4\0\6\20\1\0\1\20\3\0\2\20\5\0\2\20\1\0\1\20\2\0\2\20\1\0\1\20\2\0\1\20\3"+ + "\0\3\20\1\0\1\20\1\0\1\20\5\0\1\20\1\0\1\20\1\0\1\20\4\0\5\20\1\0\4\20\1\3"+ + "\10\20\1\0\2\20\4\0\4\20\3\0\1\20\3\0\3\20\5\0\5\20\1\0\1\20\1\0\1\20\1\0"+ + "\1\20\1\0\1\20\2\0\3\20\1\0\2\20\13\3\5\20\2\1\5\20\1\3\4\0\1\20\12\0\1\3"+ + "\1\0\1\20\3\0\3\20\1\0\5\20\2\0\1\20\1\0\4\20\1\0\1\20\5\0\5\20\4\0\1\20\1"+ + "\0\1\3\4\0\3\20\1\0\2\20\2\0\3\20\2\0\5\20\2\0\6\20\1\0\3\20\1\0\2\20\2\0"+ + "\2\20\1\0\2\20\1\0\2\20\2\0\3\20\3\0\2\20\3\0\2\20\2\0\3\20\4\0\3\20\1\0\2"+ + "\20\1\0\2\20\3\0\1\20\2\0\5\20\1\0\2\20\1\0\3\20\2\0\1\20\4\0\1\20\2\0\2\20"+ + "\2\0\4\20\1\0\4\20\1\0\1\20\1\0\5\20\1\0\4\20\2\0\1\20\1\0\1\20\5\0\1\20\1"+ + "\0\1\20\1\0\3\20"); /** * Translates DFA states to action switch labels. diff --git a/json/json.bnf b/json/json.bnf index 30aa0352d5ee..701015ec6aa5 100644 --- a/json/json.bnf +++ b/json/json.bnf @@ -43,9 +43,10 @@ // auto completion of keywords. Semantically, it represents "bad word" type // of tokens // Could be as loose as [^\s\[\]{}:,\"\']+, but is slightly more restricted - // for the time being to match most forms of npm package names in package.json. + // for the time being to match most forms of npm package names and semver versions + // in package.json. // See https://github.com/npm/validate-npm-package-name - IDENTIFIER="regexp:[[:jletterdigit:]~!()*\-./@]+" + IDENTIFIER="regexp:[[:jletterdigit:]~!()*\-./@\^<>=]+" ] extends("container|literal|reference_expression")=value diff --git a/json/src/com/intellij/json/_JsonLexer.flex b/json/src/com/intellij/json/_JsonLexer.flex index 3550a1299a21..fdf5fe1ce515 100644 --- a/json/src/com/intellij/json/_JsonLexer.flex +++ b/json/src/com/intellij/json/_JsonLexer.flex @@ -30,7 +30,7 @@ BLOCK_COMMENT="/"\*([^*]|\*+[^*/])*(\*+"/")? DOUBLE_QUOTED_STRING=\"([^\\\"\r\n]|\\[^\r\n])*\"? SINGLE_QUOTED_STRING='([^\\'\r\n]|\\[^\r\n])*'? NUMBER=-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]*)? -IDENTIFIER=[[:jletterdigit:]~!()*\-."/"@]+ +IDENTIFIER=[[:jletterdigit:]~!()*\-."/"@\^<>=]+ %% { diff --git a/json/tests/testData/highlighting/ComplianceProblems.json b/json/tests/testData/highlighting/ComplianceProblems.json index 0768332f0690..1928a83d365e 100644 --- a/json/tests/testData/highlighting/ComplianceProblems.json +++ b/json/tests/testData/highlighting/ComplianceProblems.json @@ -1,5 +1,9 @@ { /* Property key checks */ + "dependencies": { + @foo/bar: ^0.1, + foo.bar: =1.2 + }, "key1": null, 'key2': null, key3: null, @@ -10,7 +14,7 @@ "key5": 'foo', "key6": foo, "key7": [42,], - "key8": false, + "key8": false, } ["another", "top-level", "value"] null \ No newline at end of file diff --git a/json/tests/testData/psi/ExtendedIdentifierToken.json b/json/tests/testData/psi/ExtendedIdentifierToken.json index 9824b3b6ee31..99d10f78398c 100644 --- a/json/tests/testData/psi/ExtendedIdentifierToken.json +++ b/json/tests/testData/psi/ExtendedIdentifierToken.json @@ -1,5 +1,11 @@ { "devDependencies": { - @types/3d-bin.packing: 42 + @types/3d-bin.packing: 42, + "pkg1": ^4.15.0, + "pkg2": ~4.15.0, + "pkg3": >4.15.0, + "pkg4": >=4.15.0, + "pkg5": <4.15.0, + "pkg6": <=4.15.0 } } \ No newline at end of file diff --git a/json/tests/testData/psi/ExtendedIdentifierToken.txt b/json/tests/testData/psi/ExtendedIdentifierToken.txt index c09bc06a9335..222ce004efd9 100644 --- a/json/tests/testData/psi/ExtendedIdentifierToken.txt +++ b/json/tests/testData/psi/ExtendedIdentifierToken.txt @@ -17,6 +17,60 @@ JsonFile: ExtendedIdentifierToken.json PsiWhiteSpace(' ') JsonNumberLiteral PsiElement(NUMBER)('42') + PsiElement(,)(',') + PsiWhiteSpace('\n ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"pkg1"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonReferenceExpression + PsiElement(IDENTIFIER)('^4.15.0') + PsiElement(,)(',') + PsiWhiteSpace('\n ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"pkg2"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonReferenceExpression + PsiElement(IDENTIFIER)('~4.15.0') + PsiElement(,)(',') + PsiWhiteSpace('\n ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"pkg3"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonReferenceExpression + PsiElement(IDENTIFIER)('>4.15.0') + PsiElement(,)(',') + PsiWhiteSpace('\n ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"pkg4"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonReferenceExpression + PsiElement(IDENTIFIER)('>=4.15.0') + PsiElement(,)(',') + PsiWhiteSpace('\n ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"pkg5"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonReferenceExpression + PsiElement(IDENTIFIER)('<4.15.0') + PsiElement(,)(',') + PsiWhiteSpace('\n ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"pkg6"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonReferenceExpression + PsiElement(IDENTIFIER)('<=4.15.0') PsiWhiteSpace('\n ') PsiElement(})('}') PsiWhiteSpace('\n') diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModifiableModelsProvider.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModifiableModelsProvider.java index 218e2490c6c6..03c918416c70 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModifiableModelsProvider.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModifiableModelsProvider.java @@ -27,6 +27,7 @@ import com.intellij.packaging.artifacts.ModifiableArtifactModel; import com.intellij.packaging.elements.PackagingElementResolvingContext; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * @author Vladislav.Soroka @@ -71,4 +72,7 @@ public interface IdeModifiableModelsProvider extends IdeModelsProvider { void dispose(); void setTestModuleProperties(Module testModule, String productionModuleName); + + @Nullable + String getProductionModuleName(Module module); } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ImportSpec.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ImportSpec.java index 827529f98f60..fae24be6a924 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ImportSpec.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ImportSpec.java @@ -46,6 +46,8 @@ public interface ImportSpec { boolean isPreviewMode(); + boolean shouldCreateDirectoriesForEmptyContentRoots(); + boolean isReportRefreshError(); @Nullable diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ImportSpecBuilder.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ImportSpecBuilder.java index 59c10961571c..6b9ad4fd2c69 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ImportSpecBuilder.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ImportSpecBuilder.java @@ -43,6 +43,7 @@ public class ImportSpecBuilder { @Nullable private String myVmOptions; @Nullable private String myArguments; private boolean myUseDefaultCallback; + private boolean myCreateDirectoriesForEmptyContentRoots; public ImportSpecBuilder(@NotNull Project project, @NotNull ProjectSystemId id) { myProject = project; @@ -50,6 +51,11 @@ public class ImportSpecBuilder { myProgressExecutionMode = ProgressExecutionMode.IN_BACKGROUND_ASYNC; } + public ImportSpecBuilder(ImportSpec importSpec) { + this(importSpec.getProject(), importSpec.getExternalSystemId()); + apply(importSpec); + } + public ImportSpecBuilder whenAutoImportEnabled() { myWhenAutoImportEnabled = true; return this; @@ -79,6 +85,11 @@ public class ImportSpecBuilder { return this; } + public ImportSpecBuilder createDirectoriesForEmptyContentRoots() { + myCreateDirectoriesForEmptyContentRoots = true; + return this; + } + public ImportSpecBuilder dontReportRefreshErrors() { isReportRefreshError = false; return this; @@ -104,6 +115,7 @@ public class ImportSpecBuilder { mySpec.setWhenAutoImportEnabled(myWhenAutoImportEnabled); mySpec.setProgressExecutionMode(myProgressExecutionMode); mySpec.setForceWhenUptodate(myForceWhenUptodate); + mySpec.setCreateDirectoriesForEmptyContentRoots(myCreateDirectoriesForEmptyContentRoots); if (myUseDefaultCallback) { mySpec.setCallback(new ExternalProjectRefreshCallback() { @Override @@ -129,4 +141,16 @@ public class ImportSpecBuilder { mySpec.setVmOptions(myVmOptions); return mySpec; } + + private void apply(ImportSpec spec) { + myWhenAutoImportEnabled = spec.whenAutoImportEnabled(); + myProgressExecutionMode = spec.getProgressExecutionMode(); + myForceWhenUptodate = spec.isForceWhenUptodate(); + myCreateDirectoriesForEmptyContentRoots = spec.shouldCreateDirectoriesForEmptyContentRoots(); + myCallback = spec.getCallback(); + isPreviewMode = spec.isPreviewMode(); + isReportRefreshError = spec.isReportRefreshError(); + myArguments = spec.getArguments(); + myVmOptions = spec.getVmOptions(); + } } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ImportSpecImpl.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ImportSpecImpl.java index 040790de2919..0e48df5bab93 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ImportSpecImpl.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ImportSpecImpl.java @@ -34,6 +34,7 @@ public class ImportSpecImpl implements ImportSpec { private boolean whenAutoImportEnabled; @Nullable private ExternalProjectRefreshCallback myCallback; private boolean isPreviewMode; + private boolean createDirectoriesForEmptyContentRoots; private boolean isReportRefreshError; @Nullable private String myVmOptions; @Nullable private String myArguments; @@ -103,6 +104,15 @@ public class ImportSpecImpl implements ImportSpec { this.isPreviewMode = isPreviewMode; } + @Override + public boolean shouldCreateDirectoriesForEmptyContentRoots() { + return createDirectoriesForEmptyContentRoots; + } + + public void setCreateDirectoriesForEmptyContentRoots(boolean createDirectoriesForEmptyContentRoots) { + this.createDirectoriesForEmptyContentRoots = createDirectoriesForEmptyContentRoots; + } + @Override public boolean isReportRefreshError() { return isReportRefreshError; diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ExternalSystemStartupActivity.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ExternalSystemStartupActivity.java index 85b690fc9434..6024f5d75af1 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ExternalSystemStartupActivity.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ExternalSystemStartupActivity.java @@ -52,7 +52,8 @@ public class ExternalSystemStartupActivity implements StartupActivity { for (ExternalSystemManager manager : ExternalSystemManager.EP_NAME.getExtensions()) { final boolean isNewProject = project.getUserData(ExternalSystemDataKeys.NEWLY_CREATED_PROJECT) == Boolean.TRUE; if (isNewProject) { - ExternalSystemUtil.refreshProjects(new ImportSpecBuilder(project, manager.getSystemId())); + ExternalSystemUtil.refreshProjects(new ImportSpecBuilder(project, manager.getSystemId()) + .createDirectoriesForEmptyContentRoots()); } } } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/AbstractIdeModifiableModelsProvider.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/AbstractIdeModifiableModelsProvider.java index 6a98ae509460..11e0b0c999c8 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/AbstractIdeModifiableModelsProvider.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/AbstractIdeModifiableModelsProvider.java @@ -433,4 +433,10 @@ public abstract class AbstractIdeModifiableModelsProvider extends IdeModelsProvi public void setTestModuleProperties(Module testModule, String productionModuleName) { myProductionModulesForTestModules.put(testModule, productionModuleName); } + + @Nullable + @Override + public String getProductionModuleName(Module module) { + return myProductionModulesForTestModules.get(module); + } } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ContentRootDataService.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ContentRootDataService.java index bade1175200a..02335c24f99f 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ContentRootDataService.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ContentRootDataService.java @@ -15,11 +15,11 @@ */ package com.intellij.openapi.externalSystem.service.project.manage; +import com.intellij.ide.projectView.ProjectView; +import com.intellij.ide.projectView.impl.ProjectViewPane; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.externalSystem.model.DataNode; -import com.intellij.openapi.externalSystem.model.Key; -import com.intellij.openapi.externalSystem.model.ProjectKeys; -import com.intellij.openapi.externalSystem.model.ProjectSystemId; +import com.intellij.openapi.externalSystem.model.*; import com.intellij.openapi.externalSystem.model.project.ContentRootData; import com.intellij.openapi.externalSystem.model.project.ContentRootData.SourceRoot; import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType; @@ -30,6 +30,7 @@ import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettin import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings; import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; import com.intellij.openapi.externalSystem.util.ExternalSystemConstants; +import com.intellij.openapi.externalSystem.util.ExternalSystemUtil; import com.intellij.openapi.externalSystem.util.Order; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; @@ -64,6 +65,8 @@ import java.util.Set; */ @Order(ExternalSystemConstants.BUILTIN_SERVICE_ORDER) public class ContentRootDataService extends AbstractProjectDataService { + public static final com.intellij.openapi.util.Key CREATE_EMPTY_DIRECTORIES = + com.intellij.openapi.util.Key.create("createEmptyDirectories"); private static final Logger LOG = Logger.getInstance("#" + ContentRootDataService.class.getName()); @@ -82,6 +85,14 @@ public class ContentRootDataService extends AbstractProjectDataService projectDataNode = ExternalSystemApiUtil.findParent(toImport.iterator().next(), ProjectKeys.PROJECT); + if (projectDataNode != null) { + forceDirectoriesCreation = projectDataNode.getUserData(CREATE_EMPTY_DIRECTORIES) == Boolean.TRUE; + } + + Set modulesToExpand = ContainerUtil.newTroveSet(); MultiMap, DataNode> byModule = ExternalSystemApiUtil.groupBy(toImport, ModuleData.class); for (Map.Entry, Collection>> entry : byModule.entrySet()) { Module module = entry.getKey().getUserData(AbstractModuleDataService.MODULE_KEY); @@ -93,13 +104,34 @@ public class ContentRootDataService extends AbstractProjectDataService 0) { + VirtualFile virtualFile = roots[0]; + ExternalSystemUtil.invokeLater(project, ModalityState.NON_MODAL, () -> { + final ProjectView projectView = ProjectView.getInstance(project); + projectView.changeViewCB(ProjectViewPane.ID, null).doWhenProcessed(() -> projectView.selectCB(null, virtualFile, false)); + }); + } + } + } } } private static void importData(@NotNull IdeModifiableModelsProvider modelsProvider, @NotNull final Collection> data, - @NotNull final Module module) { + @NotNull final Module module, boolean forceDirectoriesCreation) { final ModifiableRootModel modifiableRootModel = modelsProvider.getModifiableRootModel(module); final ContentEntry[] contentEntries = modifiableRootModel.getContentEntries(); final Map contentEntriesMap = ContainerUtilRt.newHashMap(); @@ -107,8 +139,8 @@ public class ContentRootDataService extends AbstractProjectDataService map = ContainerUtil.newConcurrentMap(); + public final Map map = ContainerUtil.newConcurrentMap(); } static class ProjectState { @Property(surroundWithTag = false) @MapAnnotation(surroundWithTag = false, surroundValueWithTag = false, surroundKeyWithTag = false, keyAttributeName = "path", entryTagName = "dataType") - public Map map = ContainerUtil.newConcurrentMap(); + public final Map map = ContainerUtil.newConcurrentMap(); public boolean isInclusion; } static class ModuleState { @Property(surroundWithTag = false) @AbstractCollection(surroundWithTag = false, elementTag = "id") - public Set set = ContainerUtil.newConcurrentSet(); + public final Set set = ContainerUtil.newConcurrentSet(); public ModuleState() { } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/settings/AbstractExternalProjectSettingsControl.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/settings/AbstractExternalProjectSettingsControl.java index e71dddc52143..b87b035e3615 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/settings/AbstractExternalProjectSettingsControl.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/settings/AbstractExternalProjectSettingsControl.java @@ -101,7 +101,13 @@ public abstract class AbstractExternalProjectSettingsControl manager = ExternalSystemApiUtil.getManager(spec.getExternalSystemId()); @@ -273,8 +273,7 @@ public class ExternalSystemUtil { .clearNotifications(null, NotificationSource.PROJECT_SYNC, spec.getExternalSystemId()); for (String path : toRefresh) { - refreshProject( - spec.getProject(), spec.getExternalSystemId(), path, callback, false, spec.getProgressExecutionMode()); + refreshProject(path, new ImportSpecBuilder(spec).callback(callback).build()); } } } @@ -394,7 +393,9 @@ public class ExternalSystemUtil { ExternalSystemProcessingManager processingManager = ServiceManager.getService(ExternalSystemProcessingManager.class); if (processingManager.findTask(ExternalSystemTaskType.RESOLVE_PROJECT, externalSystemId, externalProjectPath) != null) { - callback.onFailure(ExternalSystemBundle.message("error.resolve.already.running", externalProjectPath), null); + if (callback != null) { + callback.onFailure(ExternalSystemBundle.message("error.resolve.already.running", externalProjectPath), null); + } return; } @@ -413,7 +414,13 @@ public class ExternalSystemUtil { final Throwable error = myTask.getError(); if (error == null) { - callback.onSuccess(myTask.getExternalProject()); + if (callback != null) { + DataNode externalProject = myTask.getExternalProject(); + if (externalProject != null && importSpec.shouldCreateDirectoriesForEmptyContentRoots()) { + externalProject.putUserData(ContentRootDataService.CREATE_EMPTY_DIRECTORIES, Boolean.TRUE); + } + callback.onSuccess(externalProject); + } if (!isPreviewMode) { externalSystemTaskActivator.runTasks(externalProjectPath, ExternalSystemTaskActivator.Phase.AFTER_SYNC); } @@ -430,7 +437,9 @@ public class ExternalSystemUtil { ); } - callback.onFailure(message, extractDetails(error)); + if (callback != null) { + callback.onFailure(message, extractDetails(error)); + } ExternalSystemManager manager = ExternalSystemApiUtil.getManager(externalSystemId); if (manager == null) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/actions/AbstractLayoutCodeProcessor.java b/platform/lang-impl/src/com/intellij/codeInsight/actions/AbstractLayoutCodeProcessor.java index cac49ae29f8e..f2e8685c8f91 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/actions/AbstractLayoutCodeProcessor.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/actions/AbstractLayoutCodeProcessor.java @@ -278,7 +278,7 @@ public abstract class AbstractLayoutCodeProcessor { return new FileTreeIterator(myProject); } - return new FileTreeIterator(Collections.emptyList()); + return new FileTreeIterator(Collections.emptyList()); } @NotNull @@ -355,7 +355,7 @@ public abstract class AbstractLayoutCodeProcessor { LOG.error(e); } }; - runLayoutCodeProcess(readAction, writeAction, false ); + runLayoutCodeProcess(readAction, writeAction); } private boolean checkFileWritable(final PsiFile file){ @@ -396,7 +396,7 @@ public abstract class AbstractLayoutCodeProcessor { return !GeneratedSourcesFilter.isGeneratedSourceByAnyFilter(virtualFile, file.getProject()); } - private void runLayoutCodeProcess(final Runnable readAction, final Runnable writeAction, final boolean globalAction) { + private void runLayoutCodeProcess(final Runnable readAction, final Runnable writeAction) { final ProgressWindow progressWindow = new ProgressWindow(true, myProject); progressWindow.setTitle(myCommandName); progressWindow.setText(myProgressText); @@ -418,7 +418,6 @@ public abstract class AbstractLayoutCodeProcessor { } final Runnable writeRunnable = () -> CommandProcessor.getInstance().executeCommand(myProject, () -> { - if (globalAction) CommandProcessor.getInstance().markCurrentCommandAsGlobal(myProject); try { writeAction.run(); @@ -544,18 +543,15 @@ public abstract class AbstractLayoutCodeProcessor { } private void performFileProcessing(@NotNull PsiFile file) { - myProcessors.stream().forEach((processor) -> { - Ref> writeTaskRef = Ref.create(); - - ApplicationManager.getApplication().runReadAction(() -> writeTaskRef.set(processor.prepareTask(file, myProcessChangedTextOnly))); + for (AbstractLayoutCodeProcessor processor : myProcessors) { + FutureTask writeTask = ReadAction.compute(() -> processor.prepareTask(file, myProcessChangedTextOnly)); ProgressIndicatorProvider.checkCanceled(); - FutureTask writeTask = writeTaskRef.get(); - + ApplicationManager.getApplication().invokeAndWait(() -> WriteCommandAction.runWriteCommandAction(myProject, myCommandName, null, writeTask)); checkStop(writeTask, file); - }); + } } private void checkStop(FutureTask task, PsiFile file) { @@ -565,6 +561,11 @@ public abstract class AbstractLayoutCodeProcessor { } } catch (InterruptedException | ExecutionException e) { + Throwable cause = e.getCause(); + if (cause instanceof IndexNotReadyException) { + LOG.warn(cause); + return; + } LOG.error("Got unexpected exception during formatting " + file, e); } } diff --git a/platform/lang-impl/src/com/intellij/execution/TerminateRemoteProcessDialog.java b/platform/lang-impl/src/com/intellij/execution/TerminateRemoteProcessDialog.java index 54b15f0b3f50..3ef7c714145f 100644 --- a/platform/lang-impl/src/com/intellij/execution/TerminateRemoteProcessDialog.java +++ b/platform/lang-impl/src/com/intellij/execution/TerminateRemoteProcessDialog.java @@ -25,13 +25,8 @@ import com.intellij.ide.GeneralSettings; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.wm.WindowManager; import com.intellij.util.ArrayUtil; -import com.intellij.util.ui.UIUtil; -import org.jetbrains.annotations.NotNull; -import javax.swing.*; -import java.awt.*; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -75,12 +70,11 @@ public class TerminateRemoteProcessDialog { }; AtomicBoolean alreadyGone = new AtomicBoolean(false); - Window projectWindow = WindowManager.getInstance().suggestParentWindow(project); + Runnable dialogRemover = Messages.createMessageDialogRemover(project); ProcessAdapter listener = new ProcessAdapter() { public void processWillTerminate(ProcessEvent event, boolean willBeDestroyed) { alreadyGone.set(true); - UIUtil.invokeLaterIfNeeded(() -> makeCurrentMessageDialogGoAway( - projectWindow != null ? projectWindow.getOwnedWindows() : Window.getWindows())); + dialogRemover.run(); } }; processHandler.addProcessListener(listener); @@ -101,17 +95,6 @@ public class TerminateRemoteProcessDialog { return getConfirmation(exitCode, canDisconnect); } - private static void makeCurrentMessageDialogGoAway(@NotNull Window[] checkWindows) { - for (Window w : checkWindows) { - JDialog dialog = w instanceof JDialog ? (JDialog)w : null; - if (dialog == null || !dialog.isModal()) continue; - JButton cancelButton = UIUtil.uiTraverser(dialog.getRootPane()).filter(JButton.class) - .filter(b -> CommonBundle.getCancelButtonText().equals(b.getText())) - .first(); - if (cancelButton != null) cancelButton.doClick(); - } - } - private static GeneralSettings.ProcessCloseConfirmation getConfirmation(int button, boolean withDisconnect) { switch (button) { case 0: diff --git a/platform/lang-impl/src/com/intellij/ide/hierarchy/HierarchyBrowserBaseEx.java b/platform/lang-impl/src/com/intellij/ide/hierarchy/HierarchyBrowserBaseEx.java index 9db7d54a116e..163d62f596af 100644 --- a/platform/lang-impl/src/com/intellij/ide/hierarchy/HierarchyBrowserBaseEx.java +++ b/platform/lang-impl/src/com/intellij/ide/hierarchy/HierarchyBrowserBaseEx.java @@ -531,7 +531,7 @@ public abstract class HierarchyBrowserBaseEx extends HierarchyBrowserBase implem changeView(currentViewType); final HierarchyTreeBuilder builder = getCurrentBuilder(); builder.restoreExpandedAndSelectedInfo(storedInfo.get()); - }, __->!myAlarm.isDisposed()); + }, __-> isDisposed()); } protected String getCurrentScopeType() { diff --git a/platform/platform-api/src/com/intellij/execution/process/AnsiEscapeDecoder.java b/platform/platform-api/src/com/intellij/execution/process/AnsiEscapeDecoder.java index 3f0030cef70e..a21170eb39ac 100644 --- a/platform/platform-api/src/com/intellij/execution/process/AnsiEscapeDecoder.java +++ b/platform/platform-api/src/com/intellij/execution/process/AnsiEscapeDecoder.java @@ -36,8 +36,10 @@ public class AnsiEscapeDecoder { private static final String M_CSI = "m" + CSI; private static final char BACKSPACE = '\b'; - private Key myCurrentTextAttributes; private final ColoredOutputTypeRegistry myColoredOutputTypeRegistry = ColoredOutputTypeRegistry.getInstance(); + private Key myCurrentTextAttributes; + private String myUnhandledStdout; + private String myUnhandledStderr; /** * Parses ansi-color codes from text and sends text fragments with color attributes to textAcceptor @@ -48,31 +50,40 @@ public class AnsiEscapeDecoder { * It can implement ColoredChunksAcceptor to receive list of pairs (text, attribute). */ public void escapeText(@NotNull String text, @NotNull Key outputType, @NotNull ColoredTextAcceptor textAcceptor) { + text = prependUnhandledText(text, outputType); text = normalizeAsciiControlCharacters(text); int pos = 0; List> chunks = null; + int unhandledSuffixLength = 0; while (true) { - int escSeqBeginInd = text.indexOf(CSI, pos); + int escSeqBeginInd = findEscSeqBeginIndex(text, pos); if (escSeqBeginInd < 0) { + if (escSeqBeginInd < -1) { + unhandledSuffixLength = decodeUnhandledSuffixLength(escSeqBeginInd); + } break; } if (pos < escSeqBeginInd) { chunks = processTextChunk(chunks, text.substring(pos, escSeqBeginInd), outputType, textAcceptor); } - final int escSeqEndInd = findEscSeqEndIndex(text, escSeqBeginInd); + int escSeqEndInd = findConsecutiveEscSequencesEndIndex(text, escSeqBeginInd); if (escSeqEndInd < 0) { + if (escSeqEndInd < -1) { + unhandledSuffixLength = decodeUnhandledSuffixLength(escSeqEndInd); + } break; } - if (text.charAt(escSeqEndInd - 1) == 'm') { - String escSeq = text.substring(escSeqBeginInd, escSeqEndInd); + if (text.charAt(escSeqEndInd) == 'm') { + String escSeq = text.substring(escSeqBeginInd, escSeqEndInd + 1); // this is a simple fix for RUBY-8996: // we replace several consecutive escape sequences with one which contains all these sequences String colorAttribute = StringUtil.replace(escSeq, M_CSI, ";"); myCurrentTextAttributes = myColoredOutputTypeRegistry.getOutputKey(colorAttribute); } - pos = escSeqEndInd; + pos = escSeqEndInd + 1; } - if (pos < text.length()) { + updateUnhandledSuffix(text, outputType, unhandledSuffixLength); + if (unhandledSuffixLength == 0 && pos < text.length()) { chunks = processTextChunk(chunks, text.substring(pos), outputType, textAcceptor); } if (chunks != null && textAcceptor instanceof ColoredChunksAcceptor) { @@ -80,6 +91,30 @@ public class AnsiEscapeDecoder { } } + private void updateUnhandledSuffix(@NotNull String text, @NotNull Key outputType, int unhandledSuffixLength) { + String unhandledSuffix = unhandledSuffixLength > 0 ? text.substring(text.length() - unhandledSuffixLength) : null; + if (outputType == ProcessOutputTypes.STDOUT) { + myUnhandledStdout = unhandledSuffix; + } + else if (outputType == ProcessOutputTypes.STDERR) { + myUnhandledStderr = unhandledSuffix; + } + } + + @NotNull + private String prependUnhandledText(@NotNull String text, @NotNull Key outputType) { + String prevUnhandledText = null; + if (outputType == ProcessOutputTypes.STDOUT) { + prevUnhandledText = myUnhandledStdout; + myUnhandledStdout = null; + } + else if (outputType == ProcessOutputTypes.STDERR) { + prevUnhandledText = myUnhandledStderr; + myUnhandledStderr = null; + } + return prevUnhandledText != null ? prevUnhandledText + text : text; + } + @NotNull private static String normalizeAsciiControlCharacters(@NotNull String text) { int ind = text.indexOf(BACKSPACE); @@ -122,29 +157,58 @@ public class AnsiEscapeDecoder { return result.toString(); } - /* - * Selects all consecutive escape sequences and returns escape sequence end index (exclusive). - * If the escape sequence isn't finished, returns -1. + /** + * Returns the index of the first occurrence of CSI within the passed string that is greater than or equal to {@code fromIndex}, + * or negative number if CSI is not found: -1 - (length of text suffix to keep in case of an incomplete CSI). */ - private static int findEscSeqEndIndex(@NotNull String text, final int escSeqBeginInd) { - int beginInd = escSeqBeginInd; - while (true) { - int letterInd = findEscSeqLetterIndex(text, beginInd); - if (letterInd == -1) { - return beginInd == escSeqBeginInd ? -1 : beginInd; - } - if (text.charAt(letterInd) != 'm') { - return beginInd == escSeqBeginInd ? letterInd + 1 : beginInd; - } - beginInd = letterInd + 1; - } - } - - private static int findEscSeqLetterIndex(@NotNull String text, int escSeqBeginInd) { - if (!text.regionMatches(escSeqBeginInd, CSI, 0, CSI.length())) { + private static int findEscSeqBeginIndex(@NotNull String text, int fromIndex) { + int ind = text.indexOf(CSI.charAt(0), fromIndex); + if (ind == -1) { return -1; } - int parameterEndInd = escSeqBeginInd + 2; + else if (ind == text.length() - 1) { + return encodeUnhandledSuffixLength(text, ind); + } + return text.charAt(ind + 1) == CSI.charAt(1) ? ind : -1; + } + + /** + * Returns end index of all consecutive escape sequences started at {@code firstEscSeqBeginInd}, or + * negative number if not found: -1 - (length of string suffix to keep in case of an incomplete last escape sequence). + */ + private static int findConsecutiveEscSequencesEndIndex(@NotNull String text, int firstEscSeqBeginInd) { + int escSeqBeginInd = firstEscSeqBeginInd; + int lastMatchedColorEscSeqEndInd = -1; + int escSeqEndInd; + while ((escSeqEndInd = findEscSeqEndIndex(text, escSeqBeginInd)) >= 0) { + if (text.charAt(escSeqEndInd) != 'm') { + // Handle non-color escape sequences separately + // ColoredOutputTypeRegistry expects only color escape sequences and in a single consecutive text chunk + return lastMatchedColorEscSeqEndInd > 0 ? lastMatchedColorEscSeqEndInd : escSeqEndInd; + } + escSeqBeginInd = escSeqEndInd + 1; + lastMatchedColorEscSeqEndInd = escSeqEndInd; + if (escSeqEndInd + 1 >= text.length()) { + return encodeUnhandledSuffixLength(text, firstEscSeqBeginInd); + } + if (text.charAt(escSeqEndInd + 1) != CSI.charAt(0)) { + break; + } + if (escSeqEndInd + 2 >= text.length()) { + return encodeUnhandledSuffixLength(text, firstEscSeqBeginInd); + } + if (text.charAt(escSeqEndInd + 2) != CSI.charAt(1)) { + break; + } + } + if (escSeqEndInd < -1) { + return encodeUnhandledSuffixLength(text, firstEscSeqBeginInd); + } + return lastMatchedColorEscSeqEndInd; + } + + private static int findEscSeqEndIndex(@NotNull String text, int escSeqBeginInd) { + int parameterEndInd = escSeqBeginInd + CSI.length(); while (parameterEndInd < text.length()) { char ch = text.charAt(parameterEndInd); if (Character.isDigit(ch) || ch == ';') { @@ -154,13 +218,21 @@ public class AnsiEscapeDecoder { break; } } - if (parameterEndInd < text.length()) { - char letter = text.charAt(parameterEndInd); - if (StringUtil.containsChar("ABCDEFGHJKSTfmisu", letter)) { - return parameterEndInd; - } + if (parameterEndInd == text.length()) { + return encodeUnhandledSuffixLength(text, escSeqBeginInd); } - return -1; + return StringUtil.containsChar("ABCDEFGHJKSTfmisu", text.charAt(parameterEndInd)) ? parameterEndInd : -1; + } + + private static int encodeUnhandledSuffixLength(@NotNull String text, int suffixStartInd) { + return -1 - (text.length() - suffixStartInd); + } + + private static int decodeUnhandledSuffixLength(int encodedUnhandledSuffixLength) { + if (encodedUnhandledSuffixLength >= -1) { + throw new AssertionError(); + } + return -encodedUnhandledSuffixLength - 1; } @Nullable diff --git a/platform/platform-api/src/com/intellij/openapi/fileEditor/PsiElementNavigatable.java b/platform/platform-api/src/com/intellij/openapi/fileEditor/PsiElementNavigatable.java index d44ef2078bcd..de1423e73a93 100644 --- a/platform/platform-api/src/com/intellij/openapi/fileEditor/PsiElementNavigatable.java +++ b/platform/platform-api/src/com/intellij/openapi/fileEditor/PsiElementNavigatable.java @@ -26,6 +26,7 @@ import com.intellij.pom.Navigatable; import com.intellij.psi.PsiElement; import com.intellij.psi.SmartPointerManager; import com.intellij.psi.SmartPsiElementPointer; +import com.intellij.util.ObjectUtils; import org.jetbrains.annotations.NotNull; import static com.intellij.openapi.util.Conditions.or; @@ -39,8 +40,9 @@ public class PsiElementNavigatable implements Navigatable { @Override public final void navigate(boolean requestFocus) { - PsiElement element = myPointer.getElement(); - if (element != null && element.isValid()) { + PsiElement originalElement = myPointer.getElement(); + if (originalElement != null && originalElement.isValid()) { + PsiElement element = ObjectUtils.notNull(originalElement.getNavigationElement(), originalElement); VirtualFile file = element.getContainingFile().getVirtualFile(); if (file != null) { new Task.Modal(element.getProject(), EditorBundle.message("editor.open.file.progress", file.getName()), true) { @@ -62,7 +64,7 @@ public class PsiElementNavigatable implements Navigatable { @Override public boolean canNavigate() { PsiElement element = myPointer.getElement(); - return element != null && element.isValid() && element.getContainingFile().getVirtualFile() != null; + return element != null && element.isValid() && ObjectUtils.notNull(element.getNavigationElement(), element).getContainingFile().getVirtualFile() != null; } @Override diff --git a/platform/platform-api/src/com/intellij/openapi/ui/Messages.java b/platform/platform-api/src/com/intellij/openapi/ui/Messages.java index 92d1f80d1bdc..9faeec5af73d 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/Messages.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/Messages.java @@ -112,6 +112,24 @@ public class Messages { return UIUtil.getQuestionIcon(); } + @NotNull + public static Runnable createMessageDialogRemover(@Nullable Project project) { + Window projectWindow = project == null ? null : WindowManager.getInstance().suggestParentWindow(project); + return () -> UIUtil.invokeLaterIfNeeded(() -> makeCurrentMessageDialogGoAway( + projectWindow != null ? projectWindow.getOwnedWindows() : Window.getWindows())); + } + + private static void makeCurrentMessageDialogGoAway(@NotNull Window[] checkWindows) { + for (Window w : checkWindows) { + JDialog dialog = w instanceof JDialog ? (JDialog)w : null; + if (dialog == null || !dialog.isModal()) continue; + JButton cancelButton = UIUtil.uiTraverser(dialog.getRootPane()).filter(JButton.class) + .filter(b -> CommonBundle.getCancelButtonText().equals(b.getText())) + .first(); + if (cancelButton != null) cancelButton.doClick(); + } + } + /** * Please, use {@link #showOkCancelDialog} or {@link #showYesNoCancelDialog} if possible (these dialogs implements native OS behavior)! * @return number of button pressed: from 0 up to options.length-1 inclusive, or -1 for Cancel diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/UsageTrigger.java b/platform/platform-impl/src/com/intellij/internal/statistic/UsageTrigger.java index 770c560ad942..5c792732fa46 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/UsageTrigger.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/UsageTrigger.java @@ -19,10 +19,10 @@ import com.intellij.internal.statistic.beans.ConvertUsagesUtil; import com.intellij.internal.statistic.beans.GroupDescriptor; import com.intellij.internal.statistic.beans.UsageDescriptor; import com.intellij.openapi.components.*; -import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.xmlb.annotations.MapAnnotation; import com.intellij.util.xmlb.annotations.Tag; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import java.util.HashMap; @@ -42,11 +42,11 @@ public class UsageTrigger implements PersistentStateComponent\u001B[0m0\u001B[1;33m\u001B[0m") + .addExpected(":abcd", "\u001B[1;33m") + .addExpected(">", "\u001B[0;1;33m") + .addExpected("0", "stdout") + .addExpected("", "\u001B[0;1;33m") + )); + } + + private static void check(@NotNull ColoredText text) { + check(true, Collections.singletonList(text)); + } + + private static void check(boolean testCharByCharProcessing, @NotNull List texts) { AnsiEscapeDecoder decoder = new AnsiEscapeDecoder(); - decoder.escapeText(" 10% 0/1 build modules\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b 70% 1/1 build modules", - ProcessOutputTypes.STDERR, - createExpectedAcceptor( - Pair.create(" 70% 1/1 build modules", ProcessOutputTypes.STDERR) - ) - ); - decoder.escapeText("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b 40% 1/2 build modules\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b 30% 1/3 build modules\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b 25% 1/4 build modules", - ProcessOutputTypes.STDERR, - createExpectedAcceptor( - Pair.create("\n 25% 1/4 build modules", ProcessOutputTypes.STDERR) - ) - ); - } - - @NotNull - private static List> toListWithKeyName(@NotNull Collection> list) { - return ContainerUtil.map(list, pair -> Pair.create(pair.first, pair.second.toString())); - } - - @SafeVarargs - private static AnsiEscapeDecoder.ColoredChunksAcceptor createExpectedAcceptor(@NotNull final Pair... expected) { - return new AnsiEscapeDecoder.ColoredChunksAcceptor() { - @Override - public void coloredChunksAvailable(@NotNull List> chunks) { - List> expectedWithKeyName = toListWithKeyName(Arrays.asList(expected)); - List> actualWithKeyName = toListWithKeyName(chunks); - Assert.assertEquals(expectedWithKeyName, actualWithKeyName); - } - - @Override - public void coloredTextAvailable(@NotNull String text, @NotNull Key attributes) { - throw new RuntimeException(); // shouldn't be called - } + List> actualColoredChunks = ContainerUtil.newArrayList(); + //noinspection CodeBlock2Expr + AnsiEscapeDecoder.ColoredTextAcceptor acceptor = (text, attributes) -> { + actualColoredChunks.add(Pair.create(text, attributes.toString())); }; + // test stdout + for (ColoredText text : texts) { + decoder.escapeText(text.myRawText, ProcessOutputTypes.STDOUT, acceptor); + } + List> expectedColoredChunks = new ArrayList<>(); + for (ColoredText text : texts) { + expectedColoredChunks.addAll(text.myExpectedColoredChunks); + } + Assert.assertEquals(expectedColoredChunks, actualColoredChunks); + + if (testCharByCharProcessing) { + // test stdout char by char + actualColoredChunks.clear(); + decoder = new AnsiEscapeDecoder(); + for (ColoredText text : texts) { + for (int i = 0; i < text.myRawText.length(); i++) { + decoder.escapeText(String.valueOf(text.myRawText.charAt(i)), ProcessOutputTypes.STDOUT, acceptor); + } + } + expectedColoredChunks.clear(); + for (ColoredText text : texts) { + for (Pair chunk : text.myExpectedColoredChunks) { + String chunkText = chunk.first; + for (int i = 0; i < chunkText.length(); i++) { + expectedColoredChunks.add(Pair.create(String.valueOf(chunkText.charAt(i)), chunk.second)); + } + } + } + Assert.assertEquals(expectedColoredChunks, actualColoredChunks); + } + + // test stderr + actualColoredChunks.clear(); + decoder = new AnsiEscapeDecoder(); + for (ColoredText text : texts) { + decoder.escapeText(text.myRawText, ProcessOutputTypes.STDERR, acceptor); + } + expectedColoredChunks.clear(); + for (ColoredText text : texts) { + for (Pair chunk : text.myExpectedColoredChunks) { + expectedColoredChunks.add(Pair.create(chunk.first, STDERR_KEY)); + } + } + Assert.assertEquals(expectedColoredChunks, actualColoredChunks); } @NotNull public static Process createTestProcess() { // have to be synchronised because used from pooled thread ByteArrayOutputStream outputStream = new ByteArrayOutputStream(10000); - BufferExposingByteArrayInputStream inputStream = new BufferExposingByteArrayInputStream(new byte[0]); AtomicBoolean finished = new AtomicBoolean(); return new Process() { @Override @@ -114,17 +149,17 @@ public class AnsiEscapeDecoderTest extends PlatformTestCase { @Override public InputStream getInputStream() { - return inputStream; + return new ByteArrayInputStream(new byte[0]); } @Override public InputStream getErrorStream() { - return inputStream; + return new ByteArrayInputStream(new byte[0]); } @Override public int waitFor() { - while (!finished.get()); + while (!finished.get()) {} return 0; } @@ -143,11 +178,14 @@ public class AnsiEscapeDecoderTest extends PlatformTestCase { public void testPerformance() throws IOException { Process testProcess = createTestProcess(); + //noinspection CodeBlock2Expr withProcessHandlerFrom(testProcess, handler -> { - PlatformTestUtil.startPerformanceTest("ansi color", 15000, ()->{ - for (int i=0; i<2_000_000;i++) { - handler.notifyTextAvailable(i+"Chrome 35.0.1916 (Linux): Executed 0 of 1\u001B[32m SUCCESS\u001B[39m (0 secs / 0 secs)\n", ProcessOutputTypes.STDOUT); - handler.notifyTextAvailable(i+"Plain\u001B[32mGreen\u001B[39mNormal\u001B[1A\u001B[2K\u001B[31mRed\u001B[39m\n", ProcessOutputTypes.SYSTEM); + PlatformTestUtil.startPerformanceTest("ansi color", 15000, () -> { + for (int i = 0; i < 2_000_000; i++) { + handler.notifyTextAvailable(i + "Chrome 35.0.1916 (Linux): Executed 0 of 1\u001B[32m SUCCESS\u001B[39m (0 secs / 0 secs)\n", + ProcessOutputTypes.STDOUT); + handler.notifyTextAvailable(i + "Plain\u001B[32mGreen\u001B[39mNormal\u001B[1A\u001B[2K\u001B[31mRed\u001B[39m\n", + ProcessOutputTypes.SYSTEM); } }).cpuBound().assertTiming(); }); @@ -168,4 +206,18 @@ public class AnsiEscapeDecoderTest extends PlatformTestCase { handler.waitFor(); } } + + private static class ColoredText { + private final String myRawText; + private final List> myExpectedColoredChunks = new ArrayList<>(); + + public ColoredText(@NotNull String rawText) { + myRawText = rawText; + } + + private ColoredText addExpected(@NotNull String text, @NotNull String colorKey) { + myExpectedColoredChunks.add(Pair.create(text, colorKey)); + return this; + } + } } diff --git a/platform/script-debugger/debugger-ui/src/RemoteVmConnection.kt b/platform/script-debugger/debugger-ui/src/RemoteVmConnection.kt index 078941fc0233..9a3196e76d07 100644 --- a/platform/script-debugger/debugger-ui/src/RemoteVmConnection.kt +++ b/platform/script-debugger/debugger-ui/src/RemoteVmConnection.kt @@ -50,52 +50,53 @@ abstract class RemoteVmConnection : VmConnection() { port = address.port setState(ConnectionStatus.WAITING_FOR_CONNECTION, "Connecting to ${address.hostString}:${address.port}") val result = AsyncPromise() + + var attemptNumber = 0 + fun attempt() { + connectCancelHandler.set { result.setError("Closed explicitly") } + + val connectionPromise = AsyncPromise() + connectionPromise.rejected { result.setError(it) } + + result + .done { + vm = it!! + setState(ConnectionStatus.CONNECTED, "Connected to ${connectedAddressToPresentation(address, it)}") + startProcessing() + } + .rejected { + if (it !is ConnectException) { + LOG.errorIfNotMessage(it) + } + setState(ConnectionStatus.CONNECTION_FAILED, it.message) + } + .processed { connectCancelHandler.set(null) } + + createBootstrap(address, result) + .connect(address, connectionPromise, + maxAttemptCount = if (stopCondition == null) NettyUtil.DEFAULT_CONNECT_ATTEMPT_COUNT else -1, + stopCondition = stopCondition) + ?.closeFuture()?.addListener { + if (result.isFulfilled) { + close("Process disconnected unexpectedly", ConnectionStatus.DISCONNECTED) + } + else { + if (++attemptNumber > 10 || (stopCondition?.value(null) ?: false)) { + result.setError("Cannot establish connection - promptly closed after open") + } + else { + sleep(result, 300) + attempt() + } + } + } + } + val future = ApplicationManager.getApplication().executeOnPooledThread { if (Thread.interrupted()) { return@executeOnPooledThread } - var attemptNumber = 0 - fun attempt() { - connectCancelHandler.set { result.setError("Closed explicitly") } - - val connectionPromise = AsyncPromise() - connectionPromise.rejected { result.setError(it) } - - result - .done { - vm = it!! - setState(ConnectionStatus.CONNECTED, "Connected to ${connectedAddressToPresentation(address, it)}") - startProcessing() - } - .rejected { - if (it !is ConnectException) { - LOG.errorIfNotMessage(it) - } - setState(ConnectionStatus.CONNECTION_FAILED, it.message) - } - .processed { connectCancelHandler.set(null) } - - createBootstrap(address, result) - .connect(address, connectionPromise, - maxAttemptCount = if (stopCondition == null) NettyUtil.DEFAULT_CONNECT_ATTEMPT_COUNT else -1, - stopCondition = stopCondition) - ?.closeFuture()?.addListener { - if (result.isFulfilled) { - close("Process disconnected unexpectedly", ConnectionStatus.DISCONNECTED) - } - else { - if (++attemptNumber > 10 || (stopCondition?.value(null) ?: false)) { - result.setError("Process disconnected unexpectedly") - } - else { - sleep(result, 300) - attempt() - } - } - } - } - attempt() } diff --git a/platform/testFramework/src/com/intellij/testFramework/CpuTimings.java b/platform/testFramework/src/com/intellij/testFramework/CpuTimings.java index 4f9f185e05f9..08c10a09bbc2 100644 --- a/platform/testFramework/src/com/intellij/testFramework/CpuTimings.java +++ b/platform/testFramework/src/com/intellij/testFramework/CpuTimings.java @@ -29,7 +29,7 @@ import java.util.stream.LongStream; /** * @author peter */ -class CpuTimings { +public class CpuTimings { final long[] rawData; long average; private final double myStandardDeviation; @@ -58,7 +58,7 @@ class CpuTimings { @SuppressWarnings("UseOfSystemOutOrSystemErr") static CpuTimings calcStableCpuTiming() { for (int i = 0;; i++) { - CpuTimings timings = calcCpuTiming(); + CpuTimings timings = calcCpuTiming(20, CpuTimings::addBigIntegers); if (timings.myStandardDeviation < 1.8) { return timings; } @@ -73,24 +73,47 @@ class CpuTimings { } } - private static CpuTimings calcCpuTiming() { - int n = 20; - long[] elapsed = new long[n]; - for (int i = 0; i < n; i++) { - elapsed[i] = measureCPU(); + private static CpuTimings calcCpuTiming(int iterationCount, Runnable oneIteration) { + long[] elapsed = new long[iterationCount]; + for (int i = 0; i < iterationCount; i++) { + long start = System.currentTimeMillis(); + oneIteration.run(); + elapsed[i] = System.currentTimeMillis() - start; } return new CpuTimings(elapsed); } - private static long measureCPU() { - long start = System.currentTimeMillis(); - + private static void addBigIntegers() { BigInteger k = new BigInteger("1"); for (int i = 0; i < 1000000; i++) { k = k.add(new BigInteger("1")); } + } - return System.currentTimeMillis() - start; + private static void mulDiv() { + long k = 241; + for (int i = 0; i < 22_222_222; i++) { + k = i % 10 == 3 ? k * 239 : k % 12342; + } + ensureOdd(k); + } + + private static void ensureOdd(long k) { + if (k % 2 == 0) { + throw new AssertionError("Should be an odd value"); + } + } + + private static void mulDivMemAccess() { + int[] array = new int[240_000]; + for (int i = 0; i < array.length; i++) { + array[i] = i * 42 + 1; + } + int k = 241; + for (int i = 0; i < 5_750_000; i++) { + k *= array[Math.abs(k) % array.length]; + } + ensureOdd(k); } public static double getProcessCpuLoad() { @@ -134,4 +157,14 @@ class CpuTimings { } } + @SuppressWarnings("UseOfSystemOutOrSystemErr") + public static void main(String[] args) { + for (int i = 0; i < 20; i++) { + // each line can be uncommented alone, to check the results of different benchmarks + //System.out.println(calcCpuTiming(20, CpuTimings::addBigIntegers)); + //System.out.println(calcCpuTiming(20, CpuTimings::mulDivMemAccess)); + //System.out.println(calcCpuTiming(20, CpuTimings::mulDiv)); + } + } + } diff --git a/platform/util/src/com/intellij/util/ArrayUtil.java b/platform/util/src/com/intellij/util/ArrayUtil.java index 0f110b8dc207..6d85a1b47797 100644 --- a/platform/util/src/com/intellij/util/ArrayUtil.java +++ b/platform/util/src/com/intellij/util/ArrayUtil.java @@ -960,10 +960,12 @@ public class ArrayUtil extends ArrayUtilRt { int n = time.length; Arrays.sort(time); long total = 0; - for (int i= n /2- n / part /2; i< n /2+ n / part /2; i++) { + int start = n / 2 - n / part / 2; + int end = n / 2 + n / part / 2; + for (int i = start; i < end; i++) { total += time[i]; } - int middlePartLength = n / part; + int middlePartLength = end - start; return middlePartLength == 0 ? 0 : total / middlePartLength; } @@ -972,10 +974,12 @@ public class ArrayUtil extends ArrayUtilRt { int n = time.length; Arrays.sort(time); long total = 0; - for (int i= n /2- n / part /2; i< n /2+ n / part /2; i++) { + int start = n / 2 - n / part / 2; + int end = n / 2 + n / part / 2; + for (int i = start; i < end; i++) { total += time[i]; } - int middlePartLength = n / part; + int middlePartLength = end - start; return middlePartLength == 0 ? 0 : total / middlePartLength; } diff --git a/platform/util/src/com/intellij/util/xmlb/BeanBinding.java b/platform/util/src/com/intellij/util/xmlb/BeanBinding.java index 838d23a7507a..64303b5c9e87 100644 --- a/platform/util/src/com/intellij/util/xmlb/BeanBinding.java +++ b/platform/util/src/com/intellij/util/xmlb/BeanBinding.java @@ -94,24 +94,26 @@ public class BeanBinding extends NotNullDeserializeBinding { continue; } - if (filter != null) { - if (filter instanceof SkipDefaultsSerializationFilter) { - if (((SkipDefaultsSerializationFilter)filter).equal(binding, o)) { + Property property = accessor.getAnnotation(Property.class); + if (property == null || !property.alwaysWrite()) { + if (filter != null) { + if (filter instanceof SkipDefaultsSerializationFilter) { + if (((SkipDefaultsSerializationFilter)filter).equal(binding, o)) { + continue; + } + } + else if (!filter.accepts(accessor, o)) { continue; } } - else if (!filter.accepts(accessor, o)) { + + //todo: optimize. Cache it. + if (property != null && property.filter() != SerializationFilter.class && + !ReflectionUtil.newInstance(property.filter()).accepts(accessor, o)) { continue; } } - //todo: optimize. Cache it. - Property property = accessor.getAnnotation(Property.class); - if (property != null && property.filter() != SerializationFilter.class && - !ReflectionUtil.newInstance(property.filter()).accepts(accessor, o)) { - continue; - } - if (element == null) { element = new Element(myTagName); } diff --git a/platform/util/src/com/intellij/util/xmlb/annotations/Property.java b/platform/util/src/com/intellij/util/xmlb/annotations/Property.java index 0dad892f0a64..d5d9097f4ead 100644 --- a/platform/util/src/com/intellij/util/xmlb/annotations/Property.java +++ b/platform/util/src/com/intellij/util/xmlb/annotations/Property.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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,4 +31,6 @@ public @interface Property { boolean surroundWithTag() default true; Class filter() default SerializationFilter.class; + + boolean alwaysWrite() default false; } diff --git a/plugins/devkit/src/testAssistant/TestLocationDataRule.java b/plugins/devkit/src/testAssistant/TestLocationDataRule.java index 56fa658017be..0f4a71d9a19a 100644 --- a/plugins/devkit/src/testAssistant/TestLocationDataRule.java +++ b/plugins/devkit/src/testAssistant/TestLocationDataRule.java @@ -66,7 +66,7 @@ public class TestLocationDataRule implements GetDataRule { if (fileIndex.isInContent(file) && !fileIndex.isInSource(file) && !fileIndex.isInLibraryClasses(file)) { final VirtualFile parent = file.getParent(); final VirtualFile contentRoot = fileIndex.getContentRootForFile(file); - if (contentRoot != null) { + if (contentRoot != null && parent != null) { final String relativePath = VfsUtilCore.getRelativePath(parent, contentRoot, '/'); if (relativePath != null) { final PsiSearchHelper searchHelper = PsiSearchHelper.SERVICE.getInstance(project); diff --git a/plugins/devkit/src/util/PsiUtil.java b/plugins/devkit/src/util/PsiUtil.java index f382b354e9a8..47bb55b8179b 100644 --- a/plugins/devkit/src/util/PsiUtil.java +++ b/plugins/devkit/src/util/PsiUtil.java @@ -136,7 +136,7 @@ public class PsiUtil { return flag; } - public static boolean isPluginProject(final Project project) { + public static boolean isPluginProject(@NotNull final Project project) { return CachedValuesManager.getManager(project).getCachedValue(project, () -> { boolean foundMarkerClass = JavaPsiFacade.getInstance(project).findClass(IDE_PROJECT_MARKER_CLASS, diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/wizard/GradleModuleBuilder.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/wizard/GradleModuleBuilder.java index d1d57f385278..ddbb797d3fa7 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/wizard/GradleModuleBuilder.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/wizard/GradleModuleBuilder.java @@ -27,6 +27,8 @@ import com.intellij.ide.util.projectWizard.WizardContext; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.externalSystem.importing.ImportSpec; +import com.intellij.openapi.externalSystem.importing.ImportSpecBuilder; import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys; import com.intellij.openapi.externalSystem.model.project.ProjectData; import com.intellij.openapi.externalSystem.model.project.ProjectId; @@ -212,9 +214,12 @@ public class GradleModuleBuilder extends AbstractExternalModuleBuilder 1 expression = str(expression.replace('@LINE@', '\n')) #Not using frame.f_globals because of https://sourceforge.net/tracker2/?func=detail&aid=2541355&group_id=85796&atid=577329 @@ -484,16 +485,18 @@ def console_exec(thread_id, frame_id, expression, dbg): interpreter = ConsoleWriter() - try: - code = compile_command(expression) - except (OverflowError, SyntaxError, ValueError): - # Case 1 - interpreter.showsyntaxerror() - return False - - if code is None: - # Case 2 - return True + if not is_multiline: + try: + code = compile_command(expression) + except (OverflowError, SyntaxError, ValueError): + # Case 1 + interpreter.showsyntaxerror() + return False + if code is None: + # Case 2 + return True + else: + code = expression #Case 3 diff --git a/python/src/com/jetbrains/python/actions/PyExecuteSelectionAction.java b/python/src/com/jetbrains/python/actions/PyExecuteSelectionAction.java index 5d48204fc915..4a1d59a873d3 100644 --- a/python/src/com/jetbrains/python/actions/PyExecuteSelectionAction.java +++ b/python/src/com/jetbrains/python/actions/PyExecuteSelectionAction.java @@ -17,9 +17,12 @@ package com.jetbrains.python.actions; import com.google.common.collect.Lists; import com.intellij.execution.ExecutionHelper; +import com.intellij.execution.ExecutionManager; import com.intellij.execution.console.LanguageConsoleView; import com.intellij.execution.process.ProcessHandler; +import com.intellij.execution.ui.ExecutionConsole; import com.intellij.execution.ui.RunContentDescriptor; +import com.intellij.execution.ui.RunContentManager; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.ex.util.EditorUtil; @@ -27,15 +30,13 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.wm.ToolWindow; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentManager; import com.intellij.util.Consumer; -import com.jetbrains.python.console.PyCodeExecutor; -import com.jetbrains.python.console.PydevConsoleRunner; -import com.jetbrains.python.console.PythonConsoleRunnerFactory; -import com.jetbrains.python.console.PythonConsoleToolWindow; +import com.jetbrains.python.console.*; import com.jetbrains.python.psi.PyFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -176,11 +177,20 @@ public class PyExecuteSelectionAction extends AnAction { ExecutionHelper .selectContentDescriptor(dataContext, project, consoles, "Select console to execute in", descriptor -> { if (descriptor != null && descriptor.getExecutionConsole() instanceof PyCodeExecutor) { - consumer.consume((PyCodeExecutor)descriptor.getExecutionConsole()); - final PythonConsoleToolWindow toolWindow = PythonConsoleToolWindow.getInstance(project); - if (toolWindow != null && !toolWindow.getToolWindow().isVisible()) { - toolWindow.getToolWindow().show(null); - ContentManager contentManager = toolWindow.getToolWindow().getContentManager(); + ExecutionConsole console = descriptor.getExecutionConsole(); + consumer.consume((PyCodeExecutor)console); + ToolWindow toolWindow; + if (console instanceof PythonDebugLanguageConsoleView) { + RunContentManager runContentManager = ExecutionManager.getInstance(project).getContentManager(); + toolWindow = runContentManager.getToolWindowByDescriptor(descriptor); + } + else { + PythonConsoleToolWindow consoleToolWindow = PythonConsoleToolWindow.getInstance(project); + toolWindow = consoleToolWindow != null ? consoleToolWindow.getToolWindow() : null; + } + if (toolWindow != null && !toolWindow.isVisible()) { + toolWindow.show(null); + ContentManager contentManager = toolWindow.getContentManager(); Content content = contentManager.findContent(descriptor.getDisplayName()); if (content != null) { contentManager.setSelectedContent(content); diff --git a/python/src/com/jetbrains/python/console/PythonDebugLanguageConsoleView.java b/python/src/com/jetbrains/python/console/PythonDebugLanguageConsoleView.java index 0cad728b8eab..565b957e3418 100644 --- a/python/src/com/jetbrains/python/console/PythonDebugLanguageConsoleView.java +++ b/python/src/com/jetbrains/python/console/PythonDebugLanguageConsoleView.java @@ -61,7 +61,7 @@ public class PythonDebugLanguageConsoleView extends DuplexConsoleView