diff --git a/python/educational-core/course-creator/resources/META-INF/plugin.xml b/python/educational-core/course-creator/resources/META-INF/plugin.xml
index cf6708adafea..ca47bf362d37 100644
--- a/python/educational-core/course-creator/resources/META-INF/plugin.xml
+++ b/python/educational-core/course-creator/resources/META-INF/plugin.xml
@@ -23,6 +23,8 @@
+
@@ -83,4 +85,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java
index 70fa712c1c0d..d05530f84a6e 100644
--- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java
+++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java
@@ -36,6 +36,7 @@ public class CCVirtualFileListener extends VirtualFileAdapter {
String name = createdFile.getName();
if (CCUtils.isTestsFile(project, createdFile)
|| EduNames.TASK_HTML.equals(name)
+ || EduNames.TASK_MD.equals(name)
|| name.contains(EduNames.WINDOW_POSTFIX)
|| name.contains(EduNames.WINDOWS_POSTFIX)
|| name.contains(EduNames.ANSWERS_POSTFIX)) {
diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTask.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTask.java
index 0f582614e969..2596d8cacd52 100644
--- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTask.java
+++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTask.java
@@ -16,6 +16,7 @@ import com.intellij.psi.PsiElement;
import com.intellij.util.Function;
import com.jetbrains.edu.coursecreator.CCLanguageManager;
import com.jetbrains.edu.coursecreator.CCUtils;
+import com.jetbrains.edu.coursecreator.settings.CCSettings;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.core.EduUtils;
import com.jetbrains.edu.learning.courseFormat.Course;
@@ -99,7 +100,7 @@ public class CCCreateTask extends CCCreateStudyItemActionBase {
return;
}
createFromTemplate(taskDirectory.get(), manager.getTestsTemplate(project), view, false);
- createFromTemplate(taskDirectory.get(), FileTemplateManager.getInstance(project).getInternalTemplate(EduNames.TASK_HTML), view, false);
+ createFromTemplate(taskDirectory.get(), FileTemplateManager.getInstance(project).getInternalTemplate(CCSettings.getInstance().useHtmlAsDefaultTaskFormat() ? EduNames.TASK_HTML : EduNames.TASK_MD), view, false);
String defaultExtension = manager.getDefaultTaskFileExtension();
if (defaultExtension != null) {
FileTemplate taskFileTemplate = manager.getTaskFileTemplateForExtension(project, defaultExtension);
diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCEditTaskTextAction.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCEditTaskTextAction.java
index d86a0ae61754..89024abb24c4 100644
--- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCEditTaskTextAction.java
+++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCEditTaskTextAction.java
@@ -10,6 +10,7 @@ import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.edu.coursecreator.CCUtils;
+import com.jetbrains.edu.coursecreator.settings.CCSettings;
import com.jetbrains.edu.learning.StudyState;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.StudyUtils;
@@ -51,7 +52,8 @@ public class CCEditTaskTextAction extends ToggleAction implements DumbAware {
return;
}
final StudyState studyState = new StudyState(selectedEditor);
- VirtualFile taskTextFile = studyState.getTaskDir().findChild(EduNames.TASK_HTML);
+ VirtualFile taskTextFile = studyState.getTaskDir().findChild(CCSettings.getInstance().useHtmlAsDefaultTaskFormat() ?
+ EduNames.TASK_HTML : EduNames.TASK_MD);
if (taskTextFile == null) {
LOG.info("Failed to find task.html");
return;
diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/projectView/CCTreeStructureProvider.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/projectView/CCTreeStructureProvider.java
index 2702e3d4cf56..d73ca3e90678 100644
--- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/projectView/CCTreeStructureProvider.java
+++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/projectView/CCTreeStructureProvider.java
@@ -42,7 +42,8 @@ public class CCTreeStructureProvider extends StudyTreeStructureProvider {
if (virtualFile == null) {
continue;
}
- if (StudyUtils.getTaskFile(project, virtualFile) == null && !EduNames.TASK_HTML.equals(virtualFile.getName())) {
+ if (StudyUtils.getTaskFile(project, virtualFile) == null && !EduNames.TASK_HTML.equals(virtualFile.getName())
+ && !EduNames.TASK_MD.equals(virtualFile.getName())) {
modifiedChildren.add(new CCStudentInvisibleFileNode(project, ((PsiFileNode)node).getValue(), settings));
}
}
diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/settings/CCOptions.form b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/settings/CCOptions.form
new file mode 100644
index 000000000000..19705399b439
--- /dev/null
+++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/settings/CCOptions.form
@@ -0,0 +1,62 @@
+
+
diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/settings/CCOptions.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/settings/CCOptions.java
new file mode 100644
index 000000000000..34d15f23f182
--- /dev/null
+++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/settings/CCOptions.java
@@ -0,0 +1,71 @@
+package com.jetbrains.edu.coursecreator.settings;
+
+import com.intellij.openapi.options.ConfigurationException;
+import com.jetbrains.edu.learning.settings.StudyOptionsProvider;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+public class CCOptions implements StudyOptionsProvider {
+ private JRadioButton myHtmlRadioButton;
+ private JRadioButton myMarkdownRadioButton;
+ private JPanel myPanel;
+ private boolean myIsModified = false;
+
+
+ public CCOptions() {
+ myHtmlRadioButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ myIsModified = true;
+ }
+ });
+
+ myMarkdownRadioButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ myIsModified = true;
+ }
+ });
+ }
+
+ @Nullable
+ @Override
+ public JComponent createComponent() {
+ if (CCSettings.getInstance().useHtmlAsDefaultTaskFormat()) {
+ myHtmlRadioButton.setSelected(true);
+ myMarkdownRadioButton.setSelected(false);
+ }
+ else {
+ myHtmlRadioButton.setSelected(false);
+ myMarkdownRadioButton.setSelected(true);
+ }
+ return myPanel;
+ }
+
+ @Override
+ public boolean isModified() {
+ return myIsModified;
+ }
+
+ @Override
+ public void apply() throws ConfigurationException {
+ if (myHtmlRadioButton.isSelected()) {
+ CCSettings.getInstance().setUseHtmlAsDefaultTaskFormat(true);
+ }
+ else if (myMarkdownRadioButton.isSelected()) {
+ CCSettings.getInstance().setUseHtmlAsDefaultTaskFormat(false);
+ }
+ }
+
+ @Override
+ public void reset() {
+ }
+
+ @Override
+ public void disposeUIResources() {
+
+ }
+}
diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/settings/CCSettings.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/settings/CCSettings.java
new file mode 100644
index 000000000000..2d61fbf6ebf8
--- /dev/null
+++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/settings/CCSettings.java
@@ -0,0 +1,38 @@
+package com.jetbrains.edu.coursecreator.settings;
+
+import com.intellij.openapi.components.PersistentStateComponent;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.components.State;
+import com.intellij.openapi.components.Storage;
+import org.jetbrains.annotations.Nullable;
+
+@State(name = "CCSettings", storages = @Storage("cc_settings.xml"))
+public class CCSettings implements PersistentStateComponent {
+ private CCSettings.State myState = new CCSettings.State();
+
+ public static class State {
+ public boolean isHtmlDefault = true;
+ }
+ @Nullable
+ @Override
+ public State getState() {
+ return myState;
+ }
+
+ @Override
+ public void loadState(State state) {
+ myState = state;
+ }
+
+ public boolean useHtmlAsDefaultTaskFormat() {
+ return myState.isHtmlDefault;
+ }
+
+ public void setUseHtmlAsDefaultTaskFormat(final boolean useHtml) {
+ myState.isHtmlDefault = useHtml;
+ }
+
+ public static CCSettings getInstance() {
+ return ServiceManager.getService(CCSettings.class);
+ }
+}
diff --git a/python/educational-core/student/resources/META-INF/plugin.xml b/python/educational-core/student/resources/META-INF/plugin.xml
index 996451b7eec7..70219b1442b9 100644
--- a/python/educational-core/student/resources/META-INF/plugin.xml
+++ b/python/educational-core/student/resources/META-INF/plugin.xml
@@ -102,6 +102,6 @@
-
+
\ No newline at end of file
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java
index 8559bd500b52..cfafd03603e8 100644
--- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java
@@ -25,6 +25,7 @@ import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindow;
@@ -35,6 +36,8 @@ import com.intellij.psi.PsiFile;
import com.intellij.ui.JBColor;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.content.Content;
+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;
@@ -47,12 +50,14 @@ import com.jetbrains.edu.learning.editor.StudyEditor;
import com.jetbrains.edu.learning.ui.StudyProgressToolWindowFactory;
import com.jetbrains.edu.learning.ui.StudyToolWindow;
import com.jetbrains.edu.learning.ui.StudyToolWindowFactory;
+import com.petebevin.markdown.MarkdownProcessor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.io.*;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -389,7 +394,7 @@ public class StudyUtils {
return false;
}
String name = virtualFile.getName();
- return !isTestsFile(project, name) && !EduNames.TASK_HTML.equals(name);
+ return !isTestsFile(project, name) && !EduNames.TASK_HTML.equals(name) && !EduNames.TASK_MD.equals(name);
}
if (element instanceof PsiDirectory) {
VirtualFile virtualFile = ((PsiDirectory)element).getVirtualFile();
@@ -438,25 +443,35 @@ public class StudyUtils {
return text;
}
if (taskDirectory != null) {
- VirtualFile taskTextFile = taskDirectory.findChild(EduNames.TASK_HTML);
- if (taskTextFile == null) {
- VirtualFile srcDir = taskDirectory.findChild("src");
- if (srcDir != null) {
- taskTextFile = srcDir.findChild(EduNames.TASK_HTML);
- }
+ final String taskTextFileHtml = getTaskTextFrom(taskDirectory, EduNames.TASK_HTML);
+ if (taskTextFileHtml != null) return taskTextFileHtml;
+
+ final String taskTextFileMd = getTaskTextFrom(taskDirectory, EduNames.TASK_MD);
+ if (taskTextFileMd != null) return convertToHtml(taskTextFileMd);
+ }
+ return null;
+ }
+
+ @Nullable
+ private static String getTaskTextFrom(@NotNull VirtualFile taskDirectory, @NotNull String taskTextFilename) {
+ VirtualFile taskTextFile = taskDirectory.findChild(taskTextFilename);
+ if (taskTextFile == null) {
+ VirtualFile srcDir = taskDirectory.findChild("src");
+ if (srcDir != null) {
+ taskTextFile = srcDir.findChild(taskTextFilename);
}
- if (taskTextFile != null) {
- try {
- return FileUtil.loadTextAndClose(taskTextFile.getInputStream());
- }
- catch (IOException e) {
- LOG.info(e);
- }
+ }
+ if (taskTextFile != null) {
+ try {
+ return FileUtil.loadTextAndClose(taskTextFile.getInputStream());
+ }
+ catch (IOException e) {
+ LOG.info(e);
}
}
return null;
}
-
+
@Nullable
public static StudyPluginConfigurator getConfigurator(@NotNull final Project project) {
StudyPluginConfigurator[] extensions = StudyPluginConfigurator.EP_NAME.getExtensions();
@@ -558,4 +573,12 @@ public class StudyUtils {
}
return null;
}
+
+ private static String convertToHtml(@NotNull final String content) {
+ ArrayList lines = ContainerUtil.newArrayList(content.split("\n|\r|\r\n"));
+ MarkdownUtil.replaceHeaders(lines);
+ MarkdownUtil.replaceCodeBlock(lines);
+
+ return new MarkdownProcessor().markdown(StringUtil.join(lines, "\n"));
+ }
}
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduNames.java b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduNames.java
index a6062cb25339..a9537e24d6c5 100644
--- a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduNames.java
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduNames.java
@@ -21,6 +21,7 @@ import org.jetbrains.annotations.NonNls;
@NonNls
public class EduNames {
public static final String TASK_HTML = "task.html";
+ public static final String TASK_MD = "task.md";
public static final String HINTS = "hints";
public static final String LESSON = "lesson";
public static final String LESSON_TITLED = StringUtil.toTitleCase(LESSON);
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java
index 0fd1000866a5..eca6e59322e1 100644
--- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java
@@ -70,7 +70,7 @@ public class StudyGenerator {
File resourceFile = new File(newResourceRoot, fileName);
File fileInProject = new File(taskDir.getCanonicalPath(), fileName);
FileUtil.copy(resourceFile, fileInProject);
- if (!StudyUtils.isTestsFile(project, fileName) && !EduNames.TASK_HTML.equals(fileName)) {
+ if (!StudyUtils.isTestsFile(project, fileName) && !EduNames.TASK_HTML.equals(fileName) && !EduNames.TASK_MD.equals(fileName)) {
StudyTaskManager.getInstance(project).addInvisibleFiles(FileUtil.toSystemIndependentName(fileInProject.getPath()));
}
}
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/projectView/StudyTreeStructureProvider.java b/python/educational-core/student/src/com/jetbrains/edu/learning/projectView/StudyTreeStructureProvider.java
index 0a3a3e841feb..443c7ac523fc 100644
--- a/python/educational-core/student/src/com/jetbrains/edu/learning/projectView/StudyTreeStructureProvider.java
+++ b/python/educational-core/student/src/com/jetbrains/edu/learning/projectView/StudyTreeStructureProvider.java
@@ -79,7 +79,7 @@ public class StudyTreeStructureProvider implements TreeStructureProvider, DumbAw
if (!StudyTaskManager.getInstance(project).isInvisibleFile(virtualFile.getPath())) {
String fileName = virtualFile.getName();
if (!fileName.contains(EduNames.WINDOW_POSTFIX) && !fileName.contains(EduNames.WINDOWS_POSTFIX)
- && !StudyUtils.isTestsFile(project, fileName) && !EduNames.TASK_HTML.equals(fileName)) {
+ && !StudyUtils.isTestsFile(project, fileName) && !EduNames.TASK_HTML.equals(fileName) && !EduNames.TASK_MD.equals(fileName)) {
nodes.add(node);
}
}
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicOptionsProvider.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicOptionsProvider.java
deleted file mode 100644
index 921200ef4937..000000000000
--- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicOptionsProvider.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.jetbrains.edu.learning.stepic;
-
-import com.intellij.openapi.options.ConfigurationException;
-import com.jetbrains.edu.learning.settings.StudyOptionsProvider;
-import org.jetbrains.annotations.Nullable;
-
-import javax.swing.*;
-
-public class StepicOptionsProvider implements StudyOptionsProvider{
-
- private StepicStudyOptions myPanel;
-
- @Nullable
- @Override
- public JComponent createComponent() {
- myPanel = new StepicStudyOptions();
- return myPanel.getPanel();
- }
-
- @Override
- public boolean isModified() {
- return myPanel.isModified();
- }
-
- @Override
- public void apply() throws ConfigurationException {
- myPanel.apply();
- }
-
- @Override
- public void reset() {
- myPanel.reset();
- }
-
- @Override
- public void disposeUIResources() {
- }
-}
diff --git a/python/educational-core/student/student.iml b/python/educational-core/student/student.iml
index 80c21a26244e..bbd07b593eae 100644
--- a/python/educational-core/student/student.iml
+++ b/python/educational-core/student/student.iml
@@ -22,5 +22,6 @@
+
\ No newline at end of file
diff --git a/python/educational-python/course-creator-python/resources/fileTemplates/internal/task.md.ft b/python/educational-python/course-creator-python/resources/fileTemplates/internal/task.md.ft
new file mode 100644
index 000000000000..d822b90838a7
--- /dev/null
+++ b/python/educational-python/course-creator-python/resources/fileTemplates/internal/task.md.ft
@@ -0,0 +1,3 @@
+This is the markdown document.
+
+Write your task text here
\ No newline at end of file