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 = []