diff --git a/.idea/libraries/builder_model.xml b/.idea/libraries/builder_model.xml deleted file mode 100644 index 194983c4ed4c..000000000000 --- a/.idea/libraries/builder_model.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/dexlib2.xml b/.idea/libraries/dexlib2.xml new file mode 100644 index 000000000000..b38e3790e09d --- /dev/null +++ b/.idea/libraries/dexlib2.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/google_analytics_library.xml b/.idea/libraries/google_analytics_library.xml new file mode 100644 index 000000000000..4065f2b9e542 --- /dev/null +++ b/.idea/libraries/google_analytics_library.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/sherpa_solver.xml b/.idea/libraries/sherpa_solver.xml index b0cca4264467..dafc23b05b18 100644 --- a/.idea/libraries/sherpa_solver.xml +++ b/.idea/libraries/sherpa_solver.xml @@ -1,7 +1,7 @@ - + diff --git a/.idea/libraries/studio_profiler_grpc_1_0_jarjar.xml b/.idea/libraries/studio_profiler_grpc_1_0_jarjar.xml new file mode 100644 index 000000000000..16ebb4312e9c --- /dev/null +++ b/.idea/libraries/studio_profiler_grpc_1_0_jarjar.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 3455ca934d71..40c5ad631ac4 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -3,6 +3,7 @@ + @@ -12,15 +13,17 @@ + - + - - + + + @@ -65,6 +68,7 @@ + @@ -190,7 +194,9 @@ + + @@ -200,6 +206,8 @@ + + @@ -211,6 +219,7 @@ + @@ -236,14 +245,13 @@ - + - - + @@ -252,7 +260,6 @@ - @@ -290,6 +297,7 @@ + @@ -307,7 +315,6 @@ - \ No newline at end of file diff --git a/.idea/runConfigurations/Android_Studio.xml b/.idea/runConfigurations/Android_Studio.xml new file mode 100644 index 000000000000..8019da516f41 --- /dev/null +++ b/.idea/runConfigurations/Android_Studio.xml @@ -0,0 +1,26 @@ + + + + + + \ No newline at end of file diff --git a/build/groovy/org/jetbrains/intellij/build/BaseIdeaProperties.groovy b/build/groovy/org/jetbrains/intellij/build/BaseIdeaProperties.groovy index 837dd3668a0d..82a78e76ae37 100644 --- a/build/groovy/org/jetbrains/intellij/build/BaseIdeaProperties.groovy +++ b/build/groovy/org/jetbrains/intellij/build/BaseIdeaProperties.groovy @@ -65,7 +65,7 @@ abstract class BaseIdeaProperties extends ProductProperties { "tasks-core", "tasks-java", "maven", "gradle", "git4idea", "remote-servers-git", "remote-servers-git-java", "svn4idea", "hg4idea", "github", "cvs-plugin", - "jetgroovy", "junit", "testng", "xpath", "xslt-debugger", "android", "javaFX-CE", + "jetgroovy", "junit", "testng", "xpath", "xslt-debugger", "android-plugin", "javaFX-CE", "java-i18n", "ant", "ui-designer", "ByteCodeViewer", "coverage", "java-decompiler-plugin", "devkit", "eclipse", "IntelliLang", "IntelliLang-java", "IntelliLang-xml", "intellilang-jps-plugin" ] diff --git a/community-main.iml b/community-main.iml index 8f57ec84e1cc..ba81bf74570e 100644 --- a/community-main.iml +++ b/community-main.iml @@ -123,5 +123,6 @@ + \ No newline at end of file diff --git a/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java b/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java index 59149f132d9d..a89bc6392904 100644 --- a/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java +++ b/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java @@ -291,6 +291,9 @@ public abstract class QuickFixFactory { @NotNull public abstract IntentionAction createInsertMethodCallFix(@NotNull PsiMethodCallExpression call, PsiMethod method); + @NotNull + public abstract LocalQuickFixAndIntentionActionOnPsiElement createAccessStaticViaInstanceFix(PsiReferenceExpression methodRef, JavaResolveResult result); + @NotNull public abstract IntentionAction createWrapStringWithFileFix(@Nullable PsiType type, @NotNull PsiExpression expression); } \ No newline at end of file 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 5b4b97d96ecf..654dedd48022 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 @@ -657,33 +657,32 @@ public class HighlightMethodUtil { return null; } String description; - PsiElement elementToHighlight; + PsiElement elementToHighlight = ObjectUtils.notNull(referenceToMethod.getReferenceNameElement(), referenceToMethod); if (element != null && !resolveResult.isAccessible()) { description = HighlightUtil.buildProblemWithAccessDescription(referenceToMethod, resolveResult); - elementToHighlight = referenceToMethod.getReferenceNameElement(); } else if (element != null && !resolveResult.isStaticsScopeCorrect()) { - description = null; - elementToHighlight = ObjectUtils.notNull(referenceToMethod.getReferenceNameElement(), referenceToMethod); - if (element instanceof PsiMethod && ((PsiMethod)element).hasModifierProperty(PsiModifier.STATIC)) { PsiClass containingClass = ((PsiMethod)element).getContainingClass(); if (containingClass != null && containingClass.isInterface()) { HighlightInfo info = HighlightUtil.checkFeature(elementToHighlight, HighlightUtil.Feature.STATIC_INTERFACE_CALLS, languageLevel, file); if (info != null) return info; description = checkStaticInterfaceMethodCallQualifier(referenceToMethod, resolveResult.getCurrentFileResolveScope(), containingClass); + if (description != null) { + HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(highlightInfoType).range(elementToHighlight).description(description) + .escapedToolTip(XmlStringUtil.escapeString(description)).create(); + QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createAccessStaticViaInstanceFix(referenceToMethod, resolveResult)); + return highlightInfo; + } } } - if (description == null) { - description = HighlightUtil.buildProblemWithStaticDescription(element); - } + description = HighlightUtil.buildProblemWithStaticDescription(element); } else { String methodName = referenceToMethod.getReferenceName() + buildArgTypesList(list); description = JavaErrorMessages.message("cannot.resolve.method", methodName); if (candidates.length == 0) { - elementToHighlight = referenceToMethod.getReferenceNameElement(); highlightInfoType = HighlightInfoType.WRONG_REF; } else { 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 467f71c2a18d..edaf4e6391c8 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 @@ -200,6 +200,7 @@ public class HighlightUtil extends HighlightUtilBase { /** * make element protected/package-private/public suggestion + * for private method in the interface it should add default modifier as well */ static void registerAccessQuickFixAction(@NotNull PsiMember refElement, @NotNull PsiJavaCodeReferenceElement place, @@ -238,6 +239,10 @@ public class HighlightUtil extends HighlightUtilBase { if (refElement.hasModifierProperty(PsiModifier.PROTECTED)) { minModifier = PsiModifier.PUBLIC; } + PsiClass containingClass = refElement.getContainingClass(); + if (containingClass != null && containingClass.isInterface()) { + minModifier = PsiModifier.PUBLIC; + } String[] modifiers = {PsiModifier.PACKAGE_LOCAL, PsiModifier.PROTECTED, PsiModifier.PUBLIC,}; for (int i = ArrayUtil.indexOf(modifiers, minModifier); i < modifiers.length; i++) { @PsiModifier.ModifierConstant String modifier = modifiers[i]; @@ -967,7 +972,7 @@ public class HighlightUtil extends HighlightUtilBase { if (PsiModifier.PRIVATE.equals(modifier)) { isAllowed &= modifierOwnerParent instanceof PsiClass && - (!((PsiClass)modifierOwnerParent).isInterface() || PsiUtil.isLanguageLevel9OrHigher(modifierOwner)); + (!((PsiClass)modifierOwnerParent).isInterface() || PsiUtil.isLanguageLevel9OrHigher(modifierOwner) && !((PsiClass)modifierOwnerParent).isAnnotationType()); } else if (PsiModifier.STRICTFP.equals(modifier)) { isAllowed &= modifierOwnerParent instanceof PsiClass && (!((PsiClass)modifierOwnerParent).isInterface() || PsiUtil.isLanguageLevel8OrHigher(modifierOwner)); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/intention/EmptyQuickFixFactory.java b/java/java-analysis-impl/src/com/intellij/codeInsight/intention/EmptyQuickFixFactory.java index 7fe3c51a17c5..b3544def540e 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/intention/EmptyQuickFixFactory.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/intention/EmptyQuickFixFactory.java @@ -669,6 +669,13 @@ public class EmptyQuickFixFactory extends QuickFixFactory { return QuickFixes.EMPTY_FIX; } + @NotNull + @Override + public LocalQuickFixAndIntentionActionOnPsiElement createAccessStaticViaInstanceFix(PsiReferenceExpression methodRef, + JavaResolveResult result) { + return QuickFixes.EMPTY_FIX; + } + @NotNull @Override public IntentionAction createWrapStringWithFileFix(@Nullable PsiType type, @NotNull PsiExpression expression) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/accessStaticViaInstance/AccessStaticViaInstanceBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/accessStaticViaInstance/AccessStaticViaInstanceBase.java index 748bce25fc53..a1addd4f5ae4 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/accessStaticViaInstance/AccessStaticViaInstanceBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/accessStaticViaInstance/AccessStaticViaInstanceBase.java @@ -84,6 +84,10 @@ public class AccessStaticViaInstanceBase extends BaseJavaBatchLocalInspectionToo } if (!((PsiMember)resolved).hasModifierProperty(PsiModifier.STATIC)) return; + //don't report warnings on compilation errors + PsiClass containingClass = ((PsiMember)resolved).getContainingClass(); + if (containingClass != null && containingClass.isInterface()) return; + String description = JavaErrorMessages.message("static.member.accessed.via.instance.reference", JavaHighlightUtil.formatType(qualifierExpression.getType()), HighlightMessageUtil.getSymbolName(resolved, result.getSubstitutor())); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AccessStaticViaInstanceFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AccessStaticViaInstanceFix.java index d41e92b9522c..78b9445ec247 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AccessStaticViaInstanceFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AccessStaticViaInstanceFix.java @@ -96,7 +96,7 @@ public class AccessStaticViaInstanceFix extends LocalQuickFixAndIntentionActionO try { PsiElement newQualifier = qualifierExpression.replace(factory.createReferenceExpression(containingClass)); PsiElement qualifiedWithClassName = myExpression.copy(); - if (myExpression.getTypeParameters().length == 0) { + if (myExpression.getTypeParameters().length == 0 && !(containingClass.isInterface() && !containingClass.equals(PsiTreeUtil.getParentOfType(myExpression, PsiClass.class)))) { newQualifier.delete(); if (myExpression.resolve() != myMember) { myExpression.replace(qualifiedWithClassName); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ModifierFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ModifierFix.java index 3f3948ce123c..068000712f53 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ModifierFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ModifierFix.java @@ -200,11 +200,21 @@ public class ModifierFix extends LocalQuickFixAndIntentionActionOnPsiElement { ApplicationManager.getApplication().runWriteAction(() -> { changeModifierList(modifierList); - if (myShouldHave && owner instanceof PsiMethod && PsiModifier.ABSTRACT.equals(myModifier)) { - final PsiMethod method = (PsiMethod)owner; - final PsiClass aClass = method.getContainingClass(); - if (aClass != null && !aClass.hasModifierProperty(PsiModifier.ABSTRACT)) { - changeModifierList(aClass.getModifierList()); + if (myShouldHave && owner instanceof PsiMethod) { + if (PsiModifier.ABSTRACT.equals(myModifier)) { + final PsiMethod method = (PsiMethod)owner; + final PsiClass aClass = method.getContainingClass(); + if (aClass != null && !aClass.hasModifierProperty(PsiModifier.ABSTRACT)) { + changeModifierList(aClass.getModifierList()); + } + } + else if (PsiModifier.PUBLIC.equals(myModifier) && + ((PsiMethod)owner).getBody() != null && + !((PsiMethod)owner).hasModifierProperty(PsiModifier.STATIC)) { + PsiClass containingClass = ((PsiMethod)owner).getContainingClass(); + if (containingClass != null && containingClass.isInterface()) { + modifierList.setModifierProperty(PsiModifier.DEFAULT, true); + } } } UndoUtil.markPsiFileForUndo(containingFile); diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java index 93ec1bfd0293..50992a935694 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java @@ -851,6 +851,13 @@ public class QuickFixFactoryImpl extends QuickFixFactory { return new InsertMethodCallFix(call, method); } + @NotNull + @Override + public LocalQuickFixAndIntentionActionOnPsiElement createAccessStaticViaInstanceFix(PsiReferenceExpression methodRef, + JavaResolveResult result) { + return new AccessStaticViaInstanceFix(methodRef, result, true); + } + @NotNull @Override public IntentionAction createWrapStringWithFileFix(@Nullable PsiType type, @NotNull PsiExpression expression) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiModifierListImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiModifierListImpl.java index 27b21d3ac3b5..5a1b21416507 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiModifierListImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiModifierListImpl.java @@ -17,7 +17,6 @@ package com.intellij.psi.impl.source; import com.intellij.codeInsight.AnnotationTargetUtil; import com.intellij.lang.ASTNode; -import com.intellij.openapi.util.Condition; import com.intellij.psi.*; import com.intellij.psi.augment.PsiAugmentProvider; import com.intellij.psi.impl.CheckUtil; @@ -158,9 +157,11 @@ public class PsiModifierListImpl extends JavaStubPsiElement else if (parent instanceof PsiMethod) { PsiClass aClass = ((PsiMethod)parent).getContainingClass(); if (aClass != null && aClass.isInterface()) { - implicitModifiers.add(PUBLIC); - if (!explicitModifiers.contains(DEFAULT) && !explicitModifiers.contains(STATIC) && !explicitModifiers.contains(PRIVATE)) { - implicitModifiers.add(ABSTRACT); + if (!explicitModifiers.contains(PRIVATE)) { + implicitModifiers.add(PUBLIC); + if (!explicitModifiers.contains(DEFAULT) && !explicitModifiers.contains(STATIC)) { + implicitModifiers.add(ABSTRACT); + } } } else if (aClass != null && aClass.isEnum() && ((PsiMethod)parent).isConstructor()) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/PrivateInInterfaces.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/PrivateInInterfaces.java index 4998dae837eb..d0f2e08ea6ba 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/PrivateInInterfaces.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/PrivateInInterfaces.java @@ -20,4 +20,9 @@ interface E { interface F { private int m = 0; +} + +@interface G { + private String m(); + private String m1() {return "";} } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/PrivateInInterfacesOverriding.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/PrivateInInterfacesOverriding.java new file mode 100644 index 000000000000..091b779ac2a6 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/PrivateInInterfacesOverriding.java @@ -0,0 +1,25 @@ +interface A { + void m(); +} +interface A1 extends A { + private static void m() {} +} +interface A2 extends A { + private void m() {} +} + +interface B { + private void m() {} +} +interface B1 extends B { + private void m() {} +} +interface B2 extends B { + private static void m() {} +} +interface B3 extends B { + default void m() {} +} +interface B4 extends B { + void m(); +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/accessStaticViaInstance/afterAccessForStaticMethodInInterface.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/accessStaticViaInstance/afterAccessForStaticMethodInInterface.java new file mode 100644 index 000000000000..19861c850d9e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/accessStaticViaInstance/afterAccessForStaticMethodInInterface.java @@ -0,0 +1,10 @@ +// "Access static 'I.m()' via class 'I' reference" "true" +interface I { + static void m() {} +} + +class A implements I { + { + I.m(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/accessStaticViaInstance/beforeAccessForStaticMethodInInterface.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/accessStaticViaInstance/beforeAccessForStaticMethodInInterface.java new file mode 100644 index 000000000000..26a1b58499c2 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/accessStaticViaInstance/beforeAccessForStaticMethodInInterface.java @@ -0,0 +1,10 @@ +// "Access static 'I.m()' via class 'I' reference" "true" +interface I { + static void m() {} +} + +class A implements I { + { + this.m(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/modifier19/afterMakeDefault.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/modifier19/afterMakeDefault.java new file mode 100644 index 000000000000..c787af2ad7ee --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/modifier19/afterMakeDefault.java @@ -0,0 +1,10 @@ +// "Make 'I.foo' public" "true" +interface I { + default void foo() { } +} + +class A implements I { + { + this.foo(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/modifier19/beforeMakeDefault.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/modifier19/beforeMakeDefault.java new file mode 100644 index 000000000000..76b1c34ebdb9 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/modifier19/beforeMakeDefault.java @@ -0,0 +1,10 @@ +// "Make 'I.foo' public" "true" +interface I { + private void foo() { } +} + +class A implements I { + { + this.foo(); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk9Test.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk9Test.java index c4b9a08f1e0a..f23c916d42e6 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk9Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk9Test.java @@ -41,6 +41,7 @@ public class LightAdvHighlightingJdk9Test extends LightDaemonAnalyzerTestCase { public void testSafeVarargsApplicability() { doTest(true, false); } public void testPrivateInInterfaces() { doTest(false, false); } + public void testPrivateInInterfacesOverriding() { doTest(false, false); } public void testUnderscore() { doTest(false, false); } public void testTryWithResources() { doTest(false, false); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/Modifier19Test.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/Modifier19Test.java new file mode 100644 index 000000000000..202de7565669 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/Modifier19Test.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.daemon.quickFix; + +import com.intellij.testFramework.LightProjectDescriptor; +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import org.jetbrains.annotations.NotNull; + +public class Modifier19Test extends LightQuickFixParameterizedTestCase { + public void test() throws Exception { doAllTests(); } + + @Override + protected String getBasePath() { + return "/codeInsight/daemonCodeAnalyzer/quickFix/modifier19"; + } + + @NotNull + @Override + protected LightProjectDescriptor getProjectDescriptor() { + return LightCodeInsightFixtureTestCase.JAVA_9; + } +} diff --git a/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityLibraryLicenses.groovy b/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityLibraryLicenses.groovy index cc579576ec08..9a04a5cf841c 100644 --- a/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityLibraryLicenses.groovy +++ b/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityLibraryLicenses.groovy @@ -40,7 +40,8 @@ class CommunityLibraryLicenses { new LibraryLicense(name: "Android SDK Tools JPS", libraryName: "android-sdk-tools-jps", license: "Apache 2.0", url: "http://source.android.com/"), new LibraryLicense(name: "Android SDK Tools", libraryName: "android-sdk-tools", license: "Apache 2.0", url: "http://source.android.com/"), - new LibraryLicense(name: "Android Analytics", libraryName: "analytics", license: "Apache 2.0", url: "http://source.android.com/"), + new LibraryLicense(name: "Android Analytics", libraryName: "google-analytics-library", license: "Apache 2.0", url: "http://source.android.com/"), + new LibraryLicense(name: "Android Profiler", libraryName: "studio-profiler-grpc-1.0-jarjar", license: "Apache 2.0", url: "http://source.android.com/"), new LibraryLicense(name: "Android Layout Library", libraryName: "layoutlib", license: "Apache 2.0", url: "http://source.android.com/"), new LibraryLicense(name: "Android DEX library", libraryName: "dexlib2", license: "Apache 2.0", url: "http://source.android.com/"), new LibraryLicense(name: "Android Sherpa library", libraryName: "sherpa-solver", license: "Apache 2.0", url: "http://source.android.com/"), diff --git a/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityRepositoryModules.groovy b/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityRepositoryModules.groovy index 54896406ad6d..191e75c28f35 100644 --- a/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityRepositoryModules.groovy +++ b/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityRepositoryModules.groovy @@ -238,18 +238,26 @@ class CommunityRepositoryModules { ] static PluginLayout androidPlugin(Map additionalModulesToJars) { - plugin("android") { + plugin("android-plugin") { + mainJarName = "android.jar" + directoryName = "android" + withModule("android", "android.jar") + withModule("observable", "android.jar") + withModule("wizard", "android.jar") + withModule("sdk-updates", "android.jar") + withModule("designer", "android.jar") + withModule("manifest-merger") + withModule("repository") + withModule("common", "android-base-common.jar") withModule("android-common", "android-common.jar", false) withModule("android-rt", "android-rt.jar", false) withModule("android-annotations", "androidAnnotations.jar") - withModule("common") withModule("sdklib", "sdklib.jar") withModule("sdk-common", "sdk-common.jar") withModule("layoutlib-api", "layoutlib-api.jar") withModule("layoutlib", "layoutlib-loader.jar") - withModule("manifest-merger") withModule("adt-ui", "adt-ui.jar") - withModule("repository") + withModule("adt-ui-model", "adt-ui.jar") withModule("sherpa-ui", "constraint-layout.jar") withModule("pixelprobe", "pixalprobe.jar") withModule("manifest-merger", "manifest-merger.jar") @@ -261,7 +269,6 @@ class CommunityRepositoryModules { withModule("lint-checks", "sdk-tools.jar") withModule("ninepatch", "sdk-tools.jar") withModule("perflib", "sdk-tools.jar") - withModule("rpclib", "sdk-tools.jar") withModule("builder-model", "sdk-tools.jar") withModule("builder-test-api", "sdk-tools.jar") withModule("instant-run-common", "sdk-tools.jar") @@ -270,37 +277,37 @@ class CommunityRepositoryModules { withModule("android-gradle-jps", "jps/android-gradle-jps.jar", false) withModule("android-jps-plugin", "jps/android-jps-plugin.jar", false) withProjectLibrary("freemarker-2.3.20") //todo[nik] move to module libraries - withProjectLibrary("builder-model") //todo[nik] move to module libraries withProjectLibrary("jgraphx-3.4.0.1") //todo[nik] move to module libraries withProjectLibrary("kxml2") //todo[nik] move to module libraries withProjectLibrary("lombok-ast") //todo[nik] move to module libraries withProjectLibrary("layoutlib") //todo[nik] move to module libraries - withResource("device-art-resources", "lib/device-art-resources") + withResource("../android/device-art-resources", "lib/device-art-resources") withResourceFromModule("layoutlib-resources", ".", "lib/layoutlib") withResourceFromModule("sdklib", "../templates", "lib/templates") - withResourceArchive("annotations", "lib/androidAnnotations.jar") - withResource("lib/antlr4-runtime-4.5.3.jar", "lib") - withResource("lib/asm-5.0.3.jar", "lib") - withResource("lib/asm-analysis-5.0.3.jar", "lib") - withResource("lib/asm-tree-5.0.3.jar", "lib") - withResource("lib/commons-io-2.4.jar", "lib") - withResource("lib/commons-compress-1.8.1.jar", "lib") - withResource("lib/javawriter-2.2.1.jar", "lib") - withResource("lib/juniversalchardet-1.0.3.jar", "lib") - withResource("lib/layoutlib.jar", "lib") - withResource("lib/gluegen-rt.jar", "lib") - withResource("lib/gluegen-rt-natives-linux-amd64.jar", "lib") - withResource("lib/gluegen-rt-natives-linux-i586.jar", "lib") - withResource("lib/gluegen-rt-natives-macosx-universal.jar", "lib") - withResource("lib/gluegen-rt-natives-windows-amd64.jar", "lib") - withResource("lib/gluegen-rt-natives-windows-i586.jar", "lib") + withResourceArchive("../android/annotations", "lib/androidAnnotations.jar") + withResource("../android/lib/antlr4-runtime-4.5.3.jar", "lib") + withResource("../android/lib/asm-5.0.3.jar", "lib") + withResource("../android/lib/asm-analysis-5.0.3.jar", "lib") + withResource("../android/lib/asm-tree-5.0.3.jar", "lib") + withResource("../android/lib/commons-io-2.4.jar", "lib") + withResource("../android/lib/commons-compress-1.8.1.jar", "lib") + withResource("../android/lib/javawriter-2.2.1.jar", "lib") + withResource("../android/lib/juniversalchardet-1.0.3.jar", "lib") + withResource("../android/lib/layoutlib.jar", "lib") + withResource("../android/lib/google-analytics-library.jar", "lib") + withResource("../android/lib/gluegen-rt.jar", "lib") + withResource("../android/lib/gluegen-rt-natives-linux-amd64.jar", "lib") + withResource("../android/lib/gluegen-rt-natives-linux-i586.jar", "lib") + withResource("../android/lib/gluegen-rt-natives-macosx-universal.jar", "lib") + withResource("../android/lib/gluegen-rt-natives-windows-amd64.jar", "lib") + withResource("../android/lib/gluegen-rt-natives-windows-i586.jar", "lib") withProjectLibrary("jogl-all") //todo[nik] move to module libraries - withResource("lib/jogl-all-natives-linux-amd64.jar", "lib") - withResource("lib/jogl-all-natives-linux-i586.jar", "lib") - withResource("lib/jogl-all-natives-macosx-universal.jar", "lib") - withResource("lib/jogl-all-natives-windows-amd64.jar", "lib") - withResource("lib/jogl-all-natives-windows-i586.jar", "lib") - withResource("lib/androidWidgets", "lib/androidWidgets") + withResource("../android/lib/jogl-all-natives-linux-amd64.jar", "lib") + withResource("../android/lib/jogl-all-natives-linux-i586.jar", "lib") + withResource("../android/lib/jogl-all-natives-macosx-universal.jar", "lib") + withResource("../android/lib/jogl-all-natives-windows-amd64.jar", "lib") + withResource("../android/lib/jogl-all-natives-windows-i586.jar", "lib") + withResource("../android/lib/androidWidgets", "lib/androidWidgets") additionalModulesToJars.entrySet().each { withModule(it.key, it.value) } diff --git a/platform/platform-impl/src/com/intellij/designer/LightToolWindowManager.java b/platform/platform-impl/src/com/intellij/designer/LightToolWindowManager.java index 1ad75ae19716..ed47e0d868e4 100644 --- a/platform/platform-impl/src/com/intellij/designer/LightToolWindowManager.java +++ b/platform/platform-impl/src/com/intellij/designer/LightToolWindowManager.java @@ -262,7 +262,7 @@ public abstract class LightToolWindowManager implements ProjectComponent { } @Nullable - final ToolWindowAnchor getEditorMode() { + public final ToolWindowAnchor getEditorMode() { String value = myPropertiesComponent.getValue(myEditorModeKey); if (value == null) { return getAnchor(); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorHistoryManager.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorHistoryManager.java index d8c4954d226a..c40b712289e0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorHistoryManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorHistoryManager.java @@ -267,7 +267,7 @@ public final class EditorHistoryManager implements PersistentStateComponent myProgressImagesCache = new HashMap<>(); private BufferedImage getAppImage() { assertIsDispatchThread(); @@ -240,6 +243,7 @@ public abstract class AppIcon { if (myCurrentProcessId != null && !myCurrentProcessId.equals(processId)) return false; setDockIcon(getAppImage()); + myProgressImagesCache.remove(myCurrentProcessId); myCurrentProcessId = null; myLastValue = 0; @@ -308,7 +312,8 @@ public abstract class AppIcon { progressArea.intersect(borderArea); - AppImage appImg = createAppImage(); + AppImage appImg = myProgressImagesCache.get(myCurrentProcessId); + if (appImg == null) myProgressImagesCache.put(myCurrentProcessId, appImg = createAppImage()); appImg.myG2d.setColor(PROGRESS_BACKGROUND_COLOR); appImg.myG2d.fill(backgroundArea); diff --git a/platform/platform-impl/src/com/intellij/ui/ListSpeedSearch.java b/platform/platform-impl/src/com/intellij/ui/ListSpeedSearch.java index e6415812f966..88248e6cafb6 100644 --- a/platform/platform-impl/src/com/intellij/ui/ListSpeedSearch.java +++ b/platform/platform-impl/src/com/intellij/ui/ListSpeedSearch.java @@ -16,6 +16,7 @@ package com.intellij.ui; import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.IdeActions; import com.intellij.openapi.project.DumbAwareAction; @@ -108,7 +109,10 @@ public class ListSpeedSearch extends SpeedSearchBase { public MySelectAllAction(@NotNull JList list, @NotNull ListSpeedSearch search) { myList = list; mySearch = search; - copyShortcutFrom(ActionManager.getInstance().getAction(IdeActions.ACTION_SELECT_ALL)); + AnAction action = ActionManager.getInstance().getAction(IdeActions.ACTION_SELECT_ALL); + if (action != null) { + copyShortcutFrom(action); + } setEnabledInModalContext(true); } diff --git a/platform/platform-impl/src/com/intellij/util/JdkBundle.java b/platform/platform-impl/src/com/intellij/util/JdkBundle.java index da2981831826..e737db7daaf1 100644 --- a/platform/platform-impl/src/com/intellij/util/JdkBundle.java +++ b/platform/platform-impl/src/com/intellij/util/JdkBundle.java @@ -204,9 +204,10 @@ public class JdkBundle { private static Pair, Pair> getJDKNameArchVersionAndUpdate(File jvm, String homeSubPath) { GeneralCommandLine commandLine = new GeneralCommandLine().withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.NONE); - File jvmPath = new File(jvm, homeSubPath + File.separator + "jre" + File.separator + "bin" + File.separator + "java"); + String javaExecutable = "java" + (SystemInfo.isWindows ? ".exe" : ""); + File jvmPath = new File(jvm, homeSubPath + File.separator + "jre" + File.separator + "bin" + File.separator + javaExecutable); if (!jvmPath.exists()) { - jvmPath = new File(jvm, homeSubPath + File.separator + "bin" + File.separator + "java"); + jvmPath = new File(jvm, homeSubPath + File.separator + "bin" + File.separator + javaExecutable); } commandLine.setExePath(jvmPath.getAbsolutePath()); commandLine.addParameter("-version"); diff --git a/platform/testFramework/testSrc/tests/testGroups.properties b/platform/testFramework/testSrc/tests/testGroups.properties index 33cab98b4005..9c44a1ef0514 100644 --- a/platform/testFramework/testSrc/tests/testGroups.properties +++ b/platform/testFramework/testSrc/tests/testGroups.properties @@ -150,6 +150,8 @@ org.jetbrains.android.jps.AndroidBuilderTest com.android.tools.idea.gradle.service.notification.hyperlink.CustomNotificationListenerTest com.android.repository.impl.installer.BasicInstallerTest org.jetbrains.android.AndroidLintTest +com.android.tools.adtui.* +com.android.builder.model.VersionTest [GRADLE_INTEGRATION_TESTS] org.jetbrains.plugins.gradle.importing.* diff --git a/plugins/git4idea/src/git4idea/GitContentRevision.java b/plugins/git4idea/src/git4idea/GitContentRevision.java index 30a2eaeddf69..411d3bc82e66 100644 --- a/plugins/git4idea/src/git4idea/GitContentRevision.java +++ b/plugins/git4idea/src/git4idea/GitContentRevision.java @@ -141,12 +141,7 @@ public class GitContentRevision implements ByteBackedContentRevision { @Nullable VcsRevisionNumber revisionNumber, Project project, boolean isDeleted, final boolean canBeDeleted, boolean unescapePath) throws VcsException { - final FilePath file; - if (project.isDisposed()) { - file = VcsUtil.getFilePath(makeAbsolutePath(vcsRoot, path, unescapePath), false); - } else { - file = createPath(vcsRoot, path, isDeleted, canBeDeleted, unescapePath); - } + FilePath file = createPath(vcsRoot, path, isDeleted, canBeDeleted, unescapePath); return createRevision(file, revisionNumber, project); } @@ -191,13 +186,8 @@ public class GitContentRevision implements ByteBackedContentRevision { public static ContentRevision createRevision(@NotNull final VirtualFile file, @Nullable final VcsRevisionNumber revisionNumber, @NotNull final Project project) { - return createRevision(file, revisionNumber, project, null); - } - - public static ContentRevision createRevision(@NotNull final VirtualFile file, @Nullable final VcsRevisionNumber revisionNumber, - @NotNull final Project project, @Nullable final Charset charset) { - final FilePath filePath = VcsUtil.getFilePath(file); - return createRevision(filePath, revisionNumber, project, charset); + FilePath filePath = VcsUtil.getFilePath(file); + return createRevision(filePath, revisionNumber, project, null); } public static ContentRevision createRevision(@NotNull final FilePath filePath, @Nullable final VcsRevisionNumber revisionNumber, diff --git a/python/educational-core/resources/META-INF/plugin.xml b/python/educational-core/resources/META-INF/plugin.xml index dcbc35a025d4..5f794738eaa2 100644 --- a/python/educational-core/resources/META-INF/plugin.xml +++ b/python/educational-core/resources/META-INF/plugin.xml @@ -41,14 +41,11 @@ - - - - + @@ -77,8 +74,6 @@ description="Watch test input "/> - diff --git a/python/educational-core/resources/icons/com/jetbrains/edu/learning/Stepik.png b/python/educational-core/resources/icons/com/jetbrains/edu/learning/Stepik.png new file mode 100644 index 000000000000..299f0d979719 Binary files /dev/null and b/python/educational-core/resources/icons/com/jetbrains/edu/learning/Stepik.png differ diff --git a/python/educational-core/resources/icons/com/jetbrains/edu/learning/Stepik@2x.png b/python/educational-core/resources/icons/com/jetbrains/edu/learning/Stepik@2x.png new file mode 100644 index 000000000000..60229f83a5ec Binary files /dev/null and b/python/educational-core/resources/icons/com/jetbrains/edu/learning/Stepik@2x.png differ diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java index 61d0323f968c..d1548a4244fe 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java @@ -105,8 +105,10 @@ public class CCProjectComponent extends AbstractProjectComponent { public void projectOpened() { migrateIfNeeded(); - VirtualFileManager.getInstance().addVirtualFileListener(myTaskFileLifeListener); - EduUsagesCollector.projectTypeOpened(CCUtils.COURSE_MODE); + if (CCUtils.isCourseCreator(myProject)) { + VirtualFileManager.getInstance().addVirtualFileListener(myTaskFileLifeListener); + EduUsagesCollector.projectTypeOpened(CCUtils.COURSE_MODE); + } } public void projectClosed() { diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCSubtaskEditorNotificationProvider.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCSubtaskEditorNotificationProvider.java index 8c6c2a49b0fa..6189b8c9186f 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCSubtaskEditorNotificationProvider.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCSubtaskEditorNotificationProvider.java @@ -28,6 +28,7 @@ import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholderSubtaskInfo; import com.jetbrains.edu.learning.courseFormat.Lesson; import com.jetbrains.edu.learning.courseFormat.TaskFile; +import com.jetbrains.edu.learning.courseFormat.tasks.PyCharmTask; import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks; import org.jetbrains.annotations.NotNull; @@ -227,7 +228,7 @@ public class CCSubtaskEditorNotificationProvider extends EditorNotifications.Pro final Lesson lesson = myTask.getLesson(); final List list = lesson.getTaskList(); final int i = list.indexOf(myTask); - final Task task = new Task(); + final Task task = new PyCharmTask(); task.copyTaskParameters(myTask); for (TaskFile taskFile : task.getTaskFiles().values()) { taskFile.setTask(task); diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCUtils.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCUtils.java index 0b8815f4aa58..a8be51afe37f 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCUtils.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCUtils.java @@ -29,6 +29,7 @@ import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; import com.jetbrains.edu.learning.courseFormat.StudyItem; import com.jetbrains.edu.learning.courseFormat.TaskFile; +import com.jetbrains.edu.learning.courseFormat.tasks.PyCharmTask; import com.jetbrains.edu.learning.courseFormat.tasks.Task; import org.apache.commons.codec.binary.Base64; import org.jetbrains.annotations.NotNull; @@ -264,7 +265,7 @@ public class CCUtils { final Lesson lesson = new Lesson(); lesson.setName(EduNames.PYCHARM_ADDITIONAL); - final Task task = new Task(); + final Task task = new PyCharmTask(); task.setLesson(lesson); task.setName(EduNames.PYCHARM_ADDITIONAL); task.setIndex(1); diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java index cfc2e0dd636b..7a2f28b61b4a 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java @@ -16,8 +16,6 @@ import com.jetbrains.edu.learning.courseFormat.TaskFile; import com.jetbrains.edu.learning.courseFormat.tasks.Task; import org.jetbrains.annotations.NotNull; -import java.io.File; - public class CCVirtualFileListener implements VirtualFileListener { @Override @@ -37,7 +35,7 @@ public class CCVirtualFileListener implements VirtualFileListener { return; } Course course = StudyTaskManager.getInstance(project).getCourse(); - if (course == null || !CCUtils.isCourseCreator(project)) { + if (course == null) { return; } TaskFile taskFile = StudyUtils.getTaskFile(project, createdFile); diff --git a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCCreateTask.java b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCCreateTask.java index 892f93ba0399..3f64f5dc4588 100644 --- a/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCCreateTask.java +++ b/python/educational-core/src/com/jetbrains/edu/coursecreator/actions/CCCreateTask.java @@ -11,6 +11,7 @@ import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; import com.jetbrains.edu.learning.courseFormat.StudyItem; +import com.jetbrains.edu.learning.courseFormat.tasks.PyCharmTask; import com.jetbrains.edu.learning.courseFormat.tasks.Task; import icons.EducationalCoreIcons; import org.jetbrains.annotations.NotNull; @@ -109,7 +110,7 @@ public class CCCreateTask extends CCCreateStudyItemActionBase { @Override protected StudyItem createAndInitItem(@NotNull Course course, @Nullable StudyItem parentItem, String name, int index) { - final Task task = new Task(name); + final Task task = new PyCharmTask(name); task.setIndex(index); if (parentItem == null) { return null; diff --git a/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java b/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java index aaa21bbf5ead..c41739bef591 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java @@ -105,7 +105,6 @@ public interface EduPluginConfigurator { } group.add(action); } - group.add(new StudyRunAction()); group.add(new StudyEditInputAction()); return group; } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/StudySerializationUtils.java b/python/educational-core/src/com/jetbrains/edu/learning/StudySerializationUtils.java index 043625347769..fb5f151f69dd 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/StudySerializationUtils.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/StudySerializationUtils.java @@ -97,6 +97,7 @@ public class StudySerializationUtils { private static String THEORY_TAG = "theoryTask"; private static String ADAPTIVE_TASK_PARAMETERS = "adaptiveTaskParameters"; private static String ADAPTIVE = "adaptive"; + public static final String PYCHARM_TASK = "PyCharmTask"; private static String TASK_WITH_SUBTASKS = "TaskWithSubtasks"; private static String THEORY_TASK = "TheoryTask"; private static String CHOICE_TASK = "ChoiceTask"; @@ -285,6 +286,9 @@ public class StudySerializationUtils { else if (Boolean.valueOf(adaptive.getAttributeValue(VALUE))) { task.setName(CODE_TASK); } + else { + task.setName(PYCHARM_TASK); + } task.removeContent(adaptiveParams); task.removeContent(theoryTask); } @@ -638,7 +642,7 @@ public class StudySerializationUtils { case "choice": return gson.fromJson(json, ChoiceTask.class); case "theory": return gson.fromJson(json, TheoryTask.class); case "code": return gson.fromJson(json, CodeTask.class); - case "pycharm": return gson.fromJson(json, Task.class); + case "pycharm": return gson.fromJson(json, PyCharmTask.class); case "subtasks": return gson.fromJson(json, TaskWithSubtasks.class); default: { LOG.warn("Unsupported task type " + taskType); diff --git a/python/educational-core/src/com/jetbrains/edu/learning/StudyUtils.java b/python/educational-core/src/com/jetbrains/edu/learning/StudyUtils.java index 8909c6936fd7..46d3ca1b1fc2 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/StudyUtils.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/StudyUtils.java @@ -1,15 +1,11 @@ package com.jetbrains.edu.learning; -import com.intellij.execution.RunContentExecutor; -import com.intellij.execution.configurations.GeneralCommandLine; -import com.intellij.execution.process.ProcessHandler; import com.intellij.ide.IdeView; import com.intellij.ide.fileTemplates.FileTemplate; import com.intellij.ide.fileTemplates.FileTemplateManager; import com.intellij.ide.fileTemplates.FileTemplateUtil; import com.intellij.ide.projectView.ProjectView; import com.intellij.ide.util.EditorHelper; -import com.intellij.lang.Language; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; @@ -31,7 +27,6 @@ import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; import com.intellij.openapi.fileEditor.impl.LoadTextUtil; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.popup.Balloon; import com.intellij.openapi.ui.popup.JBPopupFactory; @@ -61,8 +56,6 @@ import com.intellij.util.TimeoutUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.MarkdownUtil; import com.intellij.util.ui.UIUtil; -import com.jetbrains.edu.learning.checker.StudyExecutor; -import com.jetbrains.edu.learning.checker.StudyTestRunner; import com.jetbrains.edu.learning.core.EduAnswerPlaceholderDeleteHandler; import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter; import com.jetbrains.edu.learning.core.EduNames; @@ -237,42 +230,6 @@ public class StudyUtils { } } - @Nullable - public static Sdk findSdk(@NotNull final Task task, @NotNull final Project project) { - final Language language = task.getLesson().getCourse().getLanguageById(); - return StudyExecutor.INSTANCE.forLanguage(language).findSdk(project); - } - - @NotNull - public static StudyTestRunner getTestRunner(@NotNull final Task task, @NotNull final VirtualFile taskDir) { - final Language language = task.getLesson().getCourse().getLanguageById(); - return StudyExecutor.INSTANCE.forLanguage(language).getTestRunner(task, taskDir); - } - - public static RunContentExecutor getExecutor(@NotNull final Project project, @NotNull final Task currentTask, - @NotNull final ProcessHandler handler) { - final Language language = currentTask.getLesson().getCourse().getLanguageById(); - return StudyExecutor.INSTANCE.forLanguage(language).getExecutor(project, handler); - } - - public static void setCommandLineParameters(@NotNull final GeneralCommandLine cmd, - @NotNull final Project project, - @NotNull final String filePath, - @NotNull final String sdkPath, - @NotNull final Task currentTask) { - final Language language = currentTask.getLesson().getCourse().getLanguageById(); - StudyExecutor.INSTANCE.forLanguage(language).setCommandLineParameters(cmd, project, filePath, sdkPath, currentTask); - } - - public static void showNoSdkNotification(@NotNull final Task currentTask, @NotNull final Project project) { - final Lesson lesson = currentTask.getLesson(); - if (lesson == null) return; - final Course course = lesson.getCourse(); - if (course == null) return; - final Language language = course.getLanguageById(); - StudyExecutor.INSTANCE.forLanguage(language).showNoSdkNotification(project); - } - /** * shows pop up in the center of "check task" button in study editor @@ -735,7 +692,7 @@ public class StudyUtils { } public static void registerStudyToolWindow(@Nullable final Course course, Project project) { - if (course != null && "PyCharm".equals(course.getCourseType())) { + if (course != null && EduNames.PYCHARM.equals(course.getCourseType())) { final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project); registerToolWindows(toolWindowManager, project); final ToolWindow studyToolWindow = toolWindowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW); diff --git a/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java b/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java index 984d1a5dd699..abf32c6c97e5 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java @@ -13,7 +13,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.util.Ref; import com.intellij.openapi.vfs.VirtualFile; -import com.jetbrains.edu.learning.StudyCheckListener; +import com.jetbrains.edu.learning.checker.StudyCheckListener; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.checker.StudyCheckUtils; import com.jetbrains.edu.learning.courseFormat.tasks.Task; diff --git a/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyRunAction.java b/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyRunAction.java deleted file mode 100644 index a565dadd4528..000000000000 --- a/python/educational-core/src/com/jetbrains/edu/learning/actions/StudyRunAction.java +++ /dev/null @@ -1,123 +0,0 @@ -package com.jetbrains.edu.learning.actions; - -import com.intellij.execution.ExecutionException; -import com.intellij.execution.RunContentExecutor; -import com.intellij.execution.configurations.GeneralCommandLine; -import com.intellij.execution.process.OSProcessHandler; -import com.intellij.execution.process.ProcessHandler; -import com.intellij.execution.process.ProcessListener; -import com.intellij.icons.AllIcons; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.project.DumbAwareAction; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.vfs.VirtualFile; -import com.jetbrains.edu.learning.core.EduUtils; -import com.jetbrains.edu.learning.courseFormat.tasks.Task; -import com.jetbrains.edu.learning.courseFormat.TaskFile; -import com.jetbrains.edu.learning.StudyTaskManager; -import com.jetbrains.edu.learning.StudyUtils; -import com.jetbrains.edu.learning.courseFormat.UserTest; -import com.jetbrains.edu.learning.editor.StudyEditor; -import org.jetbrains.annotations.NotNull; - -import java.util.LinkedList; -import java.util.List; - -public class StudyRunAction extends DumbAwareAction { - private static final Logger LOG = Logger.getInstance(StudyRunAction.class.getName()); - public static final String ACTION_ID = "StudyRunAction"; - private ProcessHandler myHandler; - private List myProcessListeners = new LinkedList<>(); - - public StudyRunAction() { - super("Run File With Tests", "Run your code with tests", AllIcons.General.Run); - } - - public void run(@NotNull final Project project) { - if (myHandler != null && !myHandler.isProcessTerminated()) return; - Editor selectedEditor = StudyUtils.getSelectedEditor(project); - FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance(); - assert selectedEditor != null; - VirtualFile openedFile = fileDocumentManager.getFile(selectedEditor.getDocument()); - - if (openedFile != null && openedFile.getCanonicalPath() != null) { - String filePath = openedFile.getCanonicalPath(); - executeFile(project, openedFile, filePath); - } - } - - private void executeFile(@NotNull final Project project, - @NotNull final VirtualFile openedFile, @NotNull final String filePath) { - GeneralCommandLine cmd = new GeneralCommandLine(); - cmd.withWorkDirectory(openedFile.getParent().getCanonicalPath()); - - TaskFile selectedTaskFile = StudyUtils.getTaskFile(project, openedFile); - assert selectedTaskFile != null; - final Task currentTask = selectedTaskFile.getTask(); - final Sdk sdk = StudyUtils.findSdk(currentTask, project); - if (sdk == null) { - StudyUtils.showNoSdkNotification(currentTask, project); - return; - } - String sdkHomePath = sdk.getHomePath(); - if (sdkHomePath != null) { - cmd.setExePath(sdkHomePath); - StudyUtils.setCommandLineParameters(cmd, project, filePath, sdkHomePath, currentTask); - - try { - myHandler = new OSProcessHandler(cmd); - } - catch (ExecutionException e) { - LOG.error(e); - return; - } - - for (ProcessListener processListener : myProcessListeners) { - myHandler.addProcessListener(processListener); - } - - final RunContentExecutor executor = StudyUtils.getExecutor(project, currentTask, myHandler); - if (executor != null) { - Disposer.register(project, executor); - executor.run(); - } - EduUtils.synchronize(); - } - } - - public void addProcessListener(@NotNull final ProcessListener processListener) { - myProcessListeners.add(processListener); - } - - public void removeProcessListener(@NotNull final ProcessListener processListener) { - myProcessListeners.remove(processListener); - } - - public void actionPerformed(@NotNull AnActionEvent e) { - final Project project = e.getProject(); - if (project != null) { - run(project); - } - } - - @Override - public void update(AnActionEvent e) { - EduUtils.enableAction(e, false); - - final Project project = e.getProject(); - if (project != null) { - StudyEditor studyEditor = StudyUtils.getSelectedStudyEditor(project); - if (studyEditor != null) { - final List userTests = StudyTaskManager.getInstance(project).getUserTests(studyEditor.getTaskFile().getTask()); - if (!userTests.isEmpty()) { - EduUtils.enableAction(e, true); - } - } - } - } -} diff --git a/python/educational-core/src/com/jetbrains/edu/learning/StudyCheckListener.java b/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyCheckListener.java similarity index 91% rename from python/educational-core/src/com/jetbrains/edu/learning/StudyCheckListener.java rename to python/educational-core/src/com/jetbrains/edu/learning/checker/StudyCheckListener.java index d4896f864d27..e4f2fa636028 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/StudyCheckListener.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyCheckListener.java @@ -1,4 +1,4 @@ -package com.jetbrains.edu.learning; +package com.jetbrains.edu.learning.checker; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.project.Project; diff --git a/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java b/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java index 0c070df1cb99..09e411d82931 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java @@ -3,29 +3,24 @@ package com.jetbrains.edu.learning.checker; import com.intellij.execution.process.CapturingProcessHandler; import com.intellij.execution.process.ProcessOutput; import com.intellij.ide.projectView.ProjectView; -import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; -import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; -import com.jetbrains.edu.learning.*; -import com.jetbrains.edu.learning.actions.StudyRunAction; +import com.jetbrains.edu.learning.StudySettings; +import com.jetbrains.edu.learning.StudyState; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; -import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.StudyStatus; -import com.jetbrains.edu.learning.courseFormat.TaskFile; import com.jetbrains.edu.learning.courseFormat.tasks.ChoiceTask; import com.jetbrains.edu.learning.courseFormat.tasks.Task; -import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks; import com.jetbrains.edu.learning.courseFormat.tasks.TheoryTask; import com.jetbrains.edu.learning.stepic.EduAdaptiveStepicConnector; import com.jetbrains.edu.learning.stepic.EduStepicConnector; @@ -33,8 +28,6 @@ import com.jetbrains.edu.learning.stepic.StepicUser; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Map; - public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroundable { private static final Logger LOG = Logger.getInstance(StudyCheckTask.class); @@ -47,9 +40,7 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun private final Ref myCheckInProcess; private final Process myTestProcess; private final String myCommandLine; - private boolean myRunTestFile = true; private static final String FAILED_CHECK_LAUNCH = "Failed to launch checking"; - private static final String DO_NOT_RUN_ON_CHECK = "DO_NOT_RUN_ON_CHECK"; public StudyCheckTask(Project project, StudyState studyState, Ref checkInProcess, Process testProcess, String commandLine) { super(project, "Checking Task"); @@ -98,9 +89,6 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun private void checkForEduCourse(@NotNull ProgressIndicator indicator) { final StudyTestsOutputParser.TestsOutput testsOutput = getTestOutput(indicator); - if (myRunTestFile) { - ApplicationManager.getApplication().invokeLater(() -> runTask(myProject)); - } if (testsOutput != null) { if (testsOutput.isSuccess()) { onTaskSolved(testsOutput.getMessage()); @@ -119,14 +107,6 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun } } - private static void runTask(@NotNull Project project) { - final StudyRunAction runAction = (StudyRunAction)ActionManager.getInstance().getAction(StudyRunAction.ACTION_ID); - if (runAction == null) { - return; - } - runAction.run(project); - } - @Nullable private StudyTestsOutputParser.TestsOutput getTestOutput(@NotNull ProgressIndicator indicator) { final CapturingProcessHandler handler = new CapturingProcessHandler(myTestProcess, null, myCommandLine); @@ -135,7 +115,6 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun ApplicationManager.getApplication().invokeLater( () -> StudyCheckUtils.showTestResultPopUp("Check cancelled", MessageType.WARNING.getPopupBackground(), myProject)); } - myRunTestFile = !output.getStdout().contains(DO_NOT_RUN_ON_CHECK); final Course course = StudyTaskManager.getInstance(myProject).getCourse(); if (course != null) { final StudyTestsOutputParser.TestsOutput testsOutput = StudyTestsOutputParser.getTestsOutput(output, course.isAdaptive()); @@ -173,7 +152,8 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun if (reactionPosted) { if (myStatusBeforeCheck != StudyStatus.Solved) { myTask.setStatus(StudyStatus.Solved); - EduAdaptiveStepicConnector.addNextRecommendedTask(myProject, indicator, EduAdaptiveStepicConnector.NEXT_RECOMMENDATION_REACTION); + EduAdaptiveStepicConnector.addNextRecommendedTask(myProject, myTask.getLesson(), indicator, + EduAdaptiveStepicConnector.NEXT_RECOMMENDATION_REACTION); } } else { @@ -203,7 +183,8 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun if (pair.getFirst()) { onTaskSolved("Congratulations! Remote tests passed."); if (myStatusBeforeCheck != StudyStatus.Solved) { - EduAdaptiveStepicConnector.addNextRecommendedTask(myProject, indicator, EduAdaptiveStepicConnector.NEXT_RECOMMENDATION_REACTION); + EduAdaptiveStepicConnector.addNextRecommendedTask(myProject, myTask.getLesson(), indicator, + EduAdaptiveStepicConnector.NEXT_RECOMMENDATION_REACTION); } } else { @@ -224,91 +205,13 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun } protected void onTaskFailed(@NotNull String message) { - final Course course = StudyTaskManager.getInstance(myProject).getCourse(); myTask.setStatus(StudyStatus.Failed); - if (course != null) { - if (course.isAdaptive()) { - ApplicationManager.getApplication().invokeLater( - () -> { - StudyCheckUtils.showTestResultPopUp("Failed", MessageType.ERROR.getPopupBackground(), myProject); - StudyCheckUtils.showTestResultsToolWindow(myProject, message, false); - }); - } - else { - ApplicationManager.getApplication() - .invokeLater(() -> StudyCheckUtils.showTestResultPopUp(message, MessageType.ERROR.getPopupBackground(), myProject)); - } - } + myTask.getChecker(myProject).onTaskFailed(message); } protected void onTaskSolved(@NotNull String message) { - final Course course = StudyTaskManager.getInstance(myProject).getCourse(); myTask.setStatus(StudyStatus.Solved); - if (course != null) { - if (course.isAdaptive()) { - ApplicationManager.getApplication().invokeLater( - () -> { - if (myTask instanceof ChoiceTask) { - StudyCheckUtils.showTestResultPopUp("Congratulations!", MessageType.INFO.getPopupBackground(), myProject); - } - else { - StudyCheckUtils.showTestResultPopUp("Congratulations!", MessageType.INFO.getPopupBackground(), myProject); - StudyCheckUtils.showTestResultsToolWindow(myProject, message, true); - } - }); - } - else { - if (myTask instanceof TaskWithSubtasks) { - boolean hasMoreSubtasks = ((TaskWithSubtasks)myTask).activeSubtaskNotLast(); - final int activeSubtaskIndex = ((TaskWithSubtasks)myTask).getActiveSubtaskIndex(); - int visibleSubtaskIndex = activeSubtaskIndex + 1; - - ApplicationManager.getApplication().invokeLater(() -> { - int subtaskSize = ((TaskWithSubtasks)myTask).getLastSubtaskIndex() + 1; - String resultMessage = !hasMoreSubtasks ? message : "Subtask " + visibleSubtaskIndex + "/" + subtaskSize + " solved"; - StudyCheckUtils.showTestResultPopUp(resultMessage, MessageType.INFO.getPopupBackground(), myProject); - if (hasMoreSubtasks) { - int nextSubtaskIndex = activeSubtaskIndex + 1; - StudySubtaskUtils.switchStep(myProject, (TaskWithSubtasks)myTask, nextSubtaskIndex); - rememberAnswers(nextSubtaskIndex); - } - }); - } - else { - ApplicationManager.getApplication().invokeLater( - () -> StudyCheckUtils.showTestResultPopUp(message, MessageType.INFO.getPopupBackground(), myProject)); - } - } - } - } - - private void rememberAnswers(int nextSubtaskIndex) { - VirtualFile taskDir = myTask.getTaskDir(myProject); - if (taskDir == null) { - return; - } - VirtualFile srcDir = taskDir.findChild(EduNames.SRC); - if (srcDir != null) { - taskDir = srcDir; - } - for (Map.Entry entry : myTask.getTaskFiles().entrySet()) { - TaskFile taskFile = entry.getValue(); - VirtualFile virtualFile = taskDir.findFileByRelativePath(entry.getKey()); - if (virtualFile == null) { - continue; - } - Document document = FileDocumentManager.getInstance().getDocument(virtualFile); - if (document == null) { - continue; - } - for (AnswerPlaceholder placeholder : taskFile.getActivePlaceholders()) { - if (placeholder.getSubtaskInfos().containsKey(nextSubtaskIndex - 1)) { - int offset = placeholder.getOffset(); - String answer = document.getText(TextRange.create(offset, offset + placeholder.getRealLength())); - placeholder.getSubtaskInfos().get(nextSubtaskIndex - 1).setAnswer(answer); - } - } - } + myTask.getChecker(myProject).onTaskSolved(message); } private void runAfterTaskCheckedActions() { diff --git a/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java b/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java index 7ba9aef911f5..714433bcb03b 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java @@ -28,8 +28,8 @@ import com.jetbrains.edu.learning.core.EduDocumentListener; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; -import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; +import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.editor.StudyEditor; import com.jetbrains.edu.learning.navigation.StudyNavigator; import com.jetbrains.edu.learning.ui.StudyTestResultsToolWindowFactory; @@ -108,7 +108,7 @@ public class StudyCheckUtils { } - public static void showTestResultPopUp(final String text, Color color, @NotNull final Project project) { + public static void showTestResultPopUp(@NotNull final String text, Color color, @NotNull final Project project) { BalloonBuilder balloonBuilder = JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(text, null, color, null); final Balloon balloon = balloonBuilder.createBalloon(); @@ -201,30 +201,26 @@ public class StudyCheckUtils { } } - public static void showTestResultsToolWindow(@NotNull final Project project, @NotNull final String message, boolean solved) { - final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project); - ToolWindow window = toolWindowManager.getToolWindow(StudyTestResultsToolWindowFactoryKt.ID); - if (window == null) { - toolWindowManager.registerToolWindow(StudyTestResultsToolWindowFactoryKt.ID, true, ToolWindowAnchor.BOTTOM); - window = toolWindowManager.getToolWindow(StudyTestResultsToolWindowFactoryKt.ID); - new StudyTestResultsToolWindowFactory().createToolWindowContent(project, window); - } - - final Content[] contents = window.getContentManager().getContents(); - for (Content content : contents) { - final JComponent component = content.getComponent(); - if (component instanceof ConsoleViewImpl) { - ((ConsoleViewImpl)component).clear(); - if (!solved) { - ((ConsoleViewImpl)component).print(message, ConsoleViewContentType.ERROR_OUTPUT); - } - else { - ((ConsoleViewImpl)component).print(message, ConsoleViewContentType.NORMAL_OUTPUT); - } - window.setAvailable(true, () -> {}); - window.show(() -> {}); - return; + public static void showTestResultsToolWindow(@NotNull final Project project, @NotNull final String message) { + ApplicationManager.getApplication().invokeLater(() -> { + final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project); + ToolWindow window = toolWindowManager.getToolWindow(StudyTestResultsToolWindowFactoryKt.ID); + if (window == null) { + toolWindowManager.registerToolWindow(StudyTestResultsToolWindowFactoryKt.ID, true, ToolWindowAnchor.BOTTOM); + window = toolWindowManager.getToolWindow(StudyTestResultsToolWindowFactoryKt.ID); + new StudyTestResultsToolWindowFactory().createToolWindowContent(project, window); } - } + + final Content[] contents = window.getContentManager().getContents(); + for (Content content : contents) { + final JComponent component = content.getComponent(); + if (component instanceof ConsoleViewImpl) { + ((ConsoleViewImpl)component).clear(); + ((ConsoleViewImpl)component).print(message, ConsoleViewContentType.ERROR_OUTPUT); + window.setAvailable(true,null); + window.show(null); + } + } + }); } } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyExecutor.java b/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyExecutor.java deleted file mode 100644 index 8e27c2bedd79..000000000000 --- a/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyExecutor.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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.jetbrains.edu.learning.checker; - -import com.intellij.execution.RunContentExecutor; -import com.intellij.execution.configurations.GeneralCommandLine; -import com.intellij.execution.process.ProcessHandler; -import com.intellij.lang.LanguageExtension; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.openapi.vfs.VirtualFile; -import com.jetbrains.edu.learning.courseFormat.tasks.Task; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public interface StudyExecutor { - LanguageExtension INSTANCE = new LanguageExtension<>("Edu.StudyExecutor"); - - @Nullable - Sdk findSdk(@NotNull final Project project); - - StudyTestRunner getTestRunner(@NotNull final Task task, @NotNull final VirtualFile taskDir); - - RunContentExecutor getExecutor(@NotNull final Project project, @NotNull final ProcessHandler handler); - - void setCommandLineParameters(@NotNull final GeneralCommandLine cmd, - @NotNull final Project project, - @NotNull final String filePath, - @NotNull final String sdkPath, - @NotNull final Task currentTask); - - void showNoSdkNotification(@NotNull final Project project); -} diff --git a/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyTaskChecker.java b/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyTaskChecker.java new file mode 100644 index 000000000000..879621a1a725 --- /dev/null +++ b/python/educational-core/src/com/jetbrains/edu/learning/checker/StudyTaskChecker.java @@ -0,0 +1,27 @@ +package com.jetbrains.edu.learning.checker; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.MessageType; +import com.jetbrains.edu.learning.courseFormat.tasks.Task; +import org.jetbrains.annotations.NotNull; + +public class StudyTaskChecker { + @NotNull protected final T myTask; + @NotNull protected final Project myProject; + + public StudyTaskChecker(@NotNull T task, @NotNull Project project) { + myTask = task; + myProject = project; + } + + public void onTaskSolved(@NotNull String message) { + ApplicationManager.getApplication().invokeLater( + () -> StudyCheckUtils.showTestResultPopUp(message, MessageType.INFO.getPopupBackground(), myProject)); + } + + public void onTaskFailed(@NotNull String message) { + ApplicationManager.getApplication() + .invokeLater(() -> StudyCheckUtils.showTestResultPopUp(message, MessageType.ERROR.getPopupBackground(), myProject)); + } +} diff --git a/python/educational-core/src/com/jetbrains/edu/learning/checker/TaskWithSubtasksChecker.java b/python/educational-core/src/com/jetbrains/edu/learning/checker/TaskWithSubtasksChecker.java new file mode 100644 index 000000000000..f18b15fe2eff --- /dev/null +++ b/python/educational-core/src/com/jetbrains/edu/learning/checker/TaskWithSubtasksChecker.java @@ -0,0 +1,70 @@ +package com.jetbrains.edu.learning.checker; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.edu.learning.StudySubtaskUtils; +import com.jetbrains.edu.learning.core.EduNames; +import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; +import com.jetbrains.edu.learning.courseFormat.TaskFile; +import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +public class TaskWithSubtasksChecker extends StudyTaskChecker { + public TaskWithSubtasksChecker(@NotNull TaskWithSubtasks task, + @NotNull Project project) { + super(task, project); + } + + @Override + public void onTaskSolved(@NotNull String message) { + boolean hasMoreSubtasks = myTask.activeSubtaskNotLast(); + final int activeSubtaskIndex = myTask.getActiveSubtaskIndex(); + int visibleSubtaskIndex = activeSubtaskIndex + 1; + ApplicationManager.getApplication().invokeLater(() -> { + int subtaskSize = myTask.getLastSubtaskIndex() + 1; + String resultMessage = !hasMoreSubtasks ? message : "Subtask " + visibleSubtaskIndex + "/" + subtaskSize + " solved"; + StudyCheckUtils.showTestResultPopUp(resultMessage, MessageType.INFO.getPopupBackground(), myProject); + if (hasMoreSubtasks) { + int nextSubtaskIndex = activeSubtaskIndex + 1; + StudySubtaskUtils.switchStep(myProject, myTask, nextSubtaskIndex); + rememberAnswers(nextSubtaskIndex, myTask); + } + }); + } + + private void rememberAnswers(int nextSubtaskIndex, @NotNull TaskWithSubtasks task) { + VirtualFile taskDir = task.getTaskDir(myProject); + if (taskDir == null) { + return; + } + VirtualFile srcDir = taskDir.findChild(EduNames.SRC); + if (srcDir != null) { + taskDir = srcDir; + } + for (Map.Entry entry : task.getTaskFiles().entrySet()) { + TaskFile taskFile = entry.getValue(); + VirtualFile virtualFile = taskDir.findFileByRelativePath(entry.getKey()); + if (virtualFile == null) { + continue; + } + Document document = FileDocumentManager.getInstance().getDocument(virtualFile); + if (document == null) { + continue; + } + for (AnswerPlaceholder placeholder : taskFile.getActivePlaceholders()) { + if (placeholder.getSubtaskInfos().containsKey(nextSubtaskIndex - 1)) { + int offset = placeholder.getOffset(); + String answer = document.getText(TextRange.create(offset, offset + placeholder.getRealLength())); + placeholder.getSubtaskInfos().get(nextSubtaskIndex - 1).setAnswer(answer); + } + } + } + } +} diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/Lesson.java b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/Lesson.java index 18692467e3ee..4109a93be0d9 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/Lesson.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/Lesson.java @@ -28,7 +28,7 @@ public class Lesson implements StudyItem { @Expose @SerializedName("task_list") @AbstractCollection(elementTypes = { - Task.class, + PyCharmTask.class, ChoiceTask.class, TheoryTask.class, CodeTask.class, diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/RemoteCourse.java b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/RemoteCourse.java index 40bf6eb65643..dd0f7bb26c53 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/RemoteCourse.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/RemoteCourse.java @@ -9,6 +9,7 @@ import com.intellij.openapi.progress.Task.Backgroundable; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.stepic.EduStepicConnector; +import com.jetbrains.edu.learning.stepic.EduStepicNames; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -17,7 +18,8 @@ import java.util.List; public class RemoteCourse extends Course { //course type in format "pycharm " - @SerializedName("course_format") private String myType = "pycharm" + EduStepicConnector.CURRENT_VERSION + " " + getLanguageID(); + @SerializedName("course_format") private String myType = + String.format("%s%d %s", EduStepicNames.PYCHARM_PREFIX, EduStepicConnector.CURRENT_VERSION, getLanguageID()); @SerializedName("is_idea_compatible") private boolean isCompatible = true; List sections; List instructors = new ArrayList<>(); @@ -31,6 +33,11 @@ public class RemoteCourse extends Course { return myType; } + public void setLanguage(@NotNull final String language) { + super.setLanguage(language); + updateType(language); + } + public List getSections() { return sections; } @@ -107,8 +114,10 @@ public class RemoteCourse extends Course { public void copyCourseParameters(RemoteCourse course) { setName(course.getName()); - setUpdateDate(course.getUpdateDate()); + } + private void updateType(String language) { + myType = String.format("%s%d %s", EduStepicNames.PYCHARM_PREFIX, EduStepicConnector.CURRENT_VERSION, language); } } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/ChoiceTask.java b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/ChoiceTask.java index f7ce2ae9a162..476af7826116 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/ChoiceTask.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/ChoiceTask.java @@ -2,6 +2,11 @@ package com.jetbrains.edu.learning.courseFormat.tasks; import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; +import com.intellij.openapi.project.Project; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.checker.StudyTaskChecker; +import com.jetbrains.edu.learning.editor.StudyChoiceVariantsPanel; +import com.jetbrains.edu.learning.ui.StudyToolWindow; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -48,4 +53,22 @@ public class ChoiceTask extends Task { public String getTaskType() { return "choice"; } + + @Override + public StudyTaskChecker getChecker(@NotNull Project project) { + return new StudyTaskChecker(this, project) { + @Override + public void onTaskFailed(@NotNull String message) { + super.onTaskFailed(message); + repaintChoicePanel(project, myTask); + } + + private void repaintChoicePanel(@NotNull Project project, @NotNull ChoiceTask task) { + final StudyToolWindow toolWindow = StudyUtils.getStudyToolWindow(project); + if (toolWindow != null) { + toolWindow.setBottomComponent(new StudyChoiceVariantsPanel(task)); + } + } + }; + } } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/CodeTask.java b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/CodeTask.java index fb2d701cb6ac..b82c478c43d9 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/CodeTask.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/CodeTask.java @@ -1,5 +1,8 @@ package com.jetbrains.edu.learning.courseFormat.tasks; +import com.intellij.openapi.project.Project; +import com.jetbrains.edu.learning.checker.StudyCheckUtils; +import com.jetbrains.edu.learning.checker.StudyTaskChecker; import org.jetbrains.annotations.NotNull; public class CodeTask extends Task { @@ -14,4 +17,15 @@ public class CodeTask extends Task { public String getTaskType() { return "code"; } + + @Override + public StudyTaskChecker getChecker(@NotNull Project project) { + return new StudyTaskChecker(this, project) { + @Override + public void onTaskFailed(@NotNull String message) { + super.onTaskFailed("Wrong solution"); + StudyCheckUtils.showTestResultsToolWindow(myProject, message); + } + }; + } } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/PyCharmTask.java b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/PyCharmTask.java new file mode 100644 index 000000000000..7ec30c7bf90f --- /dev/null +++ b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/PyCharmTask.java @@ -0,0 +1,21 @@ +package com.jetbrains.edu.learning.courseFormat.tasks; + +import org.jetbrains.annotations.NotNull; + +/** + * Original PyCharm Edu tasks with local tests and answer placeholders + */ +public class PyCharmTask extends Task { + + public PyCharmTask() { + } + + public PyCharmTask(@NotNull String name) { + super(name); + } + + @Override + public String getTaskType() { + return "pycharm"; + } +} \ No newline at end of file diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/Task.java b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/Task.java index 947b2532a4d7..4bfc083c656e 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/Task.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/Task.java @@ -9,6 +9,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.xmlb.XmlSerializer; import com.intellij.util.xmlb.annotations.Transient; +import com.jetbrains.edu.learning.checker.StudyTaskChecker; import com.jetbrains.edu.learning.EduPluginConfigurator; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.*; @@ -29,7 +30,7 @@ import java.util.Map; * - go to Lesson and update elementTypes in taskList AbstractCollection. Needed for proper xml serialization * - Update TaskSerializer and TaskDeserializer in StudySerializationUtil to handle json serialization */ -public class Task implements StudyItem { +public abstract class Task implements StudyItem { @Expose private String name; // index is visible to user number of task from 1 to task number @@ -268,7 +269,9 @@ public class Task implements StudyItem { } // used in json serialization/deserialization - public String getTaskType() { - return "pycharm"; + public abstract String getTaskType(); + + public StudyTaskChecker getChecker(@NotNull Project project) { + return new StudyTaskChecker<>(this, project); } } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/TaskWithSubtasks.java b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/TaskWithSubtasks.java index ac10657b829c..6146fb3e9df7 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/TaskWithSubtasks.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/courseFormat/tasks/TaskWithSubtasks.java @@ -2,6 +2,9 @@ package com.jetbrains.edu.learning.courseFormat.tasks; import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; +import com.intellij.openapi.project.Project; +import com.jetbrains.edu.learning.checker.StudyTaskChecker; +import com.jetbrains.edu.learning.checker.TaskWithSubtasksChecker; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.learning.courseFormat.StudyStatus; import com.jetbrains.edu.learning.courseFormat.TaskFile; @@ -58,4 +61,9 @@ public class TaskWithSubtasks extends Task { public String getTaskType() { return "subtasks"; } + + @Override + public StudyTaskChecker getChecker(@NotNull Project project) { + return new TaskWithSubtasksChecker(this, project); + } } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java b/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java index dad0e77a7d64..1fed74118ab7 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java @@ -73,7 +73,7 @@ public class StudyGenerator { createChildFile(taskDir, name, taskFile.text); } - public static void createDescriptions(VirtualFile taskDir, Task task) throws IOException { + private static void createDescriptions(VirtualFile taskDir, Task task) throws IOException { final Map texts = task.getTaskTexts(); for (Map.Entry entry : texts.entrySet()) { final String name = entry.getKey(); @@ -82,7 +82,7 @@ public class StudyGenerator { } } - public static void createTestFiles(VirtualFile taskDir, Task task) throws IOException { + private static void createTestFiles(VirtualFile taskDir, Task task) throws IOException { final Map tests = task.getTestsText(); for (Map.Entry entry : tests.entrySet()) { final String name = entry.getKey(); diff --git a/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java b/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java index b294e5d29dad..6b25be65eef2 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java @@ -144,7 +144,7 @@ public class StudyProjectGenerator { } @NotNull - public List getCoursesUnderProgress(boolean force, @NotNull final String progressTitle, @NotNull final Project project) { + public List getCoursesUnderProgress(boolean force, @NotNull final String progressTitle, @Nullable final Project project) { try { return ProgressManager.getInstance() .runProcessWithProgressSynchronously(() -> { diff --git a/python/educational-core/src/com/jetbrains/edu/learning/intellij/EduIntelliJTemplateFactory.java b/python/educational-core/src/com/jetbrains/edu/learning/intellij/EduIntelliJTemplateFactory.java index 48e9ce41923b..19535068e1c1 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/intellij/EduIntelliJTemplateFactory.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/intellij/EduIntelliJTemplateFactory.java @@ -3,16 +3,35 @@ package com.jetbrains.edu.learning.intellij; import com.intellij.icons.AllIcons; import com.intellij.ide.util.projectWizard.WizardContext; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; import com.intellij.platform.ProjectTemplate; import com.intellij.platform.ProjectTemplatesFactory; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; +import com.jetbrains.edu.learning.intellij.stepik.EduRemoteCourseTemplate; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; @SuppressWarnings("unused") //used in other educational plugins that are stored in separate repository public class EduIntelliJTemplateFactory extends ProjectTemplatesFactory { private static final String GROUP_NAME = "Education"; + private static List ourRemoteCourses; + + static { + ProgressManager.getInstance().run(new Task.Backgroundable(null, "Updating Course") { + @Override + public void run(@NotNull ProgressIndicator indicator) { + ourRemoteCourses = new StudyProjectGenerator().getCourses(true); + } + }); + } @NotNull @Override @@ -23,7 +42,14 @@ public class EduIntelliJTemplateFactory extends ProjectTemplatesFactory { @NotNull @Override public ProjectTemplate[] createTemplates(@Nullable String group, WizardContext context) { - return ApplicationManager.getApplication().getExtensions(EduIntelliJProjectTemplate.EP_NAME); + final ArrayList templates = new ArrayList<>(); + if (ourRemoteCourses != null) { + for (Course course : ourRemoteCourses) { + templates.add(new EduRemoteCourseTemplate(course)); + } + } + Collections.addAll(templates, ApplicationManager.getApplication().getExtensions(EduIntelliJProjectTemplate.EP_NAME)); + return templates.toArray(new ProjectTemplate[templates.size()]); } @Override diff --git a/python/educational-core/src/com/jetbrains/edu/learning/intellij/stepik/EduRemoteCourseModuleBuilder.java b/python/educational-core/src/com/jetbrains/edu/learning/intellij/stepik/EduRemoteCourseModuleBuilder.java new file mode 100644 index 000000000000..9aa0282e7985 --- /dev/null +++ b/python/educational-core/src/com/jetbrains/edu/learning/intellij/stepik/EduRemoteCourseModuleBuilder.java @@ -0,0 +1,90 @@ +package com.jetbrains.edu.learning.intellij.stepik; + +import com.intellij.ide.util.projectWizard.JavaModuleBuilder; +import com.intellij.ide.util.projectWizard.ModuleWizardStep; +import com.intellij.ide.util.projectWizard.ProjectWizardStepFactory; +import com.intellij.ide.util.projectWizard.SettingsStep; +import com.intellij.lang.Language; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.module.ModifiableModuleModel; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleWithNameAlreadyExists; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Conditions; +import com.intellij.openapi.util.InvalidDataException; +import com.jetbrains.edu.learning.EduPluginConfigurator; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.intellij.generation.EduProjectGenerator; +import org.jdom.JDOMException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; + +class EduRemoteCourseModuleBuilder extends JavaModuleBuilder { + private static final Logger LOG = Logger.getInstance(EduRemoteCourseModuleBuilder.class); + private final Course myCourse; + private EduProjectGenerator myGenerator = new EduProjectGenerator(); + + public EduRemoteCourseModuleBuilder(Course course) { + myCourse = course; + } + + @Nullable + @Override + public Module commitModule(@NotNull Project project, @Nullable ModifiableModuleModel model) { + Module module = super.commitModule(project, model); + if (module == null) { + return null; + } + String languageName = myCourse.getLanguageID(); + Language language = Language.findLanguageByID(languageName); + if (language == null) { + return module; + } + EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(language); + if (configurator == null) { + return module; + } + configurator.configureModule(module); + return module; + } + + @Nullable + @Override + public String getBuilderId() { + return "remote.course.builder." + myCourse.getName(); + } + + @Nullable + @Override + public ModuleWizardStep modifySettingsStep(@NotNull SettingsStep settingsStep) { + return ProjectWizardStepFactory.getInstance().createJavaSettingsStep(settingsStep, this, Conditions.alwaysTrue()); + } + + @NotNull + @Override + public Module createModule(@NotNull ModifiableModuleModel moduleModel) + throws InvalidDataException, IOException, ModuleWithNameAlreadyExists, JDOMException, ConfigurationException { + Module baseModule = super.createModule(moduleModel); + String languageName = myCourse.getLanguageID(); + Language language = Language.findLanguageByID(languageName); + if (language != null) { + EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(language); + if (configurator != null) { + Project project = baseModule.getProject(); + myGenerator.setSelectedCourse(myCourse); + myGenerator.generateProject(project, project.getBaseDir()); + Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null) { + LOG.info("failed to generate course"); + return baseModule; + } + configurator.createCourseModuleContent(moduleModel, project, course, getModuleFileDirectory()); + } + } + return baseModule; + } +} diff --git a/python/educational-core/src/com/jetbrains/edu/learning/intellij/stepik/EduRemoteCourseTemplate.java b/python/educational-core/src/com/jetbrains/edu/learning/intellij/stepik/EduRemoteCourseTemplate.java new file mode 100644 index 000000000000..b3f1bd994e10 --- /dev/null +++ b/python/educational-core/src/com/jetbrains/edu/learning/intellij/stepik/EduRemoteCourseTemplate.java @@ -0,0 +1,48 @@ +package com.jetbrains.edu.learning.intellij.stepik; + +import com.intellij.ide.util.projectWizard.AbstractModuleBuilder; +import com.intellij.openapi.ui.ValidationInfo; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.intellij.EduIntelliJProjectTemplate; +import icons.EducationalCoreIcons; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +public class EduRemoteCourseTemplate implements EduIntelliJProjectTemplate { + private final Course myCourse; + + public EduRemoteCourseTemplate(Course course) { + myCourse = course; + } + + @NotNull + @Override + public String getName() { + return myCourse.getName(); + } + + @Nullable + @Override + public String getDescription() { + return myCourse.getDescription(); + } + + @Override + public Icon getIcon() { + return EducationalCoreIcons.Stepik; + } + + @NotNull + @Override + public AbstractModuleBuilder createModuleBuilder() { + return new EduRemoteCourseModuleBuilder(myCourse); + } + + @Nullable + @Override + public ValidationInfo validateSettings() { + return null; + } +} diff --git a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java index 755ea42119ec..d1da82314213 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java @@ -7,20 +7,19 @@ import com.intellij.ide.projectView.ProjectView; import com.intellij.lang.Language; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.jetbrains.edu.learning.StudySettings; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.StudyUtils; -import com.jetbrains.edu.learning.checker.StudyExecutor; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.*; import com.jetbrains.edu.learning.courseFormat.tasks.ChoiceTask; @@ -28,7 +27,6 @@ import com.jetbrains.edu.learning.courseFormat.tasks.CodeTask; import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.courseFormat.tasks.TheoryTask; import com.jetbrains.edu.learning.courseGeneration.StudyGenerator; -import com.jetbrains.edu.learning.editor.StudyEditor; import com.jetbrains.edu.learning.navigation.StudyNavigator; import com.jetbrains.edu.learning.ui.StudyToolWindow; import org.apache.http.HttpEntity; @@ -49,7 +47,10 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; import java.util.concurrent.TimeUnit; import static com.jetbrains.edu.learning.stepic.EduStepicConnector.getStep; @@ -146,7 +147,7 @@ public class EduAdaptiveStepicConnector { else if (stepType.equals(CHOICE_TYPE_TEXT)) { return getChoiceTaskFromStep(name, step.block, stepId, userId); } - else if (stepType.startsWith(EduStepicConnector.PYCHARM_PREFIX)) { + else if (stepType.startsWith(EduStepicNames.PYCHARM_PREFIX)) { return EduStepicConnector.createTask(stepId); } else if (stepType.equals(TEXT_STEP_TYPE)) { @@ -280,12 +281,12 @@ public class EduAdaptiveStepicConnector { } } - public static void addNextRecommendedTask(@NotNull Project project, @NotNull ProgressIndicator indicator, int reactionToPost) { - final StudyEditor editor = StudyUtils.getSelectedStudyEditor(project); + public static void addNextRecommendedTask(@NotNull Project project, + @NotNull Lesson lesson, + @NotNull ProgressIndicator indicator, + int reactionToPost) { final Course course = StudyTaskManager.getInstance(project).getCourse(); - Task selectedTask = StudyUtils.getTaskFromSelectedEditor(project); - if (editor == null || editor.getTaskFile() == null || selectedTask == null || selectedTask.getLesson() == null || - !(course instanceof RemoteCourse)) { + if (!(course instanceof RemoteCourse)) { LOG.warn("Course is in incorrect state"); ApplicationManager.getApplication().invokeLater(() -> StudyUtils.showErrorPopupOnToolbar(project, "Can't get next recommendation: course is broken")); @@ -301,7 +302,6 @@ public class EduAdaptiveStepicConnector { return; } - final Lesson lesson = selectedTask.getLesson(); final boolean reactionPosted = postRecommendationReaction(String.valueOf(lesson.getId()), String.valueOf(user.getId()), reactionToPost); if (!reactionPosted) { LOG.warn("Recommendation reaction wasn't posted"); @@ -320,7 +320,7 @@ public class EduAdaptiveStepicConnector { task.initTask(lesson, false); boolean replaceCurrentTask = reactionToPost == TOO_HARD_RECOMMENDATION_REACTION || reactionToPost == TOO_BORING_RECOMMENDATION_REACTION; if (replaceCurrentTask) { - replaceCurrentTask(project, editor, task, lesson); + replaceCurrentTask(project, task, lesson); } else { addAsNextTask(project, task, lesson); @@ -361,7 +361,7 @@ public class EduAdaptiveStepicConnector { } } - private static void replaceCurrentTask(@NotNull Project project, @NotNull StudyEditor editor, @NotNull Task task, @NotNull Lesson lesson) { + private static void replaceCurrentTask(@NotNull Project project, @NotNull Task task, @NotNull Lesson lesson) { Course course = StudyTaskManager.getInstance(project).getCourse(); assert course != null; @@ -370,8 +370,6 @@ public class EduAdaptiveStepicConnector { task.setIndex(taskIndex); lesson.getTaskList().set(taskIndex - 1, task); - copyTaskFileParameters(editor, task); - final String lessonName = EduNames.LESSON + lesson.getIndex(); updateProjectFiles(project, task, lessonName); setToolWindowText(project, task); @@ -380,12 +378,10 @@ public class EduAdaptiveStepicConnector { private static void updateProjectFiles(@NotNull Project project, @NotNull Task task, @NotNull String lessonName) { final VirtualFile lessonDir = project.getBaseDir().findChild(lessonName); if (lessonDir != null) { - final VirtualFile taskResourceRoot = lessonDir.findChild(EduNames.TASK + task.getIndex()); ApplicationManager.getApplication().invokeLater(() -> ApplicationManager.getApplication().runWriteAction(() -> { try { - removeOldProjectFiles(project, taskResourceRoot); - StudyGenerator.createTestFiles(taskResourceRoot, task); - StudyGenerator.createDescriptions(taskResourceRoot, task); + removeOldProjectFiles(lessonDir, task.getIndex()); + StudyGenerator.createTask(task, lessonDir); } catch (IOException e) { LOG.warn(e.getMessage()); @@ -394,46 +390,14 @@ public class EduAdaptiveStepicConnector { } } - private static void removeOldProjectFiles(@NotNull Project project, @Nullable VirtualFile taskRecourseRoot) throws IOException { - if (taskRecourseRoot == null) { + private static void removeOldProjectFiles(@NotNull VirtualFile lessonDir, int taskIndex) throws IOException { + final VirtualFile taskDir = lessonDir.findChild(EduNames.TASK + taskIndex); + if (taskDir == null) { LOG.warn("Failed to update files for a new recommendation: task directory is null"); return; } - VirtualFile[] children = VfsUtil.getChildren(taskRecourseRoot); - for (VirtualFile file : children) { - String name = file.getName(); - if (StudyUtils.isTestsFile(project, name) || StudyUtils.isTaskDescriptionFile(name)) { - file.delete(taskRecourseRoot); - } - } - } - - private static void copyTaskFileParameters(@NotNull StudyEditor editor, @NotNull Task task) { - final Map taskFiles = task.getTaskFiles(); - if (taskFiles.size() == 1) { - TaskFile newTaskFile = (TaskFile)taskFiles.values().toArray()[0]; - setTaskFileParameters(editor, task, newTaskFile); - updateEditorText(editor, newTaskFile); - } - else { - LOG.warn("Got task without unexpected number of task files: " + taskFiles.size()); - } - } - private static void updateEditorText(@NotNull StudyEditor editor, @NotNull TaskFile newTaskFile) { - ApplicationManager.getApplication().invokeLater(() -> ApplicationManager.getApplication().runWriteAction(() -> { - final Document document = editor.getEditor().getDocument(); - document.setText(newTaskFile.text); - })); - } - - private static void setTaskFileParameters(@NotNull StudyEditor editor, @NotNull Task task, @NotNull TaskFile newTaskFile) { - TaskFile currentTaskFile = editor.getTaskFile(); - currentTaskFile.text = newTaskFile.text; - currentTaskFile.name = newTaskFile.name; - currentTaskFile.setTask(task); - task.getTaskFiles().clear(); - task.taskFiles.put(CODE_TASK_NAME, currentTaskFile); + taskDir.delete(EduAdaptiveStepicConnector.class); } private static void setToolWindowText(@NotNull Project project, @NotNull Task task) { @@ -491,7 +455,7 @@ public class EduAdaptiveStepicConnector { } } else { - final String templateForTask = getCodeTemplateForTask(project, task, step.options.codeTemplates); + final String templateForTask = getCodeTemplateForTask(project, step.options.codeTemplates); String text = templateForTask == null ? "# write your answer here \n" : templateForTask; createMockTaskFile(task, text); } @@ -499,10 +463,9 @@ public class EduAdaptiveStepicConnector { } private static String getCodeTemplateForTask(@NotNull Project project, - @NotNull Task task, @Nullable StepicWrappers.CodeTemplatesWrapper codeTemplates) { if (codeTemplates != null) { - final String languageString = getLanguageString(task, project); + final String languageString = getLanguageString(project); if (languageString != null) { return codeTemplates.getTemplateForLanguage(languageString); } @@ -658,12 +621,12 @@ public class EduAdaptiveStepicConnector { } @Nullable - private static String getLanguageString(@NotNull Task task, @NotNull Project project) { + private static String getLanguageString(@NotNull Project project) { final Language pythonLanguage = Language.findLanguageByID("Python"); if (pythonLanguage != null) { - final Sdk language = StudyExecutor.INSTANCE.forLanguage(pythonLanguage).findSdk(project); - if (language != null) { - final String versionString = language.getVersionString(); + Sdk sdk = ModuleRootManager.getInstance(ModuleManager.getInstance(project).getModules()[0]).getSdk(); + if (sdk != null) { + final String versionString = sdk.getVersionString(); if (versionString != null) { final List versionStringParts = StringUtil.split(versionString, " "); if (versionStringParts.size() == 2) { @@ -671,9 +634,6 @@ public class EduAdaptiveStepicConnector { } } } - else { - StudyUtils.showNoSdkNotification(task, project); - } } return null; } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java index a7ead47048fc..5ae616d81654 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java @@ -2,16 +2,20 @@ package com.jetbrains.edu.learning.stepic; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.intellij.lang.LanguageExtensionPoint; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.edu.learning.EduPluginConfigurator; import com.jetbrains.edu.learning.StudySettings; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.*; +import com.jetbrains.edu.learning.courseFormat.tasks.PyCharmTask; import com.jetbrains.edu.learning.courseFormat.tasks.Task; import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks; import org.apache.http.HttpEntity; @@ -32,12 +36,13 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.*; +import static com.jetbrains.edu.learning.stepic.EduStepicNames.PYCHARM_PREFIX; + public class EduStepicConnector { private static final Logger LOG = Logger.getInstance(EduStepicConnector.class.getName()); public static final int CURRENT_VERSION = 2; //this prefix indicates that course can be opened by educational plugin - public static final String PYCHARM_PREFIX = "pycharm"; private static final String ADAPTIVE_NOTE = "\n\nInitially, the adaptive system may behave somewhat randomly, but the more problems you solve, the smarter it becomes!"; @@ -147,6 +152,8 @@ public class EduStepicConnector { final List courses = coursesContainer.courses; for (RemoteCourse info : courses) { if (!info.isAdaptive() && StringUtil.isEmptyOrSpaces(info.getType())) continue; + setCourseLanguage(info); + if (canBeOpened(info)) { final ArrayList authors = new ArrayList<>(); for (Integer instructor : info.getInstructors()) { @@ -164,13 +171,34 @@ public class EduStepicConnector { } } - static boolean canBeOpened(RemoteCourse courseInfo) { - if (courseInfo.isAdaptive()) { - return true; + private static void setCourseLanguage(RemoteCourse info) { + if (info.isAdaptive()) { + info.setLanguage("Python"); // adaptive courses available only in PyCharm now + return; } + String courseType = info.getType(); + final int separator = courseType.indexOf(" "); + assert separator != -1; + final String language = courseType.substring(separator + 1); + info.setLanguage(language); + } + + static boolean canBeOpened(RemoteCourse courseInfo) { + final ArrayList supportedLanguages = new ArrayList<>(); + final LanguageExtensionPoint[] extensions = Extensions.getExtensions(EduPluginConfigurator.EP_NAME, null); + for (LanguageExtensionPoint extension : extensions) { + String languageId = extension.getKey(); + supportedLanguages.add(languageId); + } + + if (courseInfo.isAdaptive()) { + return supportedLanguages.contains(courseInfo.getLanguageID()); + } + String courseType = courseInfo.getType(); final List typeLanguage = StringUtil.split(courseType, " "); String prefix = typeLanguage.get(0); + if (!supportedLanguages.contains(courseInfo.getLanguageID())) return false; if (typeLanguage.size() < 2 || !prefix.startsWith(PYCHARM_PREFIX)) { return false; } @@ -188,14 +216,8 @@ public class EduStepicConnector { } } - public static RemoteCourse getCourse(@NotNull final Project project, @NotNull final RemoteCourse course) { - final RemoteCourse remoteCourse = (RemoteCourse)course.copy(); + public static RemoteCourse getCourse(@NotNull final Project project, @NotNull final RemoteCourse remoteCourse) { if (!remoteCourse.isAdaptive()) { - String courseType = remoteCourse.getType(); - final int separator = courseType.indexOf(" "); - assert separator != -1; - final String language = courseType.substring(separator + 1); - remoteCourse.setLanguage(language); try { for (Integer section : remoteCourse.getSections()) { remoteCourse.addLessons(getLessons(section)); @@ -267,7 +289,7 @@ public class EduStepicConnector { return null; } final int lastSubtaskIndex = block.options.lastSubtaskIndex; - Task task = new Task(); + Task task = new PyCharmTask(); if (lastSubtaskIndex != 0) { task = createTaskWithSubtasks(lastSubtaskIndex); } @@ -301,6 +323,9 @@ public class EduStepicConnector { final List placeholders = file.getAnswerPlaceholders(); for (AnswerPlaceholder placeholder : placeholders) { final AnswerPlaceholderSubtaskInfo info = placeholder.getActiveSubtaskInfo(); + if (info == null) { + continue; + } final int offset = placeholder.getOffset(); final int length = placeholder.getLength(); if (fileText.length() > offset + length) { diff --git a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicNames.java b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicNames.java index b25e80fcea34..5ee99dc6cde8 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicNames.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/stepic/EduStepicNames.java @@ -21,4 +21,6 @@ public class EduStepicNames { public static final String SECTIONS = "/sections/"; public static final String ENROLLMENTS = "/enrollments"; public static final String STEPIC_REGISTRATION_LINK = "https://stepic.org/registration?next=%2Fexplore%2Fcourses"; + + public static final String PYCHARM_PREFIX = "pycharm"; } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/stepic/StepicAdaptiveReactionsPanel.java b/python/educational-core/src/com/jetbrains/edu/learning/stepic/StepicAdaptiveReactionsPanel.java index 6f68839fd936..8aabf57e7144 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/stepic/StepicAdaptiveReactionsPanel.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/stepic/StepicAdaptiveReactionsPanel.java @@ -148,7 +148,8 @@ public class StepicAdaptiveReactionsPanel extends JPanel { public void run(@NotNull ProgressIndicator indicator) { StepicAdaptiveReactionsPanel.this.setEnabledRecursive(false); ApplicationManager.getApplication().invokeLater(()->setBackground(UIUtil.getLabelBackground())); - EduAdaptiveStepicConnector.addNextRecommendedTask(StepicAdaptiveReactionsPanel.this.myProject, indicator, myReaction); + EduAdaptiveStepicConnector.addNextRecommendedTask(StepicAdaptiveReactionsPanel.this.myProject, task.getLesson(), indicator, + myReaction); StepicAdaptiveReactionsPanel.this.setEnabledRecursive(true); } }); diff --git a/python/educational-core/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java b/python/educational-core/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java index e7d1eefcdd91..5124acab9833 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java @@ -194,6 +194,7 @@ public class StepicWrappers { public CourseWrapper(Course course) { this.course = new RemoteCourse(); this.course.setName(course.getName()); + this.course.setLanguage(course.getLanguageID()); this.course.setDescription(course.getDescription()); this.course.setAuthors(course.getAuthors()); } diff --git a/python/educational-core/src/com/jetbrains/edu/learning/twitter/StudyTwitterAction.java b/python/educational-core/src/com/jetbrains/edu/learning/twitter/StudyTwitterAction.java index a1c57b92583d..360c0eba54bd 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/twitter/StudyTwitterAction.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/twitter/StudyTwitterAction.java @@ -2,7 +2,7 @@ package com.jetbrains.edu.learning.twitter; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; -import com.jetbrains.edu.learning.StudyCheckListener; +import com.jetbrains.edu.learning.checker.StudyCheckListener; import com.jetbrains.edu.learning.StudyTwitterPluginConfigurator; import com.jetbrains.edu.learning.courseFormat.StudyStatus; import com.jetbrains.edu.learning.courseFormat.tasks.Task; diff --git a/python/educational-core/src/icons/EducationalCoreIcons.java b/python/educational-core/src/icons/EducationalCoreIcons.java index c089db48d76c..73c105a44024 100644 --- a/python/educational-core/src/icons/EducationalCoreIcons.java +++ b/python/educational-core/src/icons/EducationalCoreIcons.java @@ -22,6 +22,7 @@ public class EducationalCoreIcons { public static final Icon ResetTaskFile = load("/icons/com/jetbrains/edu/learning/ResetTaskFile.png"); // 16x16 public static final Icon Sandbox = load("/icons/com/jetbrains/edu/learning/Sandbox.png"); // 16x16 public static final Icon ShowHint = load("/icons/com/jetbrains/edu/learning/showHint.png"); // 16x16 + public static final Icon Stepik = load("/icons/com/jetbrains/edu/learning/Stepik.png"); // 16x16 public static final Icon Task = load("/icons/com/jetbrains/edu/learning/Task.png"); // 16x16 public static final Icon TaskCompl = load("/icons/com/jetbrains/edu/learning/TaskCompl.png"); // 16x16 public static final Icon TaskDescription = load("/icons/com/jetbrains/edu/learning/TaskDescription.png"); // 13x13 diff --git a/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java b/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java index 5be02916f2fe..051964560cc6 100644 --- a/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java +++ b/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java @@ -19,6 +19,7 @@ import com.intellij.testFramework.fixtures.CodeInsightFixtureTestCase; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.courseFormat.*; +import com.jetbrains.edu.learning.courseFormat.tasks.PyCharmTask; import com.jetbrains.edu.learning.courseFormat.tasks.Task; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -85,7 +86,7 @@ public abstract class CCTestCase extends CodeInsightFixtureTestCase { Lesson lesson = new Lesson(); lesson.setName("lesson1"); - Task task = new Task(); + Task task = new PyCharmTask(); task.setName("task1"); task.setIndex(1); lesson.addTask(task); diff --git a/python/educational-python/Edu-Python/resources/META-INF/plugin.xml b/python/educational-python/Edu-Python/resources/META-INF/plugin.xml index 35419980703f..1d4b1055bfba 100644 --- a/python/educational-python/Edu-Python/resources/META-INF/plugin.xml +++ b/python/educational-python/Edu-Python/resources/META-INF/plugin.xml @@ -35,7 +35,6 @@ - diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java index 264596c26b46..1948c1ce49a8 100644 --- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java +++ b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java @@ -6,7 +6,6 @@ import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.IdeFocusManager; import com.jetbrains.edu.learning.actions.StudyCheckAction; @@ -15,14 +14,10 @@ import com.jetbrains.edu.learning.checker.StudyCheckUtils; import com.jetbrains.edu.learning.checker.StudyTestRunner; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.StudyStatus; import com.jetbrains.edu.learning.courseFormat.TaskFile; -import com.jetbrains.edu.learning.courseFormat.tasks.ChoiceTask; import com.jetbrains.edu.learning.courseFormat.tasks.Task; -import com.jetbrains.edu.learning.editor.StudyChoiceVariantsPanel; import com.jetbrains.edu.learning.editor.StudyEditor; import com.jetbrains.edu.learning.statistics.EduUsagesCollector; -import com.jetbrains.edu.learning.ui.StudyToolWindow; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -52,7 +47,7 @@ public class PyStudyCheckAction extends StudyCheckAction { ApplicationManager.getApplication().invokeLater( () -> IdeFocusManager.getInstance(project).requestFocus(studyState.getEditor().getComponent(), true)); - final StudyTestRunner testRunner = StudyUtils.getTestRunner(task, taskDir); + final StudyTestRunner testRunner = new PyStudyTestRunner(task, taskDir); Process testProcess = null; String commandLine = ""; try { @@ -83,9 +78,9 @@ public class PyStudyCheckAction extends StudyCheckAction { return new StudyCheckTask(project, studyState, myCheckInProgress, testProcess, commandLine) { @Override protected void onTaskFailed(@NotNull String message) { + super.onTaskFailed(message); ApplicationManager.getApplication().invokeLater(() -> { if (myTaskDir == null) return; - myTask.setStatus(StudyStatus.Failed); for (Map.Entry entry : myTask.getTaskFiles().entrySet()) { final String name = entry.getKey(); final TaskFile taskFile = entry.getValue(); @@ -99,38 +94,12 @@ public class PyStudyCheckAction extends StudyCheckAction { () -> StudyCheckUtils.runSmartTestProcess(myTaskDir, testRunner, name, taskFile, project))); } } - final StudyToolWindow toolWindow = StudyUtils.getStudyToolWindow(project); - if (toolWindow != null) { - final Course course = StudyTaskManager.getInstance(project).getCourse(); - if (course != null) { - if (course.isAdaptive()) { - if (myTask instanceof ChoiceTask) { - StudyCheckUtils.showTestResultPopUp("Wrong answer", MessageType.ERROR.getPopupBackground(), project); - repaintChoicePanel(project, (ChoiceTask)myTask); - } - else { - StudyCheckUtils.showTestResultPopUp("Wrong answer", MessageType.ERROR.getPopupBackground(), project); - StudyCheckUtils.showTestResultsToolWindow(project, message, false); - } - } - else { - StudyCheckUtils.showTestResultPopUp(message, MessageType.ERROR.getPopupBackground(), project); - } - } - StudyCheckUtils.navigateToFailedPlaceholder(myStudyState, myTask, myTaskDir, project); - } + StudyCheckUtils.navigateToFailedPlaceholder(myStudyState, myTask, myTaskDir, project); }); } }; } - private static void repaintChoicePanel(@NotNull Project project, @NotNull ChoiceTask task) { - final StudyToolWindow toolWindow = StudyUtils.getStudyToolWindow(project); - if (toolWindow != null) { - toolWindow.setBottomComponent(new StudyChoiceVariantsPanel(task)); - } - } - @Nullable private static VirtualFile getTaskVirtualFile(@NotNull final StudyState studyState, @NotNull final Task task, diff --git a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyExecutor.java b/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyExecutor.java deleted file mode 100644 index 0bd87804ab94..000000000000 --- a/python/educational-python/Edu-Python/src/com/jetbrains/edu/learning/PyStudyExecutor.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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.jetbrains.edu.learning; - -import com.intellij.execution.RunContentExecutor; -import com.intellij.execution.configurations.GeneralCommandLine; -import com.intellij.execution.process.ProcessHandler; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.module.ModuleManager; -import com.intellij.openapi.options.ShowSettingsUtil; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.openapi.ui.MessageType; -import com.intellij.openapi.ui.popup.Balloon; -import com.intellij.openapi.ui.popup.BalloonBuilder; -import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.openapi.vfs.VirtualFile; -import com.jetbrains.edu.learning.courseFormat.tasks.Task; -import com.jetbrains.edu.learning.checker.StudyExecutor; -import com.jetbrains.edu.learning.checker.StudyTestRunner; -import com.jetbrains.edu.learning.courseFormat.UserTest; -import com.jetbrains.python.run.PythonTracebackFilter; -import com.jetbrains.python.sdk.PythonSdkType; -import org.jetbrains.annotations.NotNull; - -import javax.swing.event.HyperlinkEvent; -import java.io.File; -import java.util.List; - -public class PyStudyExecutor implements StudyExecutor { - - @Override - public Sdk findSdk(@NotNull final Project project) { - return PythonSdkType.findPythonSdk(ModuleManager.getInstance(project).getModules()[0]); - } - - @Override - public StudyTestRunner getTestRunner(@NotNull final Task task, @NotNull final VirtualFile taskDir) { - return new PyStudyTestRunner(task, taskDir); - } - - @Override - public RunContentExecutor getExecutor(@NotNull final Project project, @NotNull final ProcessHandler handler) { - return new RunContentExecutor(project, handler).withFilter(new PythonTracebackFilter(project)); - } - - @Override - public void setCommandLineParameters(@NotNull final GeneralCommandLine cmd, - @NotNull final Project project, - @NotNull final String filePath, - @NotNull final String sdkPath, - @NotNull final Task currentTask) { - final List userTests = StudyTaskManager.getInstance(project).getUserTests(currentTask); - if (!userTests.isEmpty()) { - EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(currentTask.getLesson().getCourse().getLanguageById()); - if (configurator != null) { - cmd.addParameter(new File(project.getBaseDir().getPath(), "user_tester.py").getPath()); - cmd.addParameter(sdkPath); - cmd.addParameter(filePath); - } - } - else { - cmd.addParameter(filePath); - } - } - - public void showNoSdkNotification(@NotNull final Project project) { - final String text = "No Python interpreter configured for the project
Configure interpreter"; - final BalloonBuilder balloonBuilder = JBPopupFactory.getInstance(). - createHtmlTextBalloonBuilder(text, null, - MessageType.WARNING.getPopupBackground(), - event -> { - if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { - ApplicationManager.getApplication() - .invokeLater( - () -> ShowSettingsUtil.getInstance().showSettingsDialog(project, "Project Interpreter")); - } - }); - balloonBuilder.setHideOnLinkClick(true); - final Balloon balloon = balloonBuilder.createBalloon(); - StudyUtils.showCheckPopUp(project, balloon); - } -} diff --git a/python/educational-python/resources/fileTemplates/internal/test_helper.py.ft b/python/educational-python/resources/fileTemplates/internal/test_helper.py.ft index 42bdaa245c70..16ba2d37f90c 100644 --- a/python/educational-python/resources/fileTemplates/internal/test_helper.py.ft +++ b/python/educational-python/resources/fileTemplates/internal/test_helper.py.ft @@ -218,13 +218,6 @@ def check_samples(samples=()): passed() -def do_not_run_on_check(): - """ - Do not execute task file on check - """ - print("#educational_plugin DO_NOT_RUN_ON_CHECK") - - def run_common_tests(error_text="Please, reload file and try again"): test_is_not_empty() test_answer_placeholders_text_deleted() diff --git a/python/helpers/pydev/_pydev_bundle/pydev_ipython_console_011.py b/python/helpers/pydev/_pydev_bundle/pydev_ipython_console_011.py index 7745c7b60549..b7705c6f680d 100644 --- a/python/helpers/pydev/_pydev_bundle/pydev_ipython_console_011.py +++ b/python/helpers/pydev/_pydev_bundle/pydev_ipython_console_011.py @@ -313,16 +313,17 @@ class _PyDevFrontEnd: version = release.__version__ def __init__(self, show_banner=True): - # Create and initialize our IPython instance. - self.ipython = PyDevTerminalInteractiveShell.instance() + if hasattr(PyDevTerminalInteractiveShell, '_instance') and PyDevTerminalInteractiveShell._instance is not None: + self.ipython = PyDevTerminalInteractiveShell._instance + else: + self.ipython = PyDevTerminalInteractiveShell.instance() if show_banner: # Display the IPython banner, this has version info and # help info self.ipython.show_banner() - self._curr_exec_line = 0 self._curr_exec_lines = []