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