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 131d9f5b46d8..421b23fef48e 100644 --- a/python/educational-core/course-creator/resources/META-INF/plugin.xml +++ b/python/educational-core/course-creator/resources/META-INF/plugin.xml @@ -19,18 +19,17 @@ - - - - + + + + + + + + - - - - - com.jetbrains.edu.coursecreator.CCProjectComponent @@ -46,49 +45,40 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + \ No newline at end of file diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCEditorFactoryListener.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCEditorFactoryListener.java deleted file mode 100644 index 0ecea377388c..000000000000 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCEditorFactoryListener.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.jetbrains.edu.coursecreator; - -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.actionSystem.EditorActionManager; -import com.intellij.openapi.editor.colors.EditorColors; -import com.intellij.openapi.editor.event.EditorFactoryEvent; -import com.intellij.openapi.editor.event.EditorFactoryListener; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.jetbrains.edu.learning.core.EduAnswerPlaceholderDeleteHandler; -import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter; -import com.jetbrains.edu.learning.core.EduDocumentListener; -import com.jetbrains.edu.learning.core.EduNames; -import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.TaskFile; -import org.jetbrains.annotations.NotNull; - -public class CCEditorFactoryListener implements EditorFactoryListener { - @Override - public void editorCreated(@NotNull EditorFactoryEvent event) { - Editor editor = event.getEditor(); - Project project = editor.getProject(); - if (project == null) { - return; - } - VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(editor.getDocument()); - if (virtualFile == null) { - return; - } - final CCProjectService service = CCProjectService.getInstance(project); - Course course = service.getCourse(); - if (course == null) { - return; - } - final VirtualFile taskDir = virtualFile.getParent(); - if (taskDir == null || !taskDir.getName().contains(EduNames.TASK)) { - return; - } - final VirtualFile lessonDir = taskDir.getParent(); - if (lessonDir == null) return; - final TaskFile taskFile = service.getTaskFile(virtualFile); - if (taskFile == null) { - return; - } - EduDocumentListener listener = new EduDocumentListener(taskFile, true, true); - CCProjectService.addDocumentListener(editor.getDocument(), listener); - editor.getDocument().addDocumentListener(listener); - EditorActionManager.getInstance() - .setReadonlyFragmentModificationHandler(editor.getDocument(), new EduAnswerPlaceholderDeleteHandler(editor)); - service.drawAnswerPlaceholders(virtualFile, editor); - editor.getColorsScheme().setColor(EditorColors.READONLY_FRAGMENT_BACKGROUND_COLOR, null); - EduAnswerPlaceholderPainter.createGuardedBlocks(editor, taskFile, false); - } - - @Override - public void editorReleased(@NotNull EditorFactoryEvent event) { - Editor editor = event.getEditor(); - Document document = editor.getDocument(); - EduDocumentListener listener = CCProjectService.getListener(document); - if (listener != null) { - document.removeDocumentListener(listener); - CCProjectService.removeListener(document); - } - editor.getMarkupModel().removeAllHighlighters(); - editor.getSelectionModel().removeSelection(); - } -} diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCLanguageManager.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCLanguageManager.java index 5ab5f5591ed8..b6c8fd4ecf53 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCLanguageManager.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCLanguageManager.java @@ -3,6 +3,7 @@ package com.jetbrains.edu.coursecreator; import com.intellij.ide.fileTemplates.FileTemplate; import com.intellij.lang.LanguageExtension; import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -21,4 +22,8 @@ public interface CCLanguageManager { FileTemplate getTestsTemplate(@NotNull final Project project); boolean doNotPackFile(File pathname); + + default boolean isTestFile(VirtualFile file) { + return false; + } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java index e5871848ecf1..b5a23ba917ed 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectComponent.java @@ -1,33 +1,40 @@ package com.jetbrains.edu.coursecreator; -import com.intellij.openapi.components.ProjectComponent; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.EditorFactory; -import com.intellij.openapi.editor.event.EditorFactoryEvent; -import com.intellij.openapi.editor.impl.EditorFactoryImpl; -import com.intellij.openapi.fileEditor.FileEditor; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorImpl; +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.components.AbstractProjectComponent; import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.ProjectManager; -import com.intellij.openapi.startup.StartupManager; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; +import com.jetbrains.edu.learning.StudyProjectComponent; +import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; import org.jetbrains.annotations.NotNull; -public class CCProjectComponent implements ProjectComponent { - private final Project myProject; - private CCFileDeletedListener myListener; +import java.io.File; - public CCProjectComponent(Project project) { +public class CCProjectComponent extends AbstractProjectComponent { + private final CCVirtualFileListener myTaskFileLifeListener = new CCVirtualFileListener(); + private final Project myProject; + + protected CCProjectComponent(Project project) { + super(project); myProject = project; } public void initComponent() { + VirtualFileManager.getInstance().addVirtualFileListener(myTaskFileLifeListener); } - public void disposeComponent() { + public void migrateIfNeeded() { + Course studyCourse = StudyTaskManager.getInstance(myProject).getCourse(); + Course course = CCProjectService.getInstance(myProject).getCourse(); + if (studyCourse == null && course != null) { + course.setCourseMode(CCUtils.COURSE_MODE); + File coursesDir = new File(PathManager.getConfigPath(), "courses"); + File courseDir = new File(coursesDir, course.getName() + "-" + myProject.getName()); + course.setCourseDirectory(courseDir.getPath()); + StudyTaskManager.getInstance(myProject).setCourse(course); + StudyProjectComponent.getInstance(myProject).registerStudyToolWindow(course); + } } @NotNull @@ -36,36 +43,11 @@ public class CCProjectComponent implements ProjectComponent { } public void projectOpened() { - StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new Runnable() { - @Override - public void run() { - final Course course = CCProjectService.getInstance(myProject).getCourse(); - if (course != null) { - course.initCourse(true); - myListener = new CCFileDeletedListener(myProject); - VirtualFileManager.getInstance().addVirtualFileListener(myListener); - final CCEditorFactoryListener editorFactoryListener = new CCEditorFactoryListener(); - EditorFactory.getInstance().addEditorFactoryListener(editorFactoryListener, myProject); - VirtualFile[] files = FileEditorManager.getInstance(myProject).getOpenFiles(); - for (VirtualFile file : files) { - if (CCProjectService.getInstance(myProject).isTaskFile(file)) { - FileEditorManager.getInstance(myProject).closeFile(file); - continue; - } - FileEditor fileEditor = FileEditorManager.getInstance(myProject).getSelectedEditor(file); - if (fileEditor instanceof PsiAwareTextEditorImpl) { - Editor editor = ((PsiAwareTextEditorImpl)fileEditor).getEditor(); - editorFactoryListener.editorCreated(new EditorFactoryEvent(new EditorFactoryImpl(ProjectManager.getInstance()), editor)); - } - } - } - } - }); + migrateIfNeeded(); + VirtualFileManager.getInstance().addVirtualFileListener(myTaskFileLifeListener); } public void projectClosed() { - if (myListener != null) { - VirtualFileManager.getInstance().removeVirtualFileListener(myListener); - } + VirtualFileManager.getInstance().removeVirtualFileListener(myTaskFileLifeListener); } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectService.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectService.java index ba9886692671..2ff6f8051033 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectService.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCProjectService.java @@ -15,140 +15,24 @@ */ package com.jetbrains.edu.coursecreator; -import com.intellij.openapi.actionSystem.AnActionEvent; 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 com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.ui.JBColor; import com.intellij.util.xmlb.XmlSerializerUtil; -import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter; -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.*; +import com.jetbrains.edu.learning.courseFormat.Course; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; @State(name = "CCProjectService", storages = @Storage("course_service.xml")) public class CCProjectService implements PersistentStateComponent { private Course myCourse; - private static final Map myDocumentListeners = new HashMap(); - - @Nullable - public TaskFile getTaskFile(@NotNull final VirtualFile virtualFile) { - VirtualFile taskDir = virtualFile.getParent(); - if (taskDir == null) { - return null; - } - String taskDirName = taskDir.getName(); - if (!taskDirName.contains(EduNames.TASK)) { - return null; - } - VirtualFile lessonDir = taskDir.getParent(); - if (lessonDir == null) { - return null; - } - String lessonDirName = lessonDir.getName(); - if (!lessonDirName.contains(EduNames.LESSON)) { - return null; - } - Lesson lesson = myCourse.getLesson(lessonDirName); - if (lesson == null) { - return null; - } - Task task = lesson.getTask(taskDir.getName()); - if (task == null) { - return null; - } - return task.getTaskFile(virtualFile.getName()); - } - - public void drawAnswerPlaceholders(@NotNull final VirtualFile virtualFile, @NotNull final Editor editor) { - TaskFile taskFile = getTaskFile(virtualFile); - if (taskFile == null) { - return; - } - List answerPlaceholders = taskFile.getAnswerPlaceholders(); - for (AnswerPlaceholder answerPlaceholder : answerPlaceholders) { - EduAnswerPlaceholderPainter.drawAnswerPlaceholder(editor, answerPlaceholder, false, JBColor.BLUE); - } - } - - public static void addDocumentListener(Document document, EduDocumentListener listener) { - myDocumentListeners.put(document, listener); - } - - public static EduDocumentListener getListener(Document document) { - return myDocumentListeners.get(document); - } - - public static void removeListener(Document document) { - myDocumentListeners.remove(document); - } - - @Nullable - public Task getTask(VirtualFile file) { - if (myCourse == null || file == null) { - return null; - } - VirtualFile taskDir = file.getParent(); - if (taskDir != null) { - String taskDirName = taskDir.getName(); - if (taskDirName.contains(EduNames.TASK)) { - VirtualFile lessonDir = taskDir.getParent(); - if (lessonDir != null) { - String lessonDirName = lessonDir.getName(); - int lessonIndex = EduUtils.getIndex(lessonDirName, EduNames.LESSON); - List lessons = myCourse.getLessons(); - if (!EduUtils.indexIsValid(lessonIndex, lessons)) { - return null; - } - Lesson lesson = lessons.get(lessonIndex); - int taskIndex = EduUtils.getIndex(taskDirName, EduNames.TASK); - List tasks = lesson.getTaskList(); - if (!EduUtils.indexIsValid(taskIndex, tasks)) { - return null; - } - return tasks.get(taskIndex); - } - } - } - return null; - } - - public boolean isTaskFile(VirtualFile file) { - Task task = getTask(file); - return task != null && task.isTaskFile(file.getName()); - } - - public static boolean setCCActionAvailable(@NotNull AnActionEvent e) { - final Project project = e.getProject(); - if (project == null) { - return false; - } - if (getInstance(project).getCourse() == null) { - EduUtils.enableAction(e, false); - return false; - } - EduUtils.enableAction(e, true); - return true; - } - public Course getCourse() { return myCourse; } - public void setCourse(@NotNull final Course course) { + public void setCourse(Course course) { myCourse = course; } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCRefactoringElementListenerProvider.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCRefactoringElementListenerProvider.java index ed9b404e04a5..7dbd894d8980 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCRefactoringElementListenerProvider.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCRefactoringElementListenerProvider.java @@ -16,24 +16,30 @@ package com.jetbrains.edu.coursecreator; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.refactoring.listeners.RefactoringElementAdapter; import com.intellij.refactoring.listeners.RefactoringElementListener; import com.intellij.refactoring.listeners.RefactoringElementListenerProvider; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; import com.jetbrains.edu.learning.courseFormat.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; -import com.jetbrains.edu.coursecreator.actions.CCRunTestsAction; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.IOException; import java.util.Map; public class CCRefactoringElementListenerProvider implements RefactoringElementListenerProvider { + private static final Logger LOG = Logger.getInstance(CCRefactoringElementListenerProvider.class); + @Nullable @Override public RefactoringElementListener getListener(PsiElement element) { @@ -62,8 +68,7 @@ public class CCRefactoringElementListenerProvider implements RefactoringElementL private static void tryToRenameTaskFile(PsiFile file, String oldName) { final PsiDirectory taskDir = file.getContainingDirectory(); - final CCProjectService service = CCProjectService.getInstance(file.getProject()); - Course course = service.getCourse(); + Course course = StudyTaskManager.getInstance(file.getProject()).getCourse(); if (course == null) { return; } @@ -82,16 +87,29 @@ public class CCRefactoringElementListenerProvider implements RefactoringElementL if (task == null) { return; } + Map taskFiles = task.getTaskFiles(); + TaskFile taskFile = task.getTaskFile(oldName); + if (taskFile == null) { + return; + } ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { - CCRunTestsAction.clearTestEnvironment(taskDir.getVirtualFile(), taskDir.getProject()); + VirtualFile patternFile = StudyUtils.getPatternFile(taskFile, oldName); + if (patternFile != null) { + try { + patternFile.delete(CCRefactoringElementListenerProvider.class); + } + catch (IOException e) { + LOG.info(e); + } + } } }); - Map taskFiles = task.getTaskFiles(); - TaskFile taskFile = task.getTaskFile(oldName); + taskFiles.remove(oldName); taskFiles.put(file.getName(), taskFile); + CCUtils.createResourceFile(file.getVirtualFile(), course, taskDir.getVirtualFile()); } @Override diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCStudyActionListener.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCStudyActionListener.java new file mode 100644 index 000000000000..945026bd1ff9 --- /dev/null +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCStudyActionListener.java @@ -0,0 +1,36 @@ +package com.jetbrains.edu.coursecreator; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.edu.learning.StudyActionListener; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.courseFormat.Task; +import com.jetbrains.edu.learning.courseFormat.TaskFile; + +public class CCStudyActionListener implements StudyActionListener { + @Override + public void beforeCheck(AnActionEvent event) { + Project project = event.getProject(); + if (project == null) { + return; + } + VirtualFile virtualFile = CommonDataKeys.VIRTUAL_FILE.getData(event.getDataContext()); + if (virtualFile == null) { + return; + } + + TaskFile taskFile = StudyUtils.getTaskFile(project, virtualFile); + if (taskFile == null) { + return; + } + + Task task = taskFile.getTask(); + VirtualFile taskDir = StudyUtils.getTaskDir(virtualFile); + if (taskDir == null) { + return; + } + CCUtils.createResources(project, task, taskDir); + } +} diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCStudyActionsProvider.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCStudyActionsProvider.java new file mode 100644 index 000000000000..7ccdc075ebe3 --- /dev/null +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCStudyActionsProvider.java @@ -0,0 +1,12 @@ +package com.jetbrains.edu.coursecreator; + +import com.intellij.openapi.actionSystem.AnAction; +import com.jetbrains.edu.coursecreator.actions.CCEditTaskTextAction; +import com.jetbrains.edu.learning.StudyActionsProvider; + +public class CCStudyActionsProvider implements StudyActionsProvider{ + @Override + public AnAction[] getActions() { + return new AnAction[]{new CCEditTaskTextAction()}; + } +} diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java index 65131d0b36ae..f6dd7417a32d 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java @@ -6,6 +6,8 @@ import com.intellij.ide.projectView.actions.MarkRootActionBase; 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.fileEditor.FileDocumentManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.DumbModePermission; import com.intellij.openapi.project.DumbService; @@ -14,27 +16,27 @@ import com.intellij.openapi.roots.ContentEntry; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileEvent; import com.intellij.psi.PsiDirectory; +import com.intellij.util.DocumentUtil; import com.intellij.util.Function; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduUtils; -import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.StudyItem; +import com.jetbrains.edu.learning.courseFormat.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; +import java.util.*; public class CCUtils { private static final Logger LOG = Logger.getInstance(CCUtils.class); public static final String GENERATED_FILES_FOLDER = ".coursecreator"; - public static final String TESTS = "coursecreatortests"; - public static final String RESOURCES = "coursecreatorresources"; + public static final String COURSE_MODE = "Course Creator"; @Nullable public static CCLanguageManager getStudyLanguageManager(@NotNull final Course course) { @@ -100,9 +102,9 @@ public class CCUtils { if (sourceDirectory == null) { return false; } - CCProjectService service = CCProjectService.getInstance(sourceDirectory.getProject()); - Course course = service.getCourse(); - if (course != null && course.getLesson(sourceDirectory.getName()) != null) { + Project project = sourceDirectory.getProject(); + Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course != null && isCourseCreator(project) && course.getLesson(sourceDirectory.getName()) != null) { return true; } return false; @@ -169,4 +171,74 @@ public class CCUtils { }); return folder.get(); } + + public static boolean isCourseCreator(@NotNull Project project) { + Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null) { + return false; + } + + return COURSE_MODE.equals(course.getCourseMode()); + } + + public static boolean isTestsFile(@NotNull Project project, @NotNull VirtualFile file) { + Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null) { + return false; + } + CCLanguageManager manager = getStudyLanguageManager(course); + if (manager == null) { + return false; + } + return manager.isTestFile(file); + } + + public static void createResourceFile(VirtualFile createdFile, Course course, VirtualFile taskVF) { + VirtualFile lessonVF = taskVF.getParent(); + if (lessonVF == null) { + return; + } + + String taskResourcesPath = FileUtil.join(course.getCourseDirectory(), lessonVF.getName(), taskVF.getName()); + File taskResourceFile = new File(taskResourcesPath); + if (!taskResourceFile.exists()) { + if (!taskResourceFile.mkdirs()) { + LOG.info("Failed to create resources for task " + taskResourcesPath); + } + } + try { + File toFile = new File(taskResourceFile, createdFile.getName()); + FileUtil.copy(new File(createdFile.getPath()), toFile); + } + catch (IOException e) { + LOG.info("Failed to copy created task file to resources " + createdFile.getPath()); + } + } + + + public static void createResources(Project project, Task task, VirtualFile taskDir) { + Map files = task.getTaskFiles(); + for (Map.Entry entry : files.entrySet()) { + String name = entry.getKey(); + VirtualFile child = taskDir.findChild(name); + if (child == null) { + continue; + } + Document patternDocument = StudyUtils.getPatternDocument(entry.getValue(), name); + Document document = FileDocumentManager.getInstance().getDocument(child); + if (document == null || patternDocument == null) { + return; + } + DocumentUtil.writeInRunUndoTransparentAction(() -> { + patternDocument.replaceString(0, patternDocument.getTextLength(), document.getCharsSequence()); + FileDocumentManager.getInstance().saveDocument(patternDocument); + }); + TaskFile target = new TaskFile(); + TaskFile.copy(entry.getValue(), target); + for (AnswerPlaceholder placeholder : target.getAnswerPlaceholders()) { + placeholder.setUseLength(false); + } + EduUtils.createStudentDocument(project, target, child, patternDocument); + } + } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCFileDeletedListener.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java similarity index 54% rename from python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCFileDeletedListener.java rename to python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java index 1d840f0c5943..3c3be598ca7f 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCFileDeletedListener.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCVirtualFileListener.java @@ -1,54 +1,95 @@ package com.jetbrains.edu.coursecreator; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; import com.intellij.util.Function; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.*; import org.jetbrains.annotations.NotNull; -class CCFileDeletedListener extends VirtualFileAdapter { +public class CCVirtualFileListener extends VirtualFileAdapter { - private final Project myProject; + private static final Logger LOG = Logger.getInstance(CCVirtualFileListener.class); - CCFileDeletedListener(@NotNull final Project project) { - myProject = project; + @Override + public void fileCreated(@NotNull VirtualFileEvent event) { + VirtualFile createdFile = event.getFile(); + Project project = ProjectUtil.guessProjectForFile(createdFile); + if (project == null) { + return; + } + Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null || !CCUtils.isCourseCreator(project)) { + return; + } + TaskFile taskFile = StudyUtils.getTaskFile(project, createdFile); + if (taskFile != null) { + return; + } + + String name = createdFile.getName(); + if (CCUtils.isTestsFile(project, createdFile) + || EduNames.TASK_HTML.equals(name) + || name.contains(EduNames.WINDOW_POSTFIX) + || name.contains(EduNames.WINDOWS_POSTFIX) + || name.contains(EduNames.ANSWERS_POSTFIX)) { + return; + } + + VirtualFile taskVF = createdFile.getParent(); + if (taskVF == null) { + return; + } + Task task = StudyUtils.getTask(project, taskVF); + if (task == null) { + return; + } + + CCUtils.createResourceFile(createdFile, course, taskVF); + + task.addTaskFile(name, 1); } @Override public void fileDeleted(@NotNull VirtualFileEvent event) { - if (myProject.isDisposed() || !myProject.isOpen()) { - return; - } VirtualFile removedFile = event.getFile(); if (removedFile.getPath().contains(CCUtils.GENERATED_FILES_FOLDER)) { return; } - final TaskFile taskFile = CCProjectService.getInstance(myProject).getTaskFile(removedFile); + + Project project = ProjectUtil.guessProjectForFile(removedFile); + if (project == null) { + return; + } + Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null) { + return; + } + final TaskFile taskFile = StudyUtils.getTaskFile(project, removedFile); if (taskFile != null) { deleteTaskFile(removedFile, taskFile); return; } - Course course = CCProjectService.getInstance(myProject).getCourse(); - if (course == null) { - return; - } if (removedFile.getName().contains(EduNames.TASK)) { - deleteTask(course, removedFile, myProject); + deleteTask(course, removedFile); } if (removedFile.getName().contains(EduNames.LESSON)) { - deleteLesson(course, removedFile); + deleteLesson(course, removedFile, project); } } - private void deleteLesson(@NotNull final Course course, @NotNull final VirtualFile removedLessonFile) { + private static void deleteLesson(@NotNull final Course course, @NotNull final VirtualFile removedLessonFile, Project project) { Lesson removedLesson = course.getLesson(removedLessonFile.getName()); if (removedLesson == null) { return; } - VirtualFile courseDir = myProject.getBaseDir(); + VirtualFile courseDir = project.getBaseDir(); CCUtils.updateHigherElements(courseDir.getChildren(), new Function() { @Override public StudyItem fun(VirtualFile file) { @@ -58,7 +99,7 @@ class CCFileDeletedListener extends VirtualFileAdapter { course.getLessons().remove(removedLesson); } - private static void deleteTask(@NotNull final Course course, @NotNull final VirtualFile removedTask, @NotNull final Project project) { + private static void deleteTask(@NotNull final Course course, @NotNull final VirtualFile removedTask) { VirtualFile lessonDir = removedTask.getParent(); if (lessonDir == null || !lessonDir.getName().contains(EduNames.LESSON)) { return; diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAddAnswerPlaceholder.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAddAnswerPlaceholder.java index 791d7fb0efae..5eae370909ee 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAddAnswerPlaceholder.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAddAnswerPlaceholder.java @@ -1,32 +1,32 @@ package com.jetbrains.edu.coursecreator.actions; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.Presentation; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.SelectionModel; -import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.ui.JBColor; -import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter; -import com.jetbrains.edu.coursecreator.CCProjectService; +import com.intellij.util.DocumentUtil; import com.jetbrains.edu.coursecreator.ui.CCCreateAnswerPlaceholderDialog; -import com.jetbrains.edu.learning.courseFormat.*; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter; +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.TaskFile; import org.jetbrains.annotations.NotNull; import java.util.List; -public class CCAddAnswerPlaceholder extends DumbAwareAction { - private static final Logger LOG = Logger.getInstance(CCAddAnswerPlaceholder.class); +public class CCAddAnswerPlaceholder extends CCAnswerPlaceholderAction { public CCAddAnswerPlaceholder() { - super("Add Answer Placeholder", "Add answer placeholder", null); + super("Add/Delete Answer Placeholder", "Add/Delete answer placeholder", null); } @@ -43,114 +43,131 @@ public class CCAddAnswerPlaceholder extends DumbAwareAction { return false; } - @Override - public void actionPerformed(@NotNull AnActionEvent e) { - final Project project = e.getData(CommonDataKeys.PROJECT); - if (project == null) { + private static void addPlaceholder(@NotNull CCState state) { + Editor editor = state.getEditor(); + Project project = state.getProject(); + PsiFile file = state.getFile(); + + final Document document = PsiDocumentManager.getInstance(project).getDocument(file); + if (document == null) { return; } - final PsiFile file = CommonDataKeys.PSI_FILE.getData(e.getDataContext()); - if (file == null) return; - final Editor editor = CommonDataKeys.EDITOR.getData(e.getDataContext()); - if (editor == null) return; + final SelectionModel model = editor.getSelectionModel(); - final Document document = PsiDocumentManager.getInstance(project).getDocument(file); - if (document == null) return; final int start = model.getSelectionStart(); - final int end = model.getSelectionEnd(); final int lineNumber = document.getLineNumber(start); int realStart = start - document.getLineStartOffset(lineNumber); - - final CCProjectService service = CCProjectService.getInstance(project); - final PsiDirectory taskDir = file.getContainingDirectory(); - final PsiDirectory lessonDir = taskDir.getParent(); - if (lessonDir == null) return; - - final TaskFile taskFile = service.getTaskFile(file.getVirtualFile()); - if (taskFile == null) { - return; - } - if (arePlaceholdersIntersect(taskFile, document, start, end)) { - return; - } final AnswerPlaceholder answerPlaceholder = new AnswerPlaceholder(); answerPlaceholder.setLine(lineNumber); answerPlaceholder.setStart(realStart); - answerPlaceholder.setPossibleAnswer(model.getSelectedText()); + answerPlaceholder.setUseLength(false); + String selectedText = model.getSelectedText(); + answerPlaceholder.setPossibleAnswer(selectedText); - CCCreateAnswerPlaceholderDialog dlg = new CCCreateAnswerPlaceholderDialog(project, answerPlaceholder - ); + CCCreateAnswerPlaceholderDialog dlg = new CCCreateAnswerPlaceholderDialog(project, answerPlaceholder); dlg.show(); if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) { return; } + + TaskFile taskFile = state.getTaskFile(); int index = taskFile.getAnswerPlaceholders().size() + 1; answerPlaceholder.setIndex(index); taskFile.addAnswerPlaceholder(answerPlaceholder); + answerPlaceholder.setTaskFile(taskFile); taskFile.sortAnswerPlaceholders(); - EduAnswerPlaceholderPainter.drawAnswerPlaceholder(editor, answerPlaceholder, false, JBColor.BLUE); - EduAnswerPlaceholderPainter.createGuardedBlocks(editor, answerPlaceholder, false); + + + computeInitialState(project, file, taskFile, document); + + EduAnswerPlaceholderPainter.drawAnswerPlaceholder(editor, answerPlaceholder, JBColor.BLUE); + EduAnswerPlaceholderPainter.createGuardedBlocks(editor, answerPlaceholder); + } + + private static void computeInitialState(Project project, PsiFile file, TaskFile taskFile, Document document) { + Document patternDocument = StudyUtils.getPatternDocument(taskFile, file.getName()); + if (patternDocument == null) { + return; + } + DocumentUtil.writeInRunUndoTransparentAction(() -> { + patternDocument.replaceString(0, patternDocument.getTextLength(), document.getCharsSequence()); + FileDocumentManager.getInstance().saveDocument(patternDocument); + }); + TaskFile target = new TaskFile(); + TaskFile.copy(taskFile, target); + List placeholders = target.getAnswerPlaceholders(); + for (AnswerPlaceholder placeholder : placeholders) { + placeholder.setUseLength(false); + } + EduUtils.createStudentDocument(project, target, file.getVirtualFile(), patternDocument); + + for (int i = 0; i < placeholders.size(); i++) { + AnswerPlaceholder fromPlaceholder = placeholders.get(i); + taskFile.getAnswerPlaceholders().get(i).setInitialState(fromPlaceholder); + } + } + + @Override + protected void performAnswerPlaceholderAction(@NotNull CCState state) { + if (canAddPlaceholder(state)) { + addPlaceholder(state); + return; + } + if (canDeletePlaceholder(state)) { + deletePlaceholder(state); + } + } + + private static void deletePlaceholder(@NotNull CCState state) { + Project project = state.getProject(); + PsiFile psiFile = state.getFile(); + final Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile); + if (document == null) return; + TaskFile taskFile = state.getTaskFile(); + AnswerPlaceholder answerPlaceholder = state.getAnswerPlaceholder(); + final List answerPlaceholders = taskFile.getAnswerPlaceholders(); + if (answerPlaceholders.contains(answerPlaceholder)) { + answerPlaceholders.remove(answerPlaceholder); + final Editor editor = state.getEditor(); + editor.getMarkupModel().removeAllHighlighters(); + StudyUtils.drawAllWindows(editor, taskFile); + EduAnswerPlaceholderPainter.createGuardedBlocks(editor, taskFile); + } } @Override public void update(@NotNull AnActionEvent event) { - if (!CCProjectService.setCCActionAvailable(event)) { - return; - } final Presentation presentation = event.getPresentation(); - final Project project = event.getData(CommonDataKeys.PROJECT); - if (project == null) { - presentation.setVisible(false); - presentation.setEnabled(false); + presentation.setEnabledAndVisible(false); + + CCState state = getState(event); + if (state == null) { return; } - final Editor editor = CommonDataKeys.EDITOR.getData(event.getDataContext()); - final PsiFile file = CommonDataKeys.PSI_FILE.getData(event.getDataContext()); - if (editor == null || file == null) { - presentation.setVisible(false); - presentation.setEnabled(false); - return; + + presentation.setVisible(true); + if (canAddPlaceholder(state) || canDeletePlaceholder(state)) { + presentation.setEnabled(true); + presentation.setText((state.getAnswerPlaceholder() == null ? "Add " : "Delete ") + EduNames.PLACEHOLDER); } + } + + + private static boolean canAddPlaceholder(@NotNull CCState state) { + Editor editor = state.getEditor(); SelectionModel selectionModel = editor.getSelectionModel(); if (!selectionModel.hasSelection()) { - presentation.setVisible(false); - presentation.setEnabled(false); - return; + return false; } int start = selectionModel.getSelectionStart(); int end = selectionModel.getSelectionEnd(); + return !arePlaceholdersIntersect(state.getTaskFile(), editor.getDocument(), start, end); + } - final CCProjectService service = CCProjectService.getInstance(project); - final PsiDirectory taskDir = file.getContainingDirectory(); - final PsiDirectory lessonDir = taskDir.getParent(); - if (lessonDir == null) return; - - final Course course = service.getCourse(); - final Lesson lesson = course.getLesson(lessonDir.getName()); - if (lesson == null) { - presentation.setVisible(false); - presentation.setEnabled(false); - return; + private static boolean canDeletePlaceholder(@NotNull CCState state) { + if (state.getEditor().getSelectionModel().hasSelection()) { + return false; } - final Task task = lesson.getTask(taskDir.getName()); - if (task == null) { - presentation.setVisible(false); - presentation.setEnabled(false); - return; - } - TaskFile taskFile = service.getTaskFile(file.getVirtualFile()); - if (taskFile == null) { - LOG.info("could not find task file"); - presentation.setVisible(false); - presentation.setEnabled(false); - return; - } - if (arePlaceholdersIntersect(taskFile, editor.getDocument(), start, end)) { - presentation.setVisible(false); - presentation.setEnabled(false); - return; - } - presentation.setVisible(true); - presentation.setEnabled(true); + return state.getAnswerPlaceholder() != null; } } \ No newline at end of file diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAddAsTaskFile.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAddAsTaskFile.java index 867cc77440b6..c62bd5e1e069 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAddAsTaskFile.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAddAsTaskFile.java @@ -1,46 +1,20 @@ package com.jetbrains.edu.coursecreator.actions; -import com.intellij.ide.projectView.ProjectView; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Task; -import com.jetbrains.edu.coursecreator.CCProjectService; -public class CCAddAsTaskFile extends AnAction { - @Override - public void actionPerformed(final AnActionEvent e) { - Project project = e.getProject(); - if (project == null) { - return; - } - final VirtualFile file = CommonDataKeys.VIRTUAL_FILE.getData(e.getDataContext()); - if (file == null) { - return; - } - Task task = CCProjectService.getInstance(project).getTask(file); - if (task == null) { - return; - } +public class CCAddAsTaskFile extends CCTaskFileActionBase { + + protected void performAction(VirtualFile file, Task task, Course course, Project project) { task.addTaskFile(file.getName(), task.getTaskFiles().size()); - ProjectView.getInstance(project).refresh(); + CCUtils.createResourceFile(file, course, StudyUtils.getTaskDir(file)); } - - @Override - public void update(AnActionEvent e) { - Project project = e.getProject(); - Presentation presentation = e.getPresentation(); - if (project == null) { - presentation.setEnabledAndVisible(false); - return; - } - VirtualFile file = CommonDataKeys.VIRTUAL_FILE.getData(e.getDataContext()); - if (file == null || file.isDirectory() || CCProjectService.getInstance(project).getTaskFile(file) != null) { - presentation.setEnabledAndVisible(false); - } + protected boolean isAvailable(Project project, VirtualFile file) { + return StudyUtils.getTaskFile(project, file) == null && !CCUtils.isTestsFile(project, file); } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAnswerPlaceholderAction.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAnswerPlaceholderAction.java index 48425e247100..e1633044d5e6 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAnswerPlaceholderAction.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAnswerPlaceholderAction.java @@ -2,15 +2,15 @@ package com.jetbrains.edu.coursecreator.actions; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.learning.courseFormat.TaskFile; -import com.jetbrains.edu.coursecreator.CCProjectService; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -23,9 +23,9 @@ abstract public class CCAnswerPlaceholderAction extends DumbAwareAction { } @Nullable - private static CCState getState(@NotNull AnActionEvent e) { + protected static CCState getState(@NotNull AnActionEvent e) { final Project project = e.getProject(); - if (project == null || CCProjectService.getInstance(project).getCourse() == null) { + if (project == null || !CCUtils.isCourseCreator(project)) { return null; } final PsiFile psiFile = CommonDataKeys.PSI_FILE.getData(e.getDataContext()); @@ -40,27 +40,16 @@ abstract public class CCAnswerPlaceholderAction extends DumbAwareAction { if (editor == null) { return null; } - TaskFile taskFile = CCProjectService.getInstance(project).getTaskFile(virtualFile); + TaskFile taskFile = StudyUtils.getTaskFile(project, virtualFile); if (taskFile == null) { return null; } AnswerPlaceholder answerPlaceholder = taskFile.getAnswerPlaceholder(editor.getDocument(), editor.getCaretModel().getLogicalPosition(), true); - if (answerPlaceholder == null) { - return null; - } return new CCState(taskFile, answerPlaceholder, psiFile, editor, project); } - @Override - public void update(@NotNull AnActionEvent e) { - Presentation presentation = e.getPresentation(); - boolean isAvailable = getState(e) != null; - presentation.setEnabled(isAvailable); - presentation.setVisible(isAvailable); - } - @Override public void actionPerformed(@NotNull AnActionEvent e) { CCState state = getState(e); @@ -80,7 +69,7 @@ abstract public class CCAnswerPlaceholderAction extends DumbAwareAction { private Project myProject; public CCState(@NotNull final TaskFile taskFile, - @NotNull final AnswerPlaceholder answerPlaceholder, + @Nullable final AnswerPlaceholder answerPlaceholder, @NotNull final PsiFile file, @NotNull final Editor editor, @NotNull final Project project) { @@ -96,7 +85,7 @@ abstract public class CCAnswerPlaceholderAction extends DumbAwareAction { return myTaskFile; } - @NotNull + @Nullable public AnswerPlaceholder getAnswerPlaceholder() { return myAnswerPlaceholder; } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCChangeCourseInfo.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCChangeCourseInfo.java index 28706dae9a72..3423c822aae8 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCChangeCourseInfo.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCChangeCourseInfo.java @@ -2,39 +2,39 @@ package com.jetbrains.edu.coursecreator.actions; import com.intellij.ide.IdeView; import com.intellij.ide.projectView.ProjectView; -import com.intellij.ide.util.DirectoryChooserUtil; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.DialogBuilder; import com.intellij.psi.PsiDirectory; -import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.coursecreator.CCProjectService; +import com.intellij.util.ui.JBUI; +import com.jetbrains.edu.coursecreator.CCUtils; import com.jetbrains.edu.coursecreator.ui.CCNewProjectPanel; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.courseFormat.Course; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.awt.*; public class CCChangeCourseInfo extends DumbAwareAction { + + private static final String ACTION_TEXT = "Change Course Information"; + public CCChangeCourseInfo() { - super("Change Course Information", "Change Course Information", null); + super(ACTION_TEXT, ACTION_TEXT, null); } @Override public void update(@NotNull AnActionEvent event) { - if (!CCProjectService.setCCActionAvailable(event)) { + final Project project = event.getProject(); + final Presentation presentation = event.getPresentation(); + if (project == null) { return; } - final Presentation presentation = event.getPresentation(); - presentation.setVisible(false); - presentation.setEnabled(false); - final Project project = event.getData(CommonDataKeys.PROJECT); - if (project == null) { + presentation.setEnabledAndVisible(false); + if (!CCUtils.isCourseCreator(project)) { return; } final IdeView view = event.getData(LangDataKeys.IDE_VIEW); @@ -45,45 +45,24 @@ public class CCChangeCourseInfo extends DumbAwareAction { if (directories.length == 0) { return; } - final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view); - if (directory != null && !project.getBaseDir().equals(directory.getVirtualFile())) { - return; - } - presentation.setVisible(true); - presentation.setEnabled(true); - + presentation.setEnabledAndVisible(true); } @Override public void actionPerformed(@NotNull AnActionEvent e) { - if (!CCProjectService.setCCActionAvailable(e)) { - return; - } - final Project project = e.getProject(); + Project project = e.getProject(); if (project == null) { return; } - Course course = CCProjectService.getInstance(project).getCourse(); + Course course = StudyTaskManager.getInstance(project).getCourse(); if (course == null) { return; } - final IdeView view = e.getData(LangDataKeys.IDE_VIEW); - if (view == null) { - return; - } - final PsiDirectory[] directories = view.getDirectories(); - if (directories.length == 0) { - return; - } - final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view); - if (directory != null && !project.getBaseDir().equals(directory.getVirtualFile())) { - return; - } - CCNewProjectPanel panel = new CCNewProjectPanel(course.getName(), Course.getAuthorsString(course.getAuthors()), course.getDescription()); - ChangeCourseInfoDialog changeCourseInfoDialog = - new ChangeCourseInfoDialog(project, panel); - changeCourseInfoDialog.show(); - if (changeCourseInfoDialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) { + + CCNewProjectPanel panel = + new CCNewProjectPanel(course.getName(), Course.getAuthorsString(course.getAuthors()), course.getDescription()); + DialogBuilder builder = createChangeInfoDialog(project, panel); + if (builder.showAndGet()) { course.setAuthors(panel.getAuthors()); course.setName(panel.getName()); course.setDescription(panel.getDescription()); @@ -91,24 +70,14 @@ public class CCChangeCourseInfo extends DumbAwareAction { } } - static class ChangeCourseInfoDialog extends DialogWrapper { + private static DialogBuilder createChangeInfoDialog(Project project, @NotNull CCNewProjectPanel panel) { + DialogBuilder builder = new DialogBuilder(project); - private final JPanel myMainPanel; + builder.setTitle(ACTION_TEXT); + JPanel changeInfoPanel = panel.getMainPanel(); + changeInfoPanel.setMinimumSize(JBUI.size(400, 300)); + builder.setCenterPanel(changeInfoPanel); - public ChangeCourseInfoDialog(@Nullable Project project, CCNewProjectPanel panel) { - super(project); - setTitle("Change Course Information"); - myMainPanel = panel.getMainPanel(); - init(); - } - - @Nullable - @Override - protected JComponent createCenterPanel() { - myMainPanel.setPreferredSize(new Dimension(400, 300)); - myMainPanel.setSize(new Dimension(400, 300)); - myMainPanel.setMaximumSize(new Dimension(400, 300)); - return myMainPanel; - } + return builder; } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java index 486f3b247c5d..bbef6bc15515 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java @@ -7,6 +7,7 @@ import com.intellij.ide.projectView.ProjectView; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; @@ -20,6 +21,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.util.containers.HashMap; import com.intellij.util.io.ZipUtil; +import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; @@ -27,7 +29,6 @@ import com.jetbrains.edu.learning.courseFormat.Lesson; import com.jetbrains.edu.learning.courseFormat.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; import com.jetbrains.edu.coursecreator.CCLanguageManager; -import com.jetbrains.edu.coursecreator.CCProjectService; import com.jetbrains.edu.coursecreator.CCUtils; import com.jetbrains.edu.coursecreator.ui.CreateCourseArchiveDialog; import org.jetbrains.annotations.NotNull; @@ -56,7 +57,9 @@ public class CCCreateCourseArchive extends DumbAwareAction { @Override public void update(@NotNull AnActionEvent e) { - CCProjectService.setCCActionAvailable(e); + Presentation presentation = e.getPresentation(); + Project project = e.getProject(); + presentation.setEnabledAndVisible(project != null && CCUtils.isCourseCreator(project)); } @Override @@ -70,8 +73,7 @@ public class CCCreateCourseArchive extends DumbAwareAction { } private void createCourseArchive(final Project project, Module module) { - final CCProjectService service = CCProjectService.getInstance(project); - final Course course = service.getCourse(); + final Course course = StudyTaskManager.getInstance(project).getCourse(); if (course == null) return; CreateCourseArchiveDialog dlg = new CreateCourseArchiveDialog(project, this); dlg.show(); @@ -195,8 +197,7 @@ public class CCCreateCourseArchive extends DumbAwareAction { @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") private static void generateJson(@NotNull final Project project, VirtualFile parentDir) { - final CCProjectService service = CCProjectService.getInstance(project); - final Course course = service.getCourse(); + final Course course = StudyTaskManager.getInstance(project).getCourse(); final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create(); final String json = gson.toJson(course); final File courseJson = new File(parentDir.getPath(), EduNames.COURSE_META_FILE); diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateStudyItemActionBase.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateStudyItemActionBase.java index 520ce0961a6a..45310c7c0e8f 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateStudyItemActionBase.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateStudyItemActionBase.java @@ -14,12 +14,13 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.util.Function; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.coursecreator.ui.CCCreateStudyItemDialog; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.StudyItem; -import com.jetbrains.edu.coursecreator.CCProjectService; -import com.jetbrains.edu.coursecreator.CCUtils; -import com.jetbrains.edu.coursecreator.ui.CCCreateStudyItemDialog; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -33,18 +34,16 @@ public abstract class CCCreateStudyItemActionBase extends DumbAwareAction { super(text, description, icon); } - @Override public void actionPerformed(AnActionEvent e) { final IdeView view = e.getData(LangDataKeys.IDE_VIEW); - final Project project = e.getData(CommonDataKeys.PROJECT); + final Project project = e.getProject(); if (view == null || project == null) { return; } final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view); if (directory == null) return; - final CCProjectService service = CCProjectService.getInstance(project); - final Course course = service.getCourse(); + final Course course = StudyTaskManager.getInstance(project).getCourse(); if (course == null) { return; } @@ -54,47 +53,45 @@ public abstract class CCCreateStudyItemActionBase extends DumbAwareAction { @Override public void update(@NotNull AnActionEvent event) { - if (!CCProjectService.setCCActionAvailable(event)) { - return; - } final Presentation presentation = event.getPresentation(); + presentation.setEnabledAndVisible(false); final Project project = event.getData(CommonDataKeys.PROJECT); final IdeView view = event.getData(LangDataKeys.IDE_VIEW); if (project == null || view == null) { presentation.setEnabledAndVisible(false); return; } + if (!StudyUtils.isStudyProject(project)) { + return; + } final PsiDirectory[] directories = view.getDirectories(); if (directories.length == 0) { - presentation.setEnabledAndVisible(false); return; } final PsiDirectory sourceDirectory = DirectoryChooserUtil.getOrChooseDirectory(view); - final CCProjectService service = CCProjectService.getInstance(project); - final Course course = service.getCourse(); + final Course course = StudyTaskManager.getInstance(project).getCourse(); if (course == null || sourceDirectory == null) { - presentation.setEnabledAndVisible(false); return; } - if (!isAddedAsLast(sourceDirectory, project, course) && - getThresholdItem(course, sourceDirectory) == null) { - presentation.setEnabledAndVisible(false); + if (!isAddedAsLast(sourceDirectory, project, course) && getThresholdItem(course, sourceDirectory) == null) { + return; } if (CommonDataKeys.PSI_FILE.getData(event.getDataContext()) != null) { - presentation.setEnabledAndVisible(false); + return; } + presentation.setEnabledAndVisible(true); } @Nullable protected abstract PsiDirectory getParentDir(@NotNull final Project project, - @NotNull final Course course, - @NotNull final PsiDirectory directory); + @NotNull final Course course, + @NotNull final PsiDirectory directory); @Nullable public PsiDirectory createItem(@Nullable final IdeView view, @NotNull final Project project, - @NotNull final PsiDirectory sourceDirectory, @NotNull final Course course) { + @NotNull final PsiDirectory sourceDirectory, @NotNull final Course course) { StudyItem parentItem = getParentItem(course, sourceDirectory); final StudyItem item = getItem(sourceDirectory, project, course, view, parentItem); if (item == null) { 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 316b06d77c04..844fc9ad3796 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 @@ -9,19 +9,19 @@ import com.intellij.ide.util.EditorHelper; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; 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.learning.core.EduNames; 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.Task; -import com.jetbrains.edu.coursecreator.CCLanguageManager; -import com.jetbrains.edu.coursecreator.CCUtils; import icons.InteractiveLearningIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -38,15 +38,15 @@ public class CCCreateTask extends CCCreateStudyItemActionBase { } - private static void createFromTemplateAndOpen(@NotNull final PsiDirectory taskDirectory, - @Nullable final FileTemplate template, - @Nullable IdeView view) { + private static void createFromTemplate(@NotNull final PsiDirectory taskDirectory, + @Nullable final FileTemplate template, + @Nullable IdeView view, boolean open) { if (template == null) { return; } try { final PsiElement file = FileTemplateUtil.createFromTemplate(template, template.getName(), null, taskDirectory); - if (view != null) { + if (view != null && open) { EditorHelper.openInEditor(file, false); view.selectElement(file); } @@ -91,31 +91,29 @@ public class CCCreateTask extends CCCreateStudyItemActionBase { protected PsiDirectory createItemDir(@NotNull final Project project, @NotNull final StudyItem item, @Nullable final IdeView view, @NotNull final PsiDirectory parentDirectory, @NotNull final Course course) { - final PsiDirectory[] taskDirectory = new PsiDirectory[1]; + + final Ref taskDirectory = new Ref<>(); ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { - taskDirectory[0] = DirectoryUtil.createSubdirectories(EduNames.TASK + item.getIndex(), parentDirectory, "\\/"); - if (taskDirectory[0] != null) { + String taskDirName = EduNames.TASK + item.getIndex(); + taskDirectory.set(DirectoryUtil.createSubdirectories(taskDirName, parentDirectory, "\\/")); + if (taskDirectory.get() != null) { CCLanguageManager manager = CCUtils.getStudyLanguageManager(course); if (manager == null) { return; } - createFromTemplateAndOpen(taskDirectory[0], manager.getTestsTemplate(project), view); - createFromTemplateAndOpen(taskDirectory[0], FileTemplateManager.getInstance(project).getInternalTemplate("task.html"), view); + createFromTemplate(taskDirectory.get(), manager.getTestsTemplate(project), view, false); + createFromTemplate(taskDirectory.get(), FileTemplateManager.getInstance(project).getInternalTemplate(EduNames.TASK_HTML), view, false); String defaultExtension = manager.getDefaultTaskFileExtension(); if (defaultExtension != null) { FileTemplate taskFileTemplate = manager.getTaskFileTemplateForExtension(project, defaultExtension); - createFromTemplateAndOpen(taskDirectory[0], taskFileTemplate, view); - if (taskFileTemplate != null) { - String taskFileName = FileUtil.getNameWithoutExtension(taskFileTemplate.getName()); - ((Task)item).addTaskFile(taskFileName + "." + defaultExtension, 1); - } + createFromTemplate(taskDirectory.get(), taskFileTemplate, view, true); } } } }); - return taskDirectory[0]; + return taskDirectory.get(); } @Override diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTaskFile.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTaskFile.java deleted file mode 100644 index eb5598625cce..000000000000 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateTaskFile.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.jetbrains.edu.coursecreator.actions; - -import com.intellij.icons.AllIcons; -import com.intellij.ide.IdeView; -import com.intellij.ide.fileTemplates.FileTemplate; -import com.intellij.ide.fileTemplates.FileTemplateUtil; -import com.intellij.ide.projectView.ProjectView; -import com.intellij.ide.util.DirectoryChooserUtil; -import com.intellij.ide.util.EditorHelper; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.LangDataKeys; -import com.intellij.openapi.actionSystem.Presentation; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.project.DumbAwareAction; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiDirectory; -import com.intellij.psi.PsiElement; -import com.jetbrains.edu.learning.core.EduNames; -import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.Lesson; -import com.jetbrains.edu.learning.courseFormat.Task; -import com.jetbrains.edu.coursecreator.CCLanguageManager; -import com.jetbrains.edu.coursecreator.CCProjectService; -import com.jetbrains.edu.coursecreator.CCUtils; -import com.jetbrains.edu.coursecreator.ui.CreateTaskFileDialog; -import org.jetbrains.annotations.NotNull; - -import static com.intellij.openapi.ui.DialogWrapper.OK_EXIT_CODE; - -public class CCCreateTaskFile extends DumbAwareAction { - - public CCCreateTaskFile() { - super("Task File", "Create new Task File", AllIcons.FileTypes.Text); - } - - @Override - public void actionPerformed(final AnActionEvent e) { - final IdeView view = e.getData(LangDataKeys.IDE_VIEW); - final Project project = e.getData(CommonDataKeys.PROJECT); - - if (view == null || project == null) { - return; - } - final PsiDirectory taskDir = DirectoryChooserUtil.getOrChooseDirectory(view); - if (taskDir == null) return; - PsiDirectory lessonDir = taskDir.getParent(); - if (lessonDir == null) { - return; - } - final CCProjectService service = CCProjectService.getInstance(project); - final Course course = service.getCourse(); - final Lesson lesson = course.getLesson(lessonDir.getName()); - final Task task = lesson.getTask(taskDir.getName()); - - final int index = task.getTaskFiles().size() + 1; - String generatedName = "file" + index; - CreateTaskFileDialog dialog = new CreateTaskFileDialog(project, generatedName, course); - dialog.show(); - if (dialog.getExitCode() != OK_EXIT_CODE) { - return; - } - final String name = dialog.getFileName(); - if (name == null) return; - FileType type = dialog.getFileType(); - if (type == null) { - return; - } - final CCLanguageManager CCLanguageManager = CCUtils.getStudyLanguageManager(course); - if (CCLanguageManager == null) { - return; - } - final String extension = type.getDefaultExtension(); - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - final FileTemplate taskTemplate = CCLanguageManager.getTaskFileTemplateForExtension(project, extension); - final String taskFileName = name + "." + extension; - try { - if (taskTemplate == null) { - VirtualFile file = taskDir.getVirtualFile().createChildData(this, taskFileName); - ProjectView.getInstance(project).select(file, file, false); - FileEditorManager.getInstance(project).openFile(file, true); - } - else { - final PsiElement taskFile = FileTemplateUtil.createFromTemplate(taskTemplate, taskFileName, null, taskDir); - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - EditorHelper.openInEditor(taskFile, false); - view.selectElement(taskFile); - } - }); - } - task.addTaskFile(taskFileName, index); - } - catch (Exception ignored) { - } - } - }); - } - - @Override - public void update(@NotNull AnActionEvent event) { - if (!CCProjectService.setCCActionAvailable(event)) { - return; - } - final Presentation presentation = event.getPresentation(); - final Project project = event.getData(CommonDataKeys.PROJECT); - if (project == null) { - presentation.setVisible(false); - presentation.setEnabled(false); - return; - } - - final IdeView view = event.getData(LangDataKeys.IDE_VIEW); - if (view == null) { - presentation.setVisible(false); - presentation.setEnabled(false); - return; - } - - final PsiDirectory[] directories = view.getDirectories(); - if (directories.length == 0) { - presentation.setVisible(false); - presentation.setEnabled(false); - return; - } - final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view); - final CCProjectService service = CCProjectService.getInstance(project); - final Course course = service.getCourse(); - if (course != null && directory != null && !directory.getName().contains(EduNames.TASK)) { - presentation.setVisible(false); - presentation.setEnabled(false); - return; - } - presentation.setVisible(true); - presentation.setEnabled(true); - } -} diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCDeleteAllAnswerPlaceholdersAction.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCDeleteAllAnswerPlaceholdersAction.java index 55915d05bae4..0916e1b678f4 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCDeleteAllAnswerPlaceholdersAction.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCDeleteAllAnswerPlaceholdersAction.java @@ -3,6 +3,7 @@ package com.jetbrains.edu.coursecreator.actions; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.UndoConfirmationPolicy; import com.intellij.openapi.command.WriteCommandAction; @@ -14,10 +15,11 @@ import com.intellij.openapi.fileEditor.TextEditor; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; -import com.jetbrains.edu.learning.core.EduUtils; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.learning.StudyUtils; +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.coursecreator.CCProjectService; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -26,7 +28,7 @@ import java.util.List; public class CCDeleteAllAnswerPlaceholdersAction extends DumbAwareAction { - public static final String ACTION_NAME = "Delete All Answer Placeholders"; + public static final String ACTION_NAME = "Delete All " + EduNames.PLACEHOLDER + "s"; public CCDeleteAllAnswerPlaceholdersAction() { super(ACTION_NAME); @@ -40,7 +42,7 @@ public class CCDeleteAllAnswerPlaceholdersAction extends DumbAwareAction { if (file == null || project == null) { return; } - final TaskFile taskFile = CCProjectService.getInstance(project).getTaskFile(file); + final TaskFile taskFile = StudyUtils.getTaskFile(project, file); if (taskFile == null) { return; } @@ -57,7 +59,7 @@ public class CCDeleteAllAnswerPlaceholdersAction extends DumbAwareAction { editor = ((TextEditor)fileEditor).getEditor(); } List placeholders = new ArrayList(taskFile.getAnswerPlaceholders()); - final ClearPlaceholders action = new ClearPlaceholders(taskFile, placeholders, editor, file, project); + final ClearPlaceholders action = new ClearPlaceholders(taskFile, placeholders, editor); new WriteCommandAction(project, ACTION_NAME) { protected void run(@NotNull final Result result) throws Throwable { action.redo(); @@ -72,60 +74,57 @@ public class CCDeleteAllAnswerPlaceholdersAction extends DumbAwareAction { } private static void updateView(@NotNull final Editor editor, - @NotNull final VirtualFile file, - @NotNull final Project project) { + @NotNull final TaskFile taskFile) { editor.getMarkupModel().removeAllHighlighters(); - CCProjectService.getInstance(project).drawAnswerPlaceholders(file, editor); + StudyUtils.drawAllWindows(editor, taskFile); } @Override public void update(AnActionEvent e) { - if (!CCProjectService.setCCActionAvailable(e)) { + Presentation presentation = e.getPresentation(); + presentation.setEnabledAndVisible(false); + + Project project = e.getProject(); + if (project == null) { + return; + } + if (!CCUtils.isCourseCreator(project)) { return; } DataContext context = e.getDataContext(); VirtualFile file = CommonDataKeys.VIRTUAL_FILE.getData(context); - final Project project = e.getProject(); - if (file == null || project == null) { - EduUtils.enableAction(e, false); + if (file == null ) { return; } - TaskFile taskFile = CCProjectService.getInstance(project).getTaskFile(file); - if (taskFile == null) { - EduUtils.enableAction(e, false); + TaskFile taskFile = StudyUtils.getTaskFile(project, file); + if (taskFile == null || taskFile.getAnswerPlaceholders().isEmpty()) { return; } - if (taskFile.getAnswerPlaceholders().isEmpty()) { - EduUtils.enableAction(e, false); - } + presentation.setEnabledAndVisible(true); } private static class ClearPlaceholders implements UndoableAction { private final List myPlaceholders; private final Editor myEditor; - private final VirtualFile myFile; - private final Project myProject; TaskFile myTaskFile; - public ClearPlaceholders(TaskFile taskFile, List placeholders, Editor editor, VirtualFile file, Project project) { + public ClearPlaceholders(TaskFile taskFile, List placeholders, Editor editor) { myTaskFile = taskFile; myPlaceholders = placeholders; myEditor = editor; - myFile = file; - myProject = project; } @Override public void undo() throws UnexpectedUndoException { myTaskFile.getAnswerPlaceholders().addAll(myPlaceholders); - updateView(myEditor, myFile, myProject); + updateView(myEditor, myTaskFile); } @Override public void redo() throws UnexpectedUndoException { myTaskFile.getAnswerPlaceholders().clear(); - updateView(myEditor, myFile, myProject); + updateView(myEditor, myTaskFile); } @Nullable diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCDeleteAnswerPlaceholder.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCDeleteAnswerPlaceholder.java deleted file mode 100644 index e385589b32bd..000000000000 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCDeleteAnswerPlaceholder.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.jetbrains.edu.coursecreator.actions; - -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiFile; -import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter; -import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; -import com.jetbrains.edu.learning.courseFormat.TaskFile; -import com.jetbrains.edu.coursecreator.CCProjectService; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -public class CCDeleteAnswerPlaceholder extends CCAnswerPlaceholderAction { - - public CCDeleteAnswerPlaceholder() { - super("Delete Answer Placeholder","Delete answer placeholder", null); - } - - @Override - protected void performAnswerPlaceholderAction(@NotNull CCState state) { - Project project = state.getProject(); - PsiFile psiFile = state.getFile(); - final Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile); - if (document == null) return; - TaskFile taskFile = state.getTaskFile(); - AnswerPlaceholder answerPlaceholder = state.getAnswerPlaceholder(); - final List answerPlaceholders = taskFile.getAnswerPlaceholders(); - if (answerPlaceholders.contains(answerPlaceholder)) { - answerPlaceholders.remove(answerPlaceholder); - final Editor editor = state.getEditor(); - editor.getMarkupModel().removeAllHighlighters(); - CCProjectService.getInstance(project).drawAnswerPlaceholders(psiFile.getVirtualFile(), editor); - EduAnswerPlaceholderPainter.createGuardedBlocks(editor, taskFile, false); - } - } -} \ No newline at end of file diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCShowAnswerPlaceholderDetails.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCEditAnswerPlaceholder.java similarity index 62% rename from python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCShowAnswerPlaceholderDetails.java rename to python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCEditAnswerPlaceholder.java index 7068c5e1c572..622ddacf1cac 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCShowAnswerPlaceholderDetails.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCEditAnswerPlaceholder.java @@ -1,5 +1,7 @@ package com.jetbrains.edu.coursecreator.actions; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiFile; @@ -7,9 +9,9 @@ import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.coursecreator.ui.CCCreateAnswerPlaceholderDialog; import org.jetbrains.annotations.NotNull; -public class CCShowAnswerPlaceholderDetails extends CCAnswerPlaceholderAction { +public class CCEditAnswerPlaceholder extends CCAnswerPlaceholderAction { - public CCShowAnswerPlaceholderDetails() { + public CCEditAnswerPlaceholder() { super("Edit Answer Placeholder", "Edit answer placeholder", null); } @@ -21,9 +23,23 @@ public class CCShowAnswerPlaceholderDetails extends CCAnswerPlaceholderAction { final PsiDirectory lessonDir = taskDir.getParent(); if (lessonDir == null) return; AnswerPlaceholder answerPlaceholder = state.getAnswerPlaceholder(); + if (answerPlaceholder == null) { + return; + } CCCreateAnswerPlaceholderDialog dlg = new CCCreateAnswerPlaceholderDialog(project, answerPlaceholder ); dlg.setTitle("Edit Answer Placeholder"); dlg.show(); } + + @Override + public void update(AnActionEvent e) { + Presentation presentation = e.getPresentation(); + presentation.setEnabledAndVisible(false); + CCState state = getState(e); + if (state == null || state.getAnswerPlaceholder() == null) { + return; + } + presentation.setEnabledAndVisible(true); + } } \ No newline at end of file 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 new file mode 100644 index 000000000000..d86a0ae61754 --- /dev/null +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCEditTaskTextAction.java @@ -0,0 +1,81 @@ +package com.jetbrains.edu.coursecreator.actions; + +import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.fileEditor.FileDocumentManager; +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.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.editor.StudyEditor; +import com.jetbrains.edu.learning.ui.StudyToolWindow; +import org.jetbrains.annotations.NotNull; + +public class CCEditTaskTextAction extends ToggleAction implements DumbAware { + private static final Logger LOG = Logger.getInstance(CCEditTaskTextAction.class); + + public CCEditTaskTextAction() { + super("Editing Mode", "Editing Mode", AllIcons.Modules.Edit); + } + + @Override + public boolean isSelected(AnActionEvent e) { + Project project = e.getProject(); + if (project == null) { + return false; + } + return StudyTaskManager.getInstance(project).getToolWindowMode() == StudyToolWindow.StudyToolWindowMode.EDITING; + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + Project project = e.getProject(); + if (project == null) { + return; + } + StudyToolWindow window = StudyUtils.getStudyToolWindow(project); + if (window == null) { + return; + } + + final StudyEditor selectedEditor = StudyUtils.getSelectedStudyEditor(project); + if (selectedEditor == null) { + StudyTaskManager.getInstance(project).setTurnEditingMode(true); + return; + } + final StudyState studyState = new StudyState(selectedEditor); + VirtualFile taskTextFile = studyState.getTaskDir().findChild(EduNames.TASK_HTML); + if (taskTextFile == null) { + LOG.info("Failed to find task.html"); + return; + } + Document document = FileDocumentManager.getInstance().getDocument(taskTextFile); + if (!state) { + if (document != null) { + FileDocumentManager.getInstance().saveDocument(document); + } + window.leaveEditingMode(project); + return; + } + window.enterEditingMode(taskTextFile, project); + } + + @Override + public void update(@NotNull AnActionEvent e) { + Project project = e.getProject(); + if (project == null) { + return; + } + + if (!CCUtils.isCourseCreator(project)) { + e.getPresentation().setEnabledAndVisible(false); + } + } +} diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCFromCourseArchive.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCFromCourseArchive.java index a7c6a91dc52e..97fdb1ec2e57 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCFromCourseArchive.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCFromCourseArchive.java @@ -22,10 +22,11 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.platform.templates.github.ZipUtil; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.learning.StudyTaskManager; 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.coursecreator.CCProjectService; import com.jetbrains.edu.learning.courseFormat.*; import com.jetbrains.edu.learning.oldCourseFormat.OldCourse; import org.jetbrains.annotations.NotNull; @@ -58,7 +59,6 @@ public class CCFromCourseArchive extends DumbAwareAction { } final String basePath = project.getBasePath(); if (basePath == null) return; - final CCProjectService service = CCProjectService.getInstance(project); Reader reader = null; try { ZipUtil.unzip(null, new File(basePath), new File(virtualFile.getPath()), null, null, true); @@ -77,7 +77,8 @@ public class CCFromCourseArchive extends DumbAwareAction { course = EduUtils.transformOldCourse(oldCourse); } - service.setCourse(course); + StudyTaskManager.getInstance(project).setCourse(course); + course.setCourseMode(CCUtils.COURSE_MODE); project.getBaseDir().refresh(false, true); int index = 1; int taskIndex = 1; @@ -94,7 +95,7 @@ public class CCFromCourseArchive extends DumbAwareAction { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { - createAnswerFile(project, taskDir, taskDir, entry); + createAnswerFile(project, taskDir, entry); } }); } @@ -103,6 +104,7 @@ public class CCFromCourseArchive extends DumbAwareAction { index += 1; taskIndex = 1; } + course.initCourse(true); } catch (FileNotFoundException e) { LOG.error(e.getMessage()); @@ -128,7 +130,6 @@ public class CCFromCourseArchive extends DumbAwareAction { public static void createAnswerFile(@NotNull final Project project, @NotNull final VirtualFile userFileDir, - @NotNull final VirtualFile answerFileDir, @NotNull final Map.Entry taskFileEntry) { final String name = taskFileEntry.getKey(); final TaskFile taskFile = taskFileEntry.getValue(); @@ -183,8 +184,9 @@ public class CCFromCourseArchive extends DumbAwareAction { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { - final String text = document.getText(TextRange.create(offset, offset + answerPlaceholder.getLength())); + final String text = document.getText(TextRange.create(offset, offset + answerPlaceholder.getRealLength())); answerPlaceholder.setTaskText(text); + answerPlaceholder.init(); final VirtualFile hints = project.getBaseDir().findChild(EduNames.HINTS); if (hints != null) { final String hintFile = answerPlaceholder.getHint(); @@ -198,7 +200,7 @@ public class CCFromCourseArchive extends DumbAwareAction { } } - document.replaceString(offset, offset + answerPlaceholder.getLength(), answerPlaceholder.getPossibleAnswer()); + document.replaceString(offset, offset + answerPlaceholder.getRealLength(), answerPlaceholder.getPossibleAnswer()); FileDocumentManager.getInstance().saveDocument(document); } }); diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCHideFromStudent.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCHideFromStudent.java new file mode 100644 index 000000000000..07cd46cfdf20 --- /dev/null +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCHideFromStudent.java @@ -0,0 +1,45 @@ +package com.jetbrains.edu.coursecreator.actions; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.courseFormat.Task; +import com.jetbrains.edu.learning.courseFormat.TaskFile; + +import java.io.IOException; +import java.util.Map; + +public class CCHideFromStudent extends CCTaskFileActionBase { + + private static final Logger LOG = Logger.getInstance(CCHideFromStudent.class); + + @Override + protected void performAction(VirtualFile file, Task task, Course course, Project project) { + Map taskFiles = task.getTaskFiles(); + TaskFile taskFile = StudyUtils.getTaskFile(project, file); + if (taskFile == null) { + return; + } + String name = file.getName(); + VirtualFile patternFile = StudyUtils.getPatternFile(taskFile, name); + ApplicationManager.getApplication().runWriteAction(() -> { + if (patternFile != null) { + try { + patternFile.delete(CCHideFromStudent.class); + } + catch (IOException e) { + LOG.info(e); + } + } + }); + taskFiles.remove(name); + } + + @Override + protected boolean isAvailable(Project project, VirtualFile file) { + return StudyUtils.getTaskFile(project, file) != null; + } +} diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java index 78410c9ad3a1..f1a1f3006bf6 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushCourse.java @@ -4,10 +4,12 @@ import com.intellij.ide.IdeView; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.coursecreator.CCProjectService; import com.jetbrains.edu.learning.stepic.EduStepicConnector; import org.jetbrains.annotations.NotNull; @@ -18,7 +20,9 @@ public class CCPushCourse extends DumbAwareAction { @Override public void update(@NotNull AnActionEvent e) { - CCProjectService.setCCActionAvailable(e); + Presentation presentation = e.getPresentation(); + Project project = e.getProject(); + presentation.setEnabledAndVisible(project != null && CCUtils.isCourseCreator(project)); } @Override @@ -28,7 +32,7 @@ public class CCPushCourse extends DumbAwareAction { if (view == null || project == null) { return; } - final Course course = CCProjectService.getInstance(project).getCourse(); + final Course course = StudyTaskManager.getInstance(project).getCourse(); if (course == null) { return; } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java index 068b82bf0b1a..4e3f7ecc7414 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCPushLesson.java @@ -11,9 +11,9 @@ import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiDirectory; +import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; -import com.jetbrains.edu.coursecreator.CCProjectService; import com.jetbrains.edu.learning.stepic.EduStepicConnector; import org.jetbrains.annotations.NotNull; @@ -30,7 +30,7 @@ public class CCPushLesson extends DumbAwareAction { if (view == null || project == null) { return; } - final Course course = CCProjectService.getInstance(project).getCourse(); + final Course course = StudyTaskManager.getInstance(project).getCourse(); if (course == null) { return; } @@ -51,7 +51,7 @@ public class CCPushLesson extends DumbAwareAction { if (view == null || project == null) { return; } - final Course course = CCProjectService.getInstance(project).getCourse(); + final Course course = StudyTaskManager.getInstance(project).getCourse(); if (course == null) { return; } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCRunTestsAction.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCRunTestsAction.java deleted file mode 100644 index f6982733db5b..000000000000 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCRunTestsAction.java +++ /dev/null @@ -1,222 +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.coursecreator.actions; - -import com.intellij.execution.Location; -import com.intellij.execution.actions.ConfigurationContext; -import com.intellij.icons.AllIcons; -import com.intellij.ide.fileTemplates.FileTemplate; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.Presentation; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.jetbrains.edu.learning.core.EduNames; -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.Task; -import com.jetbrains.edu.learning.courseFormat.TaskFile; -import com.jetbrains.edu.coursecreator.CCLanguageManager; -import com.jetbrains.edu.coursecreator.CCProjectService; -import com.jetbrains.edu.coursecreator.CCUtils; -import org.jetbrains.annotations.NotNull; - -import java.io.IOException; -import java.util.Map; - -public abstract class CCRunTestsAction extends AnAction { - private static final Logger LOG = Logger.getInstance(CCRunTestsAction.class.getName()); - - public CCRunTestsAction() { - getTemplatePresentation().setIcon(AllIcons.Actions.Lightning); - } - - @Override - public void update(@NotNull AnActionEvent e) { - final Presentation presentation = e.getPresentation(); - if (!CCProjectService.setCCActionAvailable(e)) { - presentation.setEnabledAndVisible(false); - return; - } - - presentation.setEnabledAndVisible(false); - - final ConfigurationContext context = ConfigurationContext.getFromContext(e.getDataContext()); - Location location = context.getLocation(); - if (location == null) { - return; - } - PsiElement psiElement = location.getPsiElement(); - PsiFile psiFile = psiElement.getContainingFile(); - Project project = e.getProject(); - if (project == null || psiFile == null) { - return; - } - TaskFile taskFile = CCProjectService.getInstance(project).getTaskFile(psiFile.getVirtualFile()); - if (taskFile == null) { - return; - } - presentation.setEnabledAndVisible(true); - presentation.setText("Run tests from '" + FileUtil.getNameWithoutExtension(psiFile.getName()) + "'"); - } - - public void actionPerformed(@NotNull AnActionEvent e) { - final ConfigurationContext context = ConfigurationContext.getFromContext(e.getDataContext()); - run(context); - } - - private void run(final @NotNull ConfigurationContext context) { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - final Project project = context.getProject(); - PsiElement location = context.getPsiLocation(); - final Course course = CCProjectService.getInstance(project).getCourse(); - if (course == null || location == null) { - return; - } - PsiFile psiFile = location.getContainingFile(); - final VirtualFile virtualFile = psiFile.getVirtualFile(); - final VirtualFile taskDir = virtualFile.getParent(); - if (taskDir == null) { - return; - } - VirtualFile lessonDir = taskDir.getParent(); - Lesson lesson = course.getLesson(lessonDir.getName()); - if (lesson == null) { - return; - } - final Task task = lesson.getTask(taskDir.getName()); - if (task == null) { - return; - } - clearTestEnvironment(taskDir, project); - CCLanguageManager manager = CCUtils.getStudyLanguageManager(course); - if (manager == null) { - return; - } - for (final Map.Entry entry : task.getTaskFiles().entrySet()) { - final String name = entry.getKey(); - createTaskFileForTest(taskDir, name, entry.getValue(), project); - } - FileTemplate testsTemplate = manager.getTestsTemplate(project); - if (testsTemplate == null) { - return; - } - VirtualFile testFile = taskDir.findChild(testsTemplate.getName() + "." + testsTemplate.getExtension()); - if (testFile == null) { - return; - } - executeTests(project, virtualFile, taskDir, testFile); - } - }); - } - - private static void createTaskFileForTest(@NotNull final VirtualFile taskDir, final String fileName, @NotNull final TaskFile taskFile, - @NotNull final Project project) { - final VirtualFile answerFile = taskDir.findChild(fileName); - if (answerFile == null) { - LOG.debug("could not find answer file " + fileName); - return; - } - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - final FileDocumentManager documentManager = FileDocumentManager.getInstance(); - documentManager.saveAllDocuments(); - } - }); - EduUtils.flushWindows(taskFile, answerFile, false); - createResourceFiles(answerFile, project); - } - - public static void clearTestEnvironment(@NotNull final VirtualFile taskDir, @NotNull final Project project) { - try { - VirtualFile ideaDir = project.getBaseDir().findChild(".idea"); - if (ideaDir == null) { - LOG.debug("idea directory doesn't exist"); - return; - } - VirtualFile courseResourceDir = ideaDir.findChild(EduNames.COURSE); - if (courseResourceDir != null) { - courseResourceDir.delete(project); - } - VirtualFile[] taskDirChildren = taskDir.getChildren(); - for (VirtualFile file : taskDirChildren) { - if (file.getName().contains(EduNames.WINDOWS_POSTFIX)) { - file.delete(project); - } - } - } - catch (IOException e) { - LOG.error(e); - } - } - - protected abstract void executeTests(@NotNull final Project project, - @NotNull final VirtualFile virtualFile, - @NotNull final VirtualFile taskDir, - @NotNull final VirtualFile testFile); - - //some tests could compare task files after user modifications with initial task files - private static void createResourceFiles(@NotNull final VirtualFile file, @NotNull final Project project) { - VirtualFile taskDir = file.getParent(); - int index = EduUtils.getIndex(taskDir.getName(), EduNames.TASK); - VirtualFile lessonDir = taskDir.getParent(); - int lessonIndex = EduUtils.getIndex(lessonDir.getName(), EduNames.LESSON); - Course course = CCProjectService.getInstance(project).getCourse(); - if (course == null) { - return; - } - VirtualFile ideaDir = project.getBaseDir().findChild(".idea"); - assert ideaDir != null; - try { - VirtualFile courseResourceDir = findOrCreateDir(project, ideaDir, EduNames.COURSE); - VirtualFile lessonResourceDir = findOrCreateDir(project, courseResourceDir, lessonDir.getName()); - VirtualFile taskResourceDir = findOrCreateDir(project, lessonResourceDir, taskDir.getName()); - if (EduUtils.indexIsValid(lessonIndex, course.getLessons())) { - Lesson lesson = course.getLessons().get(lessonIndex); - if (EduUtils.indexIsValid(index, lesson.getTaskList())) { - Task task = lesson.getTaskList().get(index); - for (Map.Entry entry : task.getTaskFiles().entrySet()) { - TaskFile taskFile = new TaskFile(); - TaskFile.copy(entry.getValue(), taskFile); - EduUtils.createStudentFileFromAnswer(project, taskResourceDir, taskDir, entry.getKey(), taskFile); - } - } - } - } - catch (IOException e) { - LOG.error(e); - } - } - - private static VirtualFile findOrCreateDir(@NotNull final Project project, @NotNull final VirtualFile dir, String name) - throws IOException { - VirtualFile targetDir = dir.findChild(name); - if (targetDir == null) { - targetDir = dir.createChildDirectory(project, name); - } - return targetDir; - } -} diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCShowPreview.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCShowPreview.java index f0662db25052..39a2ce7f4e0d 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCShowPreview.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCShowPreview.java @@ -37,12 +37,13 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiFile; import com.intellij.ui.JBColor; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter; 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.TaskFile; -import com.jetbrains.edu.coursecreator.CCProjectService; import com.jetbrains.edu.coursecreator.CCUtils; import org.jetbrains.annotations.NotNull; @@ -61,24 +62,23 @@ public class CCShowPreview extends DumbAwareAction { @Override public void update(@NotNull AnActionEvent e) { - if (!CCProjectService.setCCActionAvailable(e)) { - return; - } + Presentation presentation = e.getPresentation(); + presentation.setEnabledAndVisible(false); Project project = e.getProject(); if (project == null) { return; } - Presentation presentation = e.getPresentation(); - presentation.setEnabledAndVisible(false); + if (!CCUtils.isCourseCreator(project)) { + return; + } final PsiFile file = CommonDataKeys.PSI_FILE.getData(e.getDataContext()); - if (file != null && CCProjectService.getInstance(project).getTaskFile(file.getVirtualFile()) != null) { + if (file != null && StudyUtils.getTaskFile(project, file.getVirtualFile()) != null) { presentation.setEnabledAndVisible(true); } } @Override public void actionPerformed(@NotNull AnActionEvent e) { - //TODO: need to rewrite this action using new GENERATED_ROOT_FOLDER final Project project = e.getProject(); Module module = LangDataKeys.MODULE.getData(e.getDataContext()); if (project == null || module == null) { @@ -88,13 +88,12 @@ public class CCShowPreview extends DumbAwareAction { if (file == null) { return; } - final CCProjectService service = CCProjectService.getInstance(project); - Course course = service.getCourse(); + Course course = StudyTaskManager.getInstance(project).getCourse(); if (course == null) { return; } VirtualFile virtualFile = file.getVirtualFile(); - TaskFile taskFile = service.getTaskFile(virtualFile); + TaskFile taskFile = StudyUtils.getTaskFile(project, virtualFile); if (taskFile == null) { return; } @@ -149,7 +148,7 @@ public class CCShowPreview extends DumbAwareAction { } }); for (AnswerPlaceholder answerPlaceholder : taskFileCopy.getAnswerPlaceholders()) { - EduAnswerPlaceholderPainter.drawAnswerPlaceholder(createdEditor, answerPlaceholder, true, JBColor.BLUE); + EduAnswerPlaceholderPainter.drawAnswerPlaceholder(createdEditor, answerPlaceholder, JBColor.BLUE); } JPanel header = new JPanel(); header.setLayout(new BoxLayout(header, BoxLayout.Y_AXIS)); diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCTaskFileActionBase.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCTaskFileActionBase.java new file mode 100644 index 000000000000..4f0632e82ee8 --- /dev/null +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCTaskFileActionBase.java @@ -0,0 +1,59 @@ +package com.jetbrains.edu.coursecreator.actions; + +import com.intellij.ide.projectView.ProjectView; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.courseFormat.Task; + +public abstract class CCTaskFileActionBase extends AnAction { + + @Override + public void actionPerformed(final AnActionEvent e) { + Project project = e.getProject(); + if (project == null) { + return; + } + final VirtualFile file = CommonDataKeys.VIRTUAL_FILE.getData(e.getDataContext()); + if (file == null) { + return; + } + VirtualFile taskVF = file.getParent(); + if (taskVF == null) { + return; + } + Task task = StudyUtils.getTask(project, taskVF); + if (task == null) { + return; + } + Course course = StudyTaskManager.getInstance(project).getCourse(); + performAction(file, task, course, project); + ProjectView.getInstance(project).refresh(); + } + + protected abstract void performAction(VirtualFile file, Task task, Course course, Project project); + + + @Override + public void update(AnActionEvent e) { + Project project = e.getProject(); + Presentation presentation = e.getPresentation(); + if (project == null || !CCUtils.isCourseCreator(project)) { + presentation.setEnabledAndVisible(false); + return; + } + VirtualFile file = CommonDataKeys.VIRTUAL_FILE.getData(e.getDataContext()); + if (file == null || file.isDirectory() || !isAvailable(project, file)) { + presentation.setEnabledAndVisible(false); + } + } + + protected abstract boolean isAvailable(Project project, VirtualFile file); +} diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCLessonMoveHandlerDelegate.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCLessonMoveHandlerDelegate.java similarity index 93% rename from python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCLessonMoveHandlerDelegate.java rename to python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCLessonMoveHandlerDelegate.java index 685ac3741785..18ca9364c96f 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCLessonMoveHandlerDelegate.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCLessonMoveHandlerDelegate.java @@ -1,4 +1,4 @@ -package com.jetbrains.edu.coursecreator; +package com.jetbrains.edu.coursecreator.handlers; import com.intellij.ide.IdeView; import com.intellij.ide.util.DirectoryChooserUtil; @@ -18,7 +18,10 @@ import com.intellij.psi.PsiReference; import com.intellij.refactoring.move.MoveCallback; import com.intellij.refactoring.move.MoveHandlerDelegate; import com.intellij.util.Function; +import com.jetbrains.edu.coursecreator.CCUtils; import com.jetbrains.edu.coursecreator.ui.CCMoveStudyItemDialog; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.coursecreator.CCUtils; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; @@ -67,7 +70,10 @@ public class CCLessonMoveHandlerDelegate extends MoveHandlerDelegate { if (targetContainer == null || !(targetContainer instanceof PsiDirectory)) { return; } - final Course course = CCProjectService.getInstance(project).getCourse(); + final Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null) { + return; + } final PsiDirectory sourceDirectory = (PsiDirectory)elements[0]; final Lesson sourceLesson = course.getLesson(sourceDirectory.getName()); final Lesson targetLesson = course.getLesson(((PsiDirectory)targetContainer).getName()); @@ -129,6 +135,6 @@ public class CCLessonMoveHandlerDelegate extends MoveHandlerDelegate { DataContext dataContext, @Nullable PsiReference reference, Editor editor) { - return CCProjectService.getInstance(project).getCourse() != null; + return CCUtils.isCourseCreator(project); } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCLessonRenameHandler.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCLessonRenameHandler.java similarity index 94% rename from python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCLessonRenameHandler.java rename to python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCLessonRenameHandler.java index 9b29cbafec2e..0efe1110a8ca 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCLessonRenameHandler.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCLessonRenameHandler.java @@ -1,4 +1,4 @@ -package com.jetbrains.edu.coursecreator; +package com.jetbrains.edu.coursecreator.handlers; import com.intellij.ide.TitledHandler; import com.intellij.openapi.project.Project; diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCRenameHandler.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCRenameHandler.java similarity index 88% rename from python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCRenameHandler.java rename to python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCRenameHandler.java index 27bd42ca2484..e98ba1ab133f 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCRenameHandler.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCRenameHandler.java @@ -1,4 +1,4 @@ -package com.jetbrains.edu.coursecreator; +package com.jetbrains.edu.coursecreator.handlers; import com.intellij.ide.projectView.ProjectView; import com.intellij.openapi.actionSystem.CommonDataKeys; @@ -12,6 +12,8 @@ import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.refactoring.rename.RenameHandler; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.StudyItem; import org.jetbrains.annotations.NotNull; @@ -23,9 +25,7 @@ public abstract class CCRenameHandler implements RenameHandler { if (element == null || !(element instanceof PsiDirectory)) { return false; } - CCProjectService instance = CCProjectService.getInstance(element.getProject()); - Course course = instance.getCourse(); - if (course == null) { + if (!CCUtils.isCourseCreator(element.getProject())) { return false; } VirtualFile directory = ((PsiDirectory)element).getVirtualFile(); @@ -44,8 +44,10 @@ public abstract class CCRenameHandler implements RenameHandler { PsiElement element = CommonDataKeys.PSI_ELEMENT.getData(dataContext); assert element != null; PsiDirectory directory = (PsiDirectory)element; - CCProjectService instance = CCProjectService.getInstance(project); - Course course = instance.getCourse(); + Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null) { + return; + } rename(project, course, directory); ProjectView.getInstance(project).refresh(); } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCTaskMoveHandlerDelegate.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCTaskMoveHandlerDelegate.java similarity index 94% rename from python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCTaskMoveHandlerDelegate.java rename to python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCTaskMoveHandlerDelegate.java index 380e0556112b..91083e9f9edb 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCTaskMoveHandlerDelegate.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCTaskMoveHandlerDelegate.java @@ -1,4 +1,4 @@ -package com.jetbrains.edu.coursecreator; +package com.jetbrains.edu.coursecreator.handlers; import com.intellij.ide.IdeView; import com.intellij.ide.util.DirectoryChooserUtil; @@ -18,13 +18,15 @@ import com.intellij.psi.PsiReference; import com.intellij.refactoring.move.MoveCallback; import com.intellij.refactoring.move.MoveHandlerDelegate; import com.intellij.util.Function; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.coursecreator.ui.CCMoveStudyItemDialog; +import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.core.EduNames; 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.Task; -import com.jetbrains.edu.coursecreator.ui.CCMoveStudyItemDialog; import org.jetbrains.annotations.Nullable; import java.io.IOException; @@ -59,9 +61,9 @@ public class CCTaskMoveHandlerDelegate extends MoveHandlerDelegate { if (sourceDirectory == null) { return false; } - CCProjectService service = CCProjectService.getInstance(sourceDirectory.getProject()); - Course course = service.getCourse(); - if (course == null) { + Project project = sourceDirectory.getProject(); + Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null || !CCUtils.isCourseCreator(project)) { return false; } return EduUtils.getTask(sourceDirectory, course) != null; @@ -86,7 +88,7 @@ public class CCTaskMoveHandlerDelegate extends MoveHandlerDelegate { Messages.showInfoMessage("Tasks can be moved only to other lessons or inside lesson", "Incorrect Target For Move"); return; } - final Course course = CCProjectService.getInstance(project).getCourse(); + final Course course = StudyTaskManager.getInstance(project).getCourse(); final PsiDirectory sourceDirectory = (PsiDirectory)elements[0]; final Task taskToMove = EduUtils.getTask(sourceDirectory, course); @@ -192,6 +194,6 @@ public class CCTaskMoveHandlerDelegate extends MoveHandlerDelegate { DataContext dataContext, @Nullable PsiReference reference, Editor editor) { - return CCProjectService.getInstance(project).getCourse() != null; + return CCUtils.isCourseCreator(project); } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCTaskRenameHandler.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCTaskRenameHandler.java similarity index 96% rename from python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCTaskRenameHandler.java rename to python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCTaskRenameHandler.java index 0f30a1d689f5..98bcba87c6c8 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCTaskRenameHandler.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCTaskRenameHandler.java @@ -1,4 +1,4 @@ -package com.jetbrains.edu.coursecreator; +package com.jetbrains.edu.coursecreator.handlers; import com.intellij.ide.TitledHandler; import com.intellij.openapi.project.Project; diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/projectView/CCDirectoryNode.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/projectView/CCDirectoryNode.java deleted file mode 100644 index 551d344e3772..000000000000 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/projectView/CCDirectoryNode.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.jetbrains.edu.coursecreator.projectView; - -import com.intellij.ide.projectView.PresentationData; -import com.intellij.ide.projectView.ViewSettings; -import com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode; -import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiDirectory; -import com.intellij.ui.SimpleTextAttributes; -import com.jetbrains.edu.learning.core.EduNames; -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.Task; -import com.jetbrains.edu.coursecreator.CCProjectService; -import icons.InteractiveLearningIcons; -import org.jetbrains.annotations.NotNull; - -public class CCDirectoryNode extends PsiDirectoryNode { - private final PsiDirectory myValue; - private final Project myProject; - - public CCDirectoryNode(@NotNull final Project project, - PsiDirectory value, - ViewSettings viewSettings) { - super(project, value, viewSettings); - myValue = value; - myProject = project; - } - - @Override - protected void updateImpl(PresentationData data) { - String valueName = myValue.getName(); - final CCProjectService service = CCProjectService.getInstance(myProject); - final Course course = service.getCourse(); - if (course == null) return; - if (myProject.getBaseDir().equals(myValue.getVirtualFile())) { - data.clearText(); - data.setIcon(InteractiveLearningIcons.Course); - data.addText(course.getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES); - data.addText(" (" + valueName + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES); - return; - } - final Lesson lesson = course.getLesson(valueName); - if (lesson != null) { - data.clearText(); - data.setIcon(InteractiveLearningIcons.Lesson); - data.addText(lesson.getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES); - return; - } - else { - final PsiDirectory parentDir = myValue.getParentDirectory(); - if (parentDir != null) { - final Lesson parentLesson = course.getLesson(parentDir.getName()); - if (parentLesson != null) { - final Task task = parentLesson.getTask(valueName); - if (task != null) { - data.clearText(); - data.setIcon(InteractiveLearningIcons.Task); - data.addText(task.getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES); - return; - } - } - } - } - data.setPresentableText(valueName); - } - - @Override - public int getTypeSortWeight(boolean sortByType) { - String name = myValue.getName(); - if (name.startsWith(EduNames.LESSON) || name.startsWith(EduNames.TASK)) { - String logicalName = name.contains(EduNames.LESSON) ? EduNames.LESSON : EduNames.TASK; - int index = EduUtils.getIndex(name, logicalName) + 1; - return index != -1 ? index + 1: 0; - } - return 0; - } - - @Override - public String getNavigateActionText(boolean focusEditor) { - return null; - } -} 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 d4f39fbefd2f..2702e3d4cf56 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 @@ -1,23 +1,22 @@ package com.jetbrains.edu.coursecreator.projectView; -import com.intellij.ide.projectView.TreeStructureProvider; import com.intellij.ide.projectView.ViewSettings; import com.intellij.ide.projectView.impl.nodes.PsiFileNode; import com.intellij.ide.util.treeView.AbstractTreeNode; -import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; -import com.jetbrains.edu.learning.core.EduNames; -import com.jetbrains.edu.coursecreator.CCProjectService; import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.core.EduNames; +import com.jetbrains.edu.learning.projectView.StudyTreeStructureProvider; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; -public class CCTreeStructureProvider implements TreeStructureProvider, DumbAware { +public class CCTreeStructureProvider extends StudyTreeStructureProvider { @NotNull @Override public Collection modify(@NotNull AbstractTreeNode parent, @@ -26,18 +25,16 @@ public class CCTreeStructureProvider implements TreeStructureProvider, DumbAware if (!needModify(parent)) { return children; } - Collection nodes = new ArrayList(); + Collection modifiedChildren = new ArrayList(super.modify(parent, children, settings)); + for (AbstractTreeNode node : children) { Project project = node.getProject(); - if (project != null) { if (node.getValue() instanceof PsiDirectory) { String name = ((PsiDirectory)node.getValue()).getName(); - if (CCUtils.GENERATED_FILES_FOLDER.equals(name)) { + if ("zip".equals(FileUtilRt.getExtension(name))) { + modifiedChildren.add(node); continue; } - PsiDirectory directory = (PsiDirectory)node.getValue(); - nodes.add(new CCDirectoryNode(project, directory, settings)); - continue; } if (node instanceof PsiFileNode) { PsiFileNode fileNode = (PsiFileNode)node; @@ -45,34 +42,19 @@ public class CCTreeStructureProvider implements TreeStructureProvider, DumbAware if (virtualFile == null) { continue; } - if (virtualFile.getName().contains(EduNames.WINDOWS_POSTFIX)) { - continue; - } - - if (virtualFile.getParent().getName().contains(EduNames.TASK) && !CCProjectService.getInstance(project).isTaskFile(virtualFile)) { - nodes.add(new CCStudentInvisibleFileNode(project, ((PsiFileNode)node).getValue(), settings)); - continue; + if (StudyUtils.getTaskFile(project, virtualFile) == null && !EduNames.TASK_HTML.equals(virtualFile.getName())) { + modifiedChildren.add(new CCStudentInvisibleFileNode(project, ((PsiFileNode)node).getValue(), settings)); } } - nodes.add(node); - } } - return nodes; + return modifiedChildren; } - private static boolean needModify(@NotNull final AbstractTreeNode parent) { + protected boolean needModify(@NotNull final AbstractTreeNode parent) { Project project = parent.getProject(); - if (project != null) { - if (CCProjectService.getInstance(project).getCourse() == null) { - return false; - } + if (project == null) { + return false; } - return true; - } - - @Nullable - @Override - public Object getData(Collection selected, String dataName) { - return null; + return CCUtils.isCourseCreator(project); } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateCourseArchiveDialog.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateCourseArchiveDialog.java index d1bc4fca3469..538665e54864 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateCourseArchiveDialog.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateCourseArchiveDialog.java @@ -2,8 +2,8 @@ package com.jetbrains.edu.coursecreator.ui; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; +import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.coursecreator.CCProjectService; import com.jetbrains.edu.coursecreator.actions.CCCreateCourseArchive; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -17,7 +17,7 @@ public class CreateCourseArchiveDialog extends DialogWrapper { public CreateCourseArchiveDialog(@NotNull final Project project, CCCreateCourseArchive action) { super(project); - Course course = CCProjectService.getInstance(project).getCourse(); + Course course = StudyTaskManager.getInstance(project).getCourse(); assert course != null; setTitle("Create Course Archive"); myPanel = new CreateCourseArchivePanel(project, this, course.getName()); diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateCourseArchivePanel.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateCourseArchivePanel.java index c84a5cdf2b26..1eeb2edd0b14 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateCourseArchivePanel.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CreateCourseArchivePanel.java @@ -33,7 +33,7 @@ public class CreateCourseArchivePanel extends JPanel { myNameField.setText(sanitizedName.startsWith("_") ? EduNames.COURSE : sanitizedName); myLocationField.setText(project.getBasePath()); FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor(); - myLocationField.addBrowseFolderListener("Choose location folder", null, project, descriptor); + myLocationField.addBrowseFolderListener("Choose Location Folder", null, project, descriptor); myLocationField.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { diff --git a/python/educational-core/student/resources/META-INF/plugin.xml b/python/educational-core/student/resources/META-INF/plugin.xml index 296fd98bd005..1ebb41dc0aed 100644 --- a/python/educational-core/student/resources/META-INF/plugin.xml +++ b/python/educational-core/student/resources/META-INF/plugin.xml @@ -36,7 +36,9 @@ + + > diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyActionListener.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyActionListener.java new file mode 100644 index 000000000000..525adc2eddd3 --- /dev/null +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyActionListener.java @@ -0,0 +1,10 @@ +package com.jetbrains.edu.learning; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.extensions.ExtensionPointName; + +public interface StudyActionListener { + ExtensionPointName EP_NAME = ExtensionPointName.create("Edu.studyActionListener"); + + void beforeCheck(AnActionEvent event); +} diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyActionsProvider.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyActionsProvider.java new file mode 100644 index 000000000000..4aca599efea7 --- /dev/null +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyActionsProvider.java @@ -0,0 +1,10 @@ +package com.jetbrains.edu.learning; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.extensions.ExtensionPointName; + +public interface StudyActionsProvider { + ExtensionPointName EP_NAME = ExtensionPointName.create("Edu.studyActionsProvider"); + + AnAction[] getActions(); +} diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyAnswerPlaceholderExtendWordHandler.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyAnswerPlaceholderExtendWordHandler.java index 8ebc8f4dadd6..ad364dfc02f2 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyAnswerPlaceholderExtendWordHandler.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyAnswerPlaceholderExtendWordHandler.java @@ -59,6 +59,6 @@ public class StudyAnswerPlaceholderExtendWordHandler implements ExtendWordSelect Document document = FileDocumentManager.getInstance().getDocument(file); assert document != null; int startOffset = placeholder.getRealStartOffset(document); - return Collections.singletonList(new TextRange(startOffset, startOffset + placeholder.getLength())); + return Collections.singletonList(new TextRange(startOffset, startOffset + placeholder.getRealLength())); } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyBasePluginConfigurator.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyBasePluginConfigurator.java index 74410d74ce84..4fde0a889748 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyBasePluginConfigurator.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyBasePluginConfigurator.java @@ -48,9 +48,6 @@ public abstract class StudyBasePluginConfigurator implements StudyPluginConfigur public FileEditorManagerListener getFileEditorManagerListener(@NotNull Project project, @NotNull StudyToolWindow toolWindow) { return new FileEditorManagerListener() { - - private static final String EMPTY_TASK_TEXT = "Please, open any task to see task description"; - @Override public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) { Task task = getTask(file); @@ -59,7 +56,7 @@ public abstract class StudyBasePluginConfigurator implements StudyPluginConfigur @Override public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) { - toolWindow.setTaskText(EMPTY_TASK_TEXT); + toolWindow.setEmptyText(project); } @Override @@ -85,10 +82,10 @@ public abstract class StudyBasePluginConfigurator implements StudyPluginConfigur private void setTaskText(@Nullable final Task task, @Nullable final VirtualFile taskDirectory) { String text = StudyUtils.getTaskTextFromTask(task, taskDirectory); if (text == null) { - toolWindow.setTaskText(EMPTY_TASK_TEXT); + toolWindow.setEmptyText(project); return; } - toolWindow.setTaskText(text); + toolWindow.setTaskText(text, taskDirectory, project); } }; } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyMoveDelegate.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyMoveDelegate.java index 7cd21a26b510..abac20c57afe 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyMoveDelegate.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyMoveDelegate.java @@ -8,6 +8,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; import com.intellij.refactoring.move.MoveCallback; import com.intellij.refactoring.move.MoveHandlerDelegate; +import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; import org.jetbrains.annotations.Nullable; @@ -22,7 +23,7 @@ public class StudyMoveDelegate extends MoveHandlerDelegate{ if (elements.length == 1) { Project project = elements[0].getProject(); Course course = StudyTaskManager.getInstance(project).getCourse(); - if (course == null) { + if (course == null || !EduNames.STUDY.equals(course.getCourseMode())) { return false; } return !StudyUtils.isRenameableOrMoveable(project, course, elements[0]); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyPluginConfigurator.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyPluginConfigurator.java index 61d9e78570d6..db5ee3b39143 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyPluginConfigurator.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyPluginConfigurator.java @@ -40,6 +40,6 @@ public interface StudyPluginConfigurator { StudyAfterCheckAction[] getAfterCheckActions(); @NotNull String getLanguageScriptUrl(); - + boolean accept(@NotNull final Project project); } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java index 205b7929960a..ac816c7ebde5 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java @@ -25,13 +25,13 @@ import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowAnchor; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.util.containers.hash.HashMap; -import com.jetbrains.edu.learning.actions.StudyToolbarAction; import com.jetbrains.edu.learning.core.EduNames; 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.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; +import com.jetbrains.edu.learning.actions.*; import com.jetbrains.edu.learning.editor.StudyEditorFactoryListener; import com.jetbrains.edu.learning.ui.StudyProgressToolWindowFactory; import com.jetbrains.edu.learning.ui.StudyToolWindow; @@ -110,9 +110,9 @@ public class StudyProjectComponent implements ProjectComponent { List actionsOnToolbar = window.getActions(true); if (actionsOnToolbar != null) { for (AnAction action : actionsOnToolbar) { - if (action instanceof StudyToolbarAction) { - String id = ((StudyToolbarAction)action).getActionId(); - String[] shortcuts = ((StudyToolbarAction)action).getShortcuts(); + if (action instanceof StudyActionWithShortcut) { + String id = ((StudyActionWithShortcut)action).getActionId(); + String[] shortcuts = ((StudyActionWithShortcut)action).getShortcuts(); if (shortcuts != null) { addShortcut(id, shortcuts); } @@ -227,7 +227,7 @@ public class StudyProjectComponent implements ProjectComponent { keymap.addShortcut(actionShortcut.first, new KeyboardShortcut(KeyStroke.getKeyStroke(actionShortcut.second), null)); } } - } + } } myListener = null; } @@ -287,30 +287,30 @@ public class StudyProjectComponent implements ProjectComponent { final VirtualFile createdFile = event.getFile(); final VirtualFile taskDir = createdFile.getParent(); final Course course = StudyTaskManager.getInstance(myProject).getCourse(); + if (course == null || !EduNames.STUDY.equals(course.getCourseMode())) { + return; + } if (taskDir != null && taskDir.getName().contains(EduNames.TASK)) { int taskIndex = EduUtils.getIndex(taskDir.getName(), EduNames.TASK); final VirtualFile lessonDir = taskDir.getParent(); if (lessonDir != null && lessonDir.getName().contains(EduNames.LESSON)) { int lessonIndex = EduUtils.getIndex(lessonDir.getName(), EduNames.LESSON); - if (course != null) { - List lessons = course.getLessons(); - if (StudyUtils.indexIsValid(lessonIndex, lessons)) { - final Lesson lesson = lessons.get(lessonIndex); - final List tasks = lesson.getTaskList(); - if (StudyUtils.indexIsValid(taskIndex, tasks)) { - final Task task = tasks.get(taskIndex); - final TaskFile taskFile = new TaskFile(); - taskFile.initTaskFile(task, false); - taskFile.setUserCreated(true); - final String name = createdFile.getName(); - taskFile.name = name; - task.getTaskFiles().put(name, taskFile); - } + List lessons = course.getLessons(); + if (StudyUtils.indexIsValid(lessonIndex, lessons)) { + final Lesson lesson = lessons.get(lessonIndex); + final List tasks = lesson.getTaskList(); + if (StudyUtils.indexIsValid(taskIndex, tasks)) { + final Task task = tasks.get(taskIndex); + final TaskFile taskFile = new TaskFile(); + taskFile.initTaskFile(task, false); + taskFile.setUserCreated(true); + final String name = createdFile.getName(); + taskFile.name = name; + task.getTaskFiles().put(name, taskFile); } } } } } } - } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java index b534bba9118b..024cd09e9245 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java @@ -14,6 +14,7 @@ import com.intellij.util.xmlb.XmlSerializer; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.*; import com.jetbrains.edu.learning.oldCourseFormat.OldCourse; +import com.jetbrains.edu.learning.ui.StudyToolWindow; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -38,6 +39,8 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb public Map> myUserTests = new HashMap<>(); public List myInvisibleFiles = new ArrayList<>(); public boolean myShouldUseJavaFx = StudyUtils.hasJavaFx(); + private StudyToolWindow.StudyToolWindowMode myToolWindowMode = StudyToolWindow.StudyToolWindowMode.TEXT; + private boolean myTurnEditingMode = false; private StudyTaskManager() { } @@ -71,7 +74,7 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb @NotNull public List getUserTests(@NotNull final Task task) { final List userTests = myUserTests.get(task); - return userTests != null ? userTests : Collections.emptyList(); + return userTests != null ? userTests : Collections.emptyList(); } public void removeUserTest(@NotNull final Task task, @NotNull final UserTest userTest) { @@ -260,4 +263,20 @@ public class StudyTaskManager implements PersistentStateComponent, Dumb public void setShouldUseJavaFx(boolean shouldUseJavaFx) { this.myShouldUseJavaFx = shouldUseJavaFx; } + + public StudyToolWindow.StudyToolWindowMode getToolWindowMode() { + return myToolWindowMode; + } + + public void setToolWindowMode(StudyToolWindow.StudyToolWindowMode toolWindowMode) { + myToolWindowMode = toolWindowMode; + } + + public boolean isTurnEditingMode() { + return myTurnEditingMode; + } + + public void setTurnEditingMode(boolean turnEditingMode) { + myTurnEditingMode = turnEditingMode; + } } 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 73d7843efcc8..2c41cda8d574 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 @@ -128,7 +128,7 @@ public class StudyUtils { } public static void updateToolWindows(@NotNull final Project project) { - update(project); + updateStudyToolWindow(project); final ToolWindowManager windowManager = ToolWindowManager.getInstance(project); createProgressToolWindowContent(project, windowManager); @@ -300,7 +300,6 @@ public class StudyUtils { return null; } - public static void drawAllWindows(Editor editor, TaskFile taskFile) { editor.getMarkupModel().removeAllHighlighters(); final Project project = editor.getProject(); @@ -308,12 +307,12 @@ public class StudyUtils { final StudyTaskManager taskManager = StudyTaskManager.getInstance(project); for (AnswerPlaceholder answerPlaceholder : taskFile.getAnswerPlaceholders()) { final JBColor color = taskManager.getColor(answerPlaceholder); - EduAnswerPlaceholderPainter.drawAnswerPlaceholder(editor, answerPlaceholder, true, color); + EduAnswerPlaceholderPainter.drawAnswerPlaceholder(editor, answerPlaceholder, color); } final Document document = editor.getDocument(); EditorActionManager.getInstance() .setReadonlyFragmentModificationHandler(document, new EduAnswerPlaceholderDeleteHandler(editor)); - EduAnswerPlaceholderPainter.createGuardedBlocks(editor, taskFile, true); + EduAnswerPlaceholderPainter.createGuardedBlocks(editor, taskFile); editor.getColorsScheme().setColor(EditorColors.READONLY_FRAGMENT_BACKGROUND_COLOR, null); } @@ -353,21 +352,31 @@ public class StudyUtils { } } + @Nullable - public static Document getPatternDocument(@NotNull final TaskFile taskFile, String name) { + public static VirtualFile getPatternFile(@NotNull TaskFile taskFile, String name) { Task task = taskFile.getTask(); String lessonDir = EduNames.LESSON + String.valueOf(task.getLesson().getIndex()); String taskDir = EduNames.TASK + String.valueOf(task.getIndex()); Course course = task.getLesson().getCourse(); File resourceFile = new File(course.getCourseDirectory()); if (!resourceFile.exists()) { - return null; + return null; } String patternPath = FileUtil.join(resourceFile.getPath(), lessonDir, taskDir, name); VirtualFile patternFile = VfsUtil.findFileByIoFile(new File(patternPath), true); if (patternFile == null) { return null; } + return patternFile; + } + + @Nullable + public static Document getPatternDocument(@NotNull final TaskFile taskFile, String name) { + VirtualFile patternFile = getPatternFile(taskFile, name); + if (patternFile == null) { + return null; + } return FileDocumentManager.getInstance().getDocument(patternFile); } @@ -411,9 +420,10 @@ public class StudyUtils { return false; } Course course = StudyTaskManager.getInstance(project).getCourse(); - if (course == null) { + if (course == null || !EduNames.STUDY.equals(course.getCourseMode())) { return false; } + if (!isRenameableOrMoveable(project, course, element)) { return true; } @@ -473,14 +483,7 @@ public class StudyUtils { public static String getTaskText(@NotNull final Project project) { - VirtualFile[] files = FileEditorManager.getInstance(project).getSelectedFiles(); - TaskFile taskFile = null; - for (VirtualFile file : files) { - taskFile = getTaskFile(project, file); - if (taskFile != null) { - break; - } - } + TaskFile taskFile = getSelectedTaskFile(project); if (taskFile == null) { return EMPTY_TASK_TEXT; } @@ -490,19 +493,31 @@ public class StudyUtils { } return null; } + @Nullable + public static TaskFile getSelectedTaskFile(@NotNull Project project) { + VirtualFile[] files = FileEditorManager.getInstance(project).getSelectedFiles(); + TaskFile taskFile = null; + for (VirtualFile file : files) { + taskFile = getTaskFile(project, file); + if (taskFile != null) { + break; + } + } + return taskFile; + } - public static void update(Project project) { + public static void updateStudyToolWindow(Project project) { final StudyToolWindow studyToolWindow = getStudyToolWindow(project); if (studyToolWindow != null) { String taskText = getTaskText(project); - studyToolWindow.setTaskText(taskText); + studyToolWindow.setTaskText(taskText, null, project); } } public static boolean isStudyProject(@NotNull Project project) { return StudyTaskManager.getInstance(project).getCourse() != null; } - + public static boolean hasJavaFx() { try { Class.forName("javafx.application.Platform"); @@ -512,4 +527,37 @@ public class StudyUtils { return false; } } + + @Nullable + public static Task getTask(@NotNull Project project, @NotNull VirtualFile taskVF) { + Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null) { + return null; + } + VirtualFile lessonVF = taskVF.getParent(); + if (lessonVF == null) { + return null; + } + Lesson lesson = course.getLesson(lessonVF.getName()); + if (lesson == null) { + return null; + } + return lesson.getTask(taskVF.getName()); + } + + @Nullable + public static VirtualFile getTaskDir(@NotNull VirtualFile taskFile) { + VirtualFile parent = taskFile.getParent(); + if (parent == null) { + return null; + } + String name = parent.getName(); + if (name.contains(EduNames.TASK)) { + return parent; + } + if (EduNames.SRC.equals(name)) { + return parent.getParent(); + } + return null; + } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyToolbarAction.java b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyActionWithShortcut.java similarity index 58% rename from python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyToolbarAction.java rename to python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyActionWithShortcut.java index a74e547984ef..ae1fa651031c 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyToolbarAction.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyActionWithShortcut.java @@ -6,8 +6,9 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; -public abstract class StudyToolbarAction extends DumbAwareAction { - public StudyToolbarAction(@Nullable String text, @Nullable String description, @Nullable Icon icon) { +public abstract class StudyActionWithShortcut extends DumbAwareAction { + + public StudyActionWithShortcut(@Nullable String text, @Nullable String description, @Nullable Icon icon) { super(text, description, icon); } @@ -15,5 +16,5 @@ public abstract class StudyToolbarAction extends DumbAwareAction { public abstract String getActionId(); @Nullable - public abstract String[] getShortcuts(); -} + public abstract String[] getShortcuts(); +} \ No newline at end of file diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java index be3695794db6..623c7326972a 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java @@ -3,11 +3,13 @@ package com.jetbrains.edu.learning.actions; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.KeyboardShortcut; import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.util.Ref; +import com.jetbrains.edu.learning.StudyActionListener; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.checker.StudyCheckUtils; import icons.InteractiveLearningIcons; @@ -15,7 +17,7 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; -public abstract class StudyCheckAction extends StudyToolbarAction { +public abstract class StudyCheckAction extends StudyActionWithShortcut { public static final String SHORTCUT = "ctrl alt pressed ENTER"; protected Ref myCheckInProgress = new Ref<>(false); @@ -36,6 +38,9 @@ public abstract class StudyCheckAction extends StudyToolbarAction { StudyCheckUtils.showTestResultPopUp("Checking is not available while indexing is in progress", MessageType.WARNING.getPopupBackground(), project); return; } + for (StudyActionListener listener : Extensions.getExtensions(StudyActionListener.EP_NAME)) { + listener.beforeCheck(e); + } check(project); } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyFillPlaceholdersAction.java b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyFillPlaceholdersAction.java index e65c8a02bd6d..54c8e2c4ac08 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyFillPlaceholdersAction.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyFillPlaceholdersAction.java @@ -36,7 +36,7 @@ public class StudyFillPlaceholdersAction extends AnAction { continue; } int offset = placeholder.getRealStartOffset(document); - document.deleteString(offset, offset + placeholder.getLength()); + document.deleteString(offset, offset + placeholder.getRealLength()); document.insertString(offset, answer); } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyRefreshAnswerPlaceholder.java b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyRefreshAnswerPlaceholder.java index 42a7674c384b..7daa54735398 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyRefreshAnswerPlaceholder.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyRefreshAnswerPlaceholder.java @@ -6,9 +6,11 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; +import com.jetbrains.edu.learning.StudyActionListener; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.learning.courseFormat.TaskFile; import com.jetbrains.edu.learning.StudyState; @@ -30,6 +32,9 @@ public class StudyRefreshAnswerPlaceholder extends DumbAwareAction { if (project == null) { return; } + for (StudyActionListener listener : Extensions.getExtensions(StudyActionListener.EP_NAME)) { + listener.beforeCheck(e); + } final AnswerPlaceholder answerPlaceholder = getAnswerPlaceholder(e); if (answerPlaceholder == null) { return; @@ -51,7 +56,7 @@ public class StudyRefreshAnswerPlaceholder extends DumbAwareAction { public void run() { Document document = studyState.getEditor().getDocument(); int offset = answerPlaceholder.getRealStartOffset(document); - document.deleteString(offset, offset + answerPlaceholder.getLength()); + document.deleteString(offset, offset + answerPlaceholder.getRealLength()); document.insertString(offset, text); } }); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java index ef52729f7740..3c8057c75dea 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java @@ -9,6 +9,7 @@ import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; @@ -19,13 +20,14 @@ import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.problems.WolfTheProblemSolver; +import com.jetbrains.edu.learning.StudyActionListener; +import com.jetbrains.edu.learning.StudyState; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.learning.courseFormat.StudyStatus; import com.jetbrains.edu.learning.courseFormat.TaskFile; -import com.jetbrains.edu.learning.StudyState; -import com.jetbrains.edu.learning.StudyTaskManager; -import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.editor.StudyEditor; import com.jetbrains.edu.learning.navigation.StudyNavigator; import org.jetbrains.annotations.NotNull; @@ -33,7 +35,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; -public class StudyRefreshTaskFileAction extends StudyToolbarAction { +public class StudyRefreshTaskFileAction extends StudyActionWithShortcut { public static final String ACTION_ID = "RefreshTaskAction"; public static final String SHORTCUT = "ctrl shift pressed X"; private static final Logger LOG = Logger.getInstance(StudyRefreshTaskFileAction.class.getName()); @@ -73,7 +75,7 @@ public class StudyRefreshTaskFileAction extends StudyToolbarAction { WolfTheProblemSolver.getInstance(project).clearProblems(studyState.getVirtualFile()); taskFile.setHighlightErrors(false); StudyUtils.drawAllWindows(editor, taskFile); - EduAnswerPlaceholderPainter.createGuardedBlocks(editor, taskFile, true); + EduAnswerPlaceholderPainter.createGuardedBlocks(editor, taskFile); ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { @@ -148,6 +150,9 @@ public class StudyRefreshTaskFileAction extends StudyToolbarAction { public void actionPerformed(@NotNull AnActionEvent event) { final Project project = event.getProject(); if (project != null) { + for (StudyActionListener listener : Extensions.getExtensions(StudyActionListener.EP_NAME)) { + listener.beforeCheck(event); + } refresh(project); } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyShowHintAction.java b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyShowHintAction.java index 25642dc0cf87..75a1277c88d9 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyShowHintAction.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyShowHintAction.java @@ -25,7 +25,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; -public class StudyShowHintAction extends StudyToolbarAction { +public class StudyShowHintAction extends StudyActionWithShortcut { public static final String ACTION_ID = "ShowHintAction"; public static final String SHORTCUT = "ctrl pressed 7"; private static final String ourWarningMessage = "Put the caret in the answer placeholder to get hint"; diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java index 580bc3557d64..308a423d68f6 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java @@ -27,7 +27,7 @@ import javax.swing.tree.TreePath; import java.util.Map; -abstract public class StudyTaskNavigationAction extends StudyToolbarAction { +abstract public class StudyTaskNavigationAction extends StudyActionWithShortcut { public StudyTaskNavigationAction(@Nullable String text, @Nullable String description, @Nullable Icon icon) { super(text, description, icon); } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyWindowNavigationAction.java b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyWindowNavigationAction.java index 50be6069aa36..cd0b5ccf1dbe 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyWindowNavigationAction.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyWindowNavigationAction.java @@ -16,7 +16,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; -abstract public class StudyWindowNavigationAction extends StudyToolbarAction implements DumbAware { +abstract public class StudyWindowNavigationAction extends StudyActionWithShortcut implements DumbAware { protected StudyWindowNavigationAction(String actionId, String description, Icon icon) { super(actionId, description, icon); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java index 9fe0ae84ae6b..33c626a05bcb 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java @@ -21,6 +21,7 @@ import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.ex.StatusBarEx; import com.intellij.openapi.wm.ex.WindowManagerEx; 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.Task; @@ -38,7 +39,6 @@ import java.util.List; import java.util.Map; public class StudyCheckUtils { - private static final String ANSWERS_POSTFIX = "_answers"; private static final Logger LOG = Logger.getInstance(StudyCheckUtils.class); private StudyCheckUtils() { @@ -143,7 +143,7 @@ public class StudyCheckUtils { VirtualFile copy = null; try { - copy = file.copy(taskDir, taskDir, file.getNameWithoutExtension() + ANSWERS_POSTFIX + "." + file.getExtension()); + copy = file.copy(taskDir, taskDir, file.getNameWithoutExtension() + EduNames.ANSWERS_POSTFIX + "." + file.getExtension()); final FileDocumentManager documentManager = FileDocumentManager.getInstance(); final Document document = documentManager.getDocument(copy); if (document != null) { @@ -155,7 +155,7 @@ public class StudyCheckUtils { continue; } final int start = answerPlaceholder.getRealStartOffset(document); - final int end = start + answerPlaceholder.getLength(); + final int end = start + answerPlaceholder.getRealLength(); final String text = answerPlaceholder.getPossibleAnswer(); document.replaceString(start, end, text); } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java index 7cd660635d84..14870203abac 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java @@ -54,10 +54,10 @@ public class StudySmartChecker { EduDocumentListener listener = new EduDocumentListener(windowTaskFile); windowDocument.addDocumentListener(listener); int start = placeholder.getRealStartOffset(windowDocument); - int end = start + placeholder.getLength(); + int end = start + placeholder.getRealLength(); final AnswerPlaceholder userAnswerPlaceholder = usersTaskFile.getAnswerPlaceholders().get(placeholder.getIndex()); int userStart = userAnswerPlaceholder.getRealStartOffset(usersDocument); - int userEnd = userStart + userAnswerPlaceholder.getLength(); + int userEnd = userStart + userAnswerPlaceholder.getRealLength(); String text = usersDocument.getText(new TextRange(userStart, userEnd)); windowDocument.replaceString(start, end, text); ApplicationManager.getApplication().runWriteAction(() -> { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduAnswerPlaceholderPainter.java b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduAnswerPlaceholderPainter.java index 528b58e26102..81fcf85fb989 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduAnswerPlaceholderPainter.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduAnswerPlaceholderPainter.java @@ -26,9 +26,9 @@ public class EduAnswerPlaceholderPainter { } public static void drawAnswerPlaceholder(@NotNull final Editor editor, @NotNull final AnswerPlaceholder placeholder, - boolean useLength, @NotNull final JBColor color) { + @NotNull final JBColor color) { final Document document = editor.getDocument(); - if (useLength && !placeholder.isValid(document)) { + if (!placeholder.isValid(document)) { return; } EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme(); @@ -37,10 +37,8 @@ public class EduAnswerPlaceholderPainter { final Project project = editor.getProject(); assert project != null; final int startOffset = placeholder.getRealStartOffset(document); - final int length = placeholder.getLength(); - final int replacementLength = placeholder.getPossibleAnswerLength(); - int highlighterLength = useLength ? length : replacementLength; - final int endOffset = startOffset + highlighterLength; + final int length = placeholder.getRealLength(); + final int endOffset = startOffset + length; textAttributes.setEffectColor(color); RangeHighlighter highlighter = editor.getMarkupModel().addRangeHighlighter(startOffset, endOffset, PLACEHOLDERS_LAYER, @@ -56,20 +54,20 @@ public class EduAnswerPlaceholderPainter { } - public static void createGuardedBlocks(@NotNull final Editor editor, TaskFile taskFile, boolean useLength) { + public static void createGuardedBlocks(@NotNull final Editor editor, TaskFile taskFile) { for (AnswerPlaceholder answerPlaceholder : taskFile.getAnswerPlaceholders()) { - createGuardedBlocks(editor, answerPlaceholder, useLength); + createGuardedBlocks(editor, answerPlaceholder); } } - public static void createGuardedBlocks(@NotNull final Editor editor, AnswerPlaceholder placeholder, boolean useLength) { + public static void createGuardedBlocks(@NotNull final Editor editor, AnswerPlaceholder placeholder) { Document document = editor.getDocument(); if (document instanceof DocumentImpl) { DocumentImpl documentImpl = (DocumentImpl)document; List blocks = documentImpl.getGuardedBlocks(); - if (useLength && !placeholder.isValid(document)) return; + if (!placeholder.isValid(document)) return; int start = placeholder.getRealStartOffset(document); - final int length = useLength ? placeholder.getLength() : placeholder.getPossibleAnswerLength(); + final int length = placeholder.getRealLength(); int end = start + length; if (start != 0) { createGuardedBlock(editor, blocks, start - 1, start); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduDocumentListener.java b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduDocumentListener.java index 77e02bb15154..8e43bf57b26b 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduDocumentListener.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduDocumentListener.java @@ -18,29 +18,19 @@ import java.util.List; public class EduDocumentListener extends DocumentAdapter { private final TaskFile myTaskFile; private final boolean myTrackLength; - private final boolean usePossibleAnswerLength; private final List myAnswerPlaceholders = new ArrayList(); public EduDocumentListener(TaskFile taskFile) { myTaskFile = taskFile; myTrackLength = true; - usePossibleAnswerLength = false; } public EduDocumentListener(TaskFile taskFile, boolean trackLength) { myTaskFile = taskFile; myTrackLength = trackLength; - usePossibleAnswerLength = false; } - public EduDocumentListener(TaskFile taskFile, boolean trackLength, boolean usePossibleAnswerLength) { - myTaskFile = taskFile; - myTrackLength = trackLength; - this.usePossibleAnswerLength = usePossibleAnswerLength; - } - - //remembering old end before document change because of problems // with fragments containing "\n" @Override @@ -53,7 +43,7 @@ public class EduDocumentListener extends DocumentAdapter { myAnswerPlaceholders.clear(); for (AnswerPlaceholder answerPlaceholder : myTaskFile.getAnswerPlaceholders()) { int twStart = answerPlaceholder.getRealStartOffset(document); - int length = usePossibleAnswerLength ? answerPlaceholder.getPossibleAnswerLength() : answerPlaceholder.getLength(); + int length = answerPlaceholder.getRealLength(); int twEnd = twStart + length; myAnswerPlaceholders.add(new AnswerPlaceholderWrapper(answerPlaceholder, twStart, twEnd)); } @@ -85,7 +75,7 @@ public class EduDocumentListener extends DocumentAdapter { int length = twEnd - twStart; answerPlaceholder.setLine(line); answerPlaceholder.setStart(start); - if (usePossibleAnswerLength) { + if (!answerPlaceholder.getUseLength()) { answerPlaceholder.setPossibleAnswer(document.getText(TextRange.create(twStart, twStart + length))); } else if (myTrackLength) { 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 03c06d19b30d..a6062cb25339 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 @@ -32,14 +32,19 @@ public class EduNames { public static final String USER_TEST_OUTPUT = "output"; public static final String WINDOW_POSTFIX = "_window."; public static final String WINDOWS_POSTFIX = "_windows"; + public static final String ANSWERS_POSTFIX = "_answers"; public static final String USER_TESTS = "userTests"; public static final String TESTS_FILE = "tests.py"; public static final String TEST_HELPER = "test_helper.py"; public static final String SANDBOX_DIR = "Sandbox"; public static final String COURSE_META_FILE = "course.json"; + public static final String STUDY = "Study"; public static String PYCHARM_ADDITIONAL = "PyCharm additional materials"; + public static final String PYCHARM = "PyCharm"; + public static String PLACEHOLDER = "Answer Placeholder"; + public static String SRC = "src"; private EduNames() { } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduUtils.java index 4b4870673a23..b095bc94815e 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduUtils.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduUtils.java @@ -95,7 +95,7 @@ public class EduUtils { fileWindows = taskDir.createChildData(taskFile, name); printWriter = new PrintWriter(new FileOutputStream(fileWindows.getPath())); for (AnswerPlaceholder answerPlaceholder : taskFile.getAnswerPlaceholders()) { - int length = useLength ? answerPlaceholder.getLength() : answerPlaceholder.getPossibleAnswerLength(); + int length = answerPlaceholder.getRealLength(); if (!answerPlaceholder.isValid(document, length)) { printWriter.println("#educational_plugin_window = "); continue; @@ -192,6 +192,13 @@ public class EduUtils { }); } }, "Create Student File", "Create Student File"); + createStudentDocument(project, taskFile, file, document); + } + + public static void createStudentDocument(@NotNull Project project, + @NotNull TaskFile taskFile, + VirtualFile file, + final Document document) { EduDocumentListener listener = new EduDocumentListener(taskFile, false); document.addDocumentListener(listener); taskFile.sortAnswerPlaceholders(); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/AnswerPlaceholder.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/AnswerPlaceholder.java index e086091d6d8c..72f2b422b7f8 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/AnswerPlaceholder.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/AnswerPlaceholder.java @@ -25,6 +25,7 @@ public class AnswerPlaceholder { private MyInitialState myInitialState; private StudyStatus myStatus = StudyStatus.Uninitialized; private boolean mySelected = false; + private boolean myUseLength = true; @Transient private TaskFile myTaskFile; @@ -45,6 +46,9 @@ public class AnswerPlaceholder { myIndex = index; } + /** + * in actions {@link AnswerPlaceholder#getRealLength()} should be used + */ public int getLength() { return length; } @@ -138,6 +142,9 @@ public class AnswerPlaceholder { line = myInitialState.myLine; start = myInitialState.myStart; length = myInitialState.myLength; + if (!getUseLength()) { + possibleAnswer = myTaskText; + } } public StudyStatus getStatus() { @@ -156,6 +163,29 @@ public class AnswerPlaceholder { mySelected = selected; } + public void init() { + setInitialState(new MyInitialState(line, myTaskText.length(), start)); + } + + public boolean getUseLength() { + return myUseLength; + } + + /** + * @return length or possible answer length + */ + public int getRealLength() { + return myUseLength ? getLength() : getPossibleAnswerLength(); + } + + public void setUseLength(boolean useLength) { + myUseLength = useLength; + } + + public void setInitialState(AnswerPlaceholder placeholder) { + setInitialState(new MyInitialState(placeholder.line, placeholder.length, placeholder.start)); + } + public static class MyInitialState { public int myLine = -1; public int myLength = -1; diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Course.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Course.java index 5dfbebf0f609..e643d9470cc9 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Course.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Course.java @@ -24,9 +24,14 @@ public class Course { private boolean myUpToDate; @Expose @SerializedName("language") - private String myLanguage="Python"; + private String myLanguage = "Python"; - private String courseType="PyCharm"; + //this field is used to distinguish ordinary and CheckIO projects + //"PyCharm" is used here for historical reasons + private String courseType = EduNames.PYCHARM; + + //this field is used to distinguish study and course creator modes + private String courseMode = EduNames.STUDY; /** * Initializes state of course @@ -145,4 +150,12 @@ public class Course { public void setCourseType(String courseType) { this.courseType = courseType; } + + public String getCourseMode() { + return courseMode; + } + + public void setCourseMode(String courseMode) { + this.courseMode = courseMode; + } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/TaskFile.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/TaskFile.java index a235d9eb3a54..33e00d574325 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/TaskFile.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/TaskFile.java @@ -95,7 +95,7 @@ public class TaskFile { for (AnswerPlaceholder placeholder : myAnswerPlaceholders) { if (placeholder.getLine() <= line) { int realStartOffset = placeholder.getRealStartOffset(document); - int placeholderLength = useAnswerLength ? placeholder.getPossibleAnswerLength() : placeholder.getLength(); + int placeholderLength = placeholder.getRealLength(); final int length = placeholderLength > 0 ? placeholderLength : 0; int endOffset = realStartOffset + length; if (realStartOffset <= offset && offset <= endOffset) { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/editor/StudyEditorFactoryListener.java b/python/educational-core/student/src/com/jetbrains/edu/learning/editor/StudyEditorFactoryListener.java index 4678c7e0ba3e..8a9c17d0ef24 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/editor/StudyEditorFactoryListener.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/editor/StudyEditorFactoryListener.java @@ -14,10 +14,13 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.problems.WolfTheProblemSolver; -import com.jetbrains.edu.learning.core.EduDocumentListener; -import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; -import com.jetbrains.edu.learning.courseFormat.TaskFile; +import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.core.EduDocumentListener; +import com.jetbrains.edu.learning.core.EduNames; +import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.courseFormat.TaskFile; import com.jetbrains.edu.learning.navigation.StudyNavigator; import com.jetbrains.edu.learning.ui.StudyToolWindowFactory; import org.jetbrains.annotations.NotNull; @@ -44,7 +47,7 @@ public class StudyEditorFactoryListener implements EditorFactoryListener { return; } int startOffset = answerPlaceholder.getRealStartOffset(editor.getDocument()); - editor.getSelectionModel().setSelection(startOffset, startOffset + answerPlaceholder.getLength()); + editor.getSelectionModel().setSelection(startOffset, startOffset + answerPlaceholder.getRealLength()); answerPlaceholder.setSelected(true); } } @@ -57,6 +60,7 @@ public class StudyEditorFactoryListener implements EditorFactoryListener { return; } + final Document document = editor.getDocument(); final VirtualFile openedFile = FileDocumentManager.getInstance().getFile(document); if (openedFile != null) { @@ -68,11 +72,20 @@ public class StudyEditorFactoryListener implements EditorFactoryListener { StudyUtils.updateToolWindows(project); studyToolWindow.show(null); } + Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null) { + return; + } + + StudyEditor.addDocumentListener(document, new EduDocumentListener(taskFile, true)); + if (!taskFile.getAnswerPlaceholders().isEmpty()) { StudyNavigator.navigateToFirstAnswerPlaceholder(editor, taskFile); - StudyEditor.addDocumentListener(document, new EduDocumentListener(taskFile)); + boolean isStudyProject = EduNames.STUDY.equals(course.getCourseType()); StudyUtils.drawAllWindows(editor, taskFile); - editor.addEditorMouseListener(new WindowSelectionListener(taskFile)); + if (isStudyProject) { + editor.addEditorMouseListener(new WindowSelectionListener(taskFile)); + } } } } 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 6fd47eaece84..0a3a3e841feb 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 @@ -9,6 +9,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.jetbrains.edu.learning.core.EduNames; +import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.TaskFile; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.StudyUtils; @@ -24,7 +25,7 @@ public class StudyTreeStructureProvider implements TreeStructureProvider, DumbAw public Collection modify(@NotNull AbstractTreeNode parent, @NotNull Collection children, ViewSettings settings) { - if (!isCourseBasedProject(parent)) { + if (!needModify(parent)) { return children; } Collection nodes = new ArrayList<>(); @@ -78,21 +79,23 @@ 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) && !fileName.contains(".answer")) { + && !StudyUtils.isTestsFile(project, fileName) && !EduNames.TASK_HTML.equals(fileName)) { nodes.add(node); } } } - protected boolean isCourseBasedProject(@NotNull final AbstractTreeNode parent) { + protected boolean needModify(@NotNull final AbstractTreeNode parent) { final Project project = parent.getProject(); - if (project != null) { - final StudyTaskManager studyTaskManager = StudyTaskManager.getInstance(project); - if (studyTaskManager.getCourse() == null) { - return false; - } + if (project == null) { + return false; } - return true; + final StudyTaskManager studyTaskManager = StudyTaskManager.getInstance(project); + Course course = studyTaskManager.getCourse(); + if (course == null) { + return false; + } + return EduNames.STUDY.equals(course.getCourseMode()); } @Nullable diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyJavaFxToolWindow.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyJavaFxToolWindow.java index 98bec3d50589..058bd633a29b 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyJavaFxToolWindow.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyJavaFxToolWindow.java @@ -29,10 +29,9 @@ public class StudyJavaFxToolWindow extends StudyToolWindow { } @Override - public JComponent createTaskInfoPanel(String taskText, Project project) { + public JComponent createTaskInfoPanel(Project project) { myBrowserWindow = new StudyBrowserWindow(true, false); myBrowserWindow.addBackAndOpenButtons(); - myBrowserWindow.loadContent(taskText, StudyUtils.getConfigurator(project)); JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); panel.add(myBrowserWindow.getPanel()); @@ -40,7 +39,7 @@ public class StudyJavaFxToolWindow extends StudyToolWindow { } @Override - public void setTaskText(String text) { + public void setText(String text) { StudyPluginConfigurator configurator = StudyUtils.getConfigurator(ProjectUtil.guessCurrentProject(this)); myBrowserWindow.loadContent(text, configurator); } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudySwingToolWindow.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudySwingToolWindow.java index ef66218d3398..ebaff662ecb8 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudySwingToolWindow.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudySwingToolWindow.java @@ -37,7 +37,7 @@ public class StudySwingToolWindow extends StudyToolWindow { } @Override - public JComponent createTaskInfoPanel(String taskText, Project project) { + public JComponent createTaskInfoPanel(Project project) { myTaskTextPane = new JTextPane(); final JBScrollPane scrollPane = new JBScrollPane(myTaskTextPane); myTaskTextPane.setContentType(new HTMLEditorKit().getContentType()); @@ -57,12 +57,11 @@ public class StudySwingToolWindow extends StudyToolWindow { myTaskTextPane.setBackground(EditorColorsManager.getInstance().getGlobalScheme().getDefaultBackground()); } myTaskTextPane.setBorder(new EmptyBorder(20, 20, 0, 10)); - myTaskTextPane.setText(taskText); myTaskTextPane.addHyperlinkListener(BrowserHyperlinkListener.INSTANCE); return scrollPane; } - public void setTaskText(String text) { + public void setText(String text) { myTaskTextPane.setText(text); } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java index 7d24d27e6783..feb06b72159a 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java @@ -15,23 +15,27 @@ */ package com.jetbrains.edu.learning.ui; +import com.intellij.ide.browsers.WebBrowserManager; import com.intellij.openapi.Disposable; -import com.intellij.openapi.actionSystem.ActionManager; -import com.intellij.openapi.actionSystem.ActionToolbar; -import com.intellij.openapi.actionSystem.DataProvider; -import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.FileEditorManagerListener; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.SimpleToolWindowPanel; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.JBCardLayout; import com.intellij.ui.OnePixelSplitter; import com.intellij.util.ui.JBUI; -import com.jetbrains.edu.learning.StudyBasePluginConfigurator; -import com.jetbrains.edu.learning.StudyPluginConfigurator; -import com.jetbrains.edu.learning.StudyTaskManager; -import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.*; +import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.courseFormat.TaskFile; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -41,6 +45,7 @@ import java.util.Map; public abstract class StudyToolWindow extends SimpleToolWindowPanel implements DataProvider, Disposable { private static final Logger LOG = Logger.getInstance(StudyToolWindow.class); private static final String TASK_INFO_ID = "taskInfo"; + private static final String EMPTY_TASK_TEXT = "Please, open any task to see task description"; private final JBCardLayout myCardLayout; private final JPanel myContentPanel; private final OnePixelSplitter mySplitPane; @@ -49,17 +54,17 @@ public abstract class StudyToolWindow extends SimpleToolWindowPanel implements D super(true, true); myCardLayout = new JBCardLayout(); myContentPanel = new JPanel(myCardLayout); - mySplitPane = new OnePixelSplitter(myVertical=true); + mySplitPane = new OnePixelSplitter(myVertical = true); } public void init(Project project) { String taskText = StudyUtils.getTaskText(project); if (taskText == null) return; - JPanel toolbarPanel = createToolbarPanel(project); + JPanel toolbarPanel = createToolbarPanel(getActionGroup(project)); setToolbar(toolbarPanel); - myContentPanel.add(TASK_INFO_ID, createTaskInfoPanel(taskText, project)); + myContentPanel.add(TASK_INFO_ID, createTaskInfoPanel(project)); mySplitPane.setFirstComponent(myContentPanel); addAdditionalPanels(project); myCardLayout.show(myContentPanel, TASK_INFO_ID); @@ -71,6 +76,17 @@ public abstract class StudyToolWindow extends SimpleToolWindowPanel implements D final FileEditorManagerListener listener = configurator.getFileEditorManagerListener(project, this); project.getMessageBus().connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, listener); } + + if (StudyTaskManager.getInstance(project).isTurnEditingMode() || StudyTaskManager.getInstance(project).getToolWindowMode() == StudyToolWindowMode.EDITING) { + TaskFile file = StudyUtils.getSelectedTaskFile(project); + if (file != null) { + VirtualFile taskDir = file.getTask().getTaskDir(project); + setTaskText(taskText, taskDir, project); + + } + } else { + setTaskText(taskText, null, project); + } } private void addAdditionalPanels(Project project) { @@ -85,7 +101,7 @@ public abstract class StudyToolWindow extends SimpleToolWindowPanel implements D public void dispose() { } - + //used in checkiO plugin. @SuppressWarnings("unused") public void showPanelById(@NotNull final String panelId) { @@ -117,24 +133,25 @@ public abstract class StudyToolWindow extends SimpleToolWindowPanel implements D } - public abstract JComponent createTaskInfoPanel(String taskText, Project project); - - private static JPanel createToolbarPanel(@NotNull final Project project) { - final DefaultActionGroup group = getActionGroup(project); + public abstract JComponent createTaskInfoPanel(Project project); + public static JPanel createToolbarPanel(ActionGroup group) { final ActionToolbar actionToolBar = ActionManager.getInstance().createActionToolbar("Study", group, true); return JBUI.Panels.simplePanel(actionToolBar.getComponent()); } - private static DefaultActionGroup getActionGroup(@NotNull final Project project) { + public static DefaultActionGroup getActionGroup(@NotNull final Project project) { + DefaultActionGroup group = new DefaultActionGroup(); Course course = StudyTaskManager.getInstance(project).getCourse(); if (course == null) { LOG.warn("Course is null"); - return new DefaultActionGroup(); + return group; } StudyPluginConfigurator configurator = StudyUtils.getConfigurator(project); if (configurator != null) { - return configurator.getActionGroup(project); + group.addAll(configurator.getActionGroup(project)); + addAdditionalActions(group); + return group; } else { LOG.warn("No configurator is provided for plugin"); @@ -142,5 +159,68 @@ public abstract class StudyToolWindow extends SimpleToolWindowPanel implements D } } - public abstract void setTaskText(String text) ; + private static void addAdditionalActions(DefaultActionGroup group) { + StudyActionsProvider[] providers = Extensions.getExtensions(StudyActionsProvider.EP_NAME); + for (StudyActionsProvider provider : providers) { + group.addAll(provider.getActions()); + } + } + + public void setTaskText(String text, VirtualFile taskDirectory, Project project) { + if (StudyTaskManager.getInstance(project).isTurnEditingMode()) { + if (taskDirectory == null) { + LOG.info("Failed to enter editing mode for StudyToolWindow"); + return; + } + VirtualFile taskTextFile = taskDirectory.findChild(EduNames.TASK_HTML); + enterEditingMode(taskTextFile, project); + StudyTaskManager.getInstance(project).setTurnEditingMode(false); + } + else { + setText(text); + } + } + + protected abstract void setText(String text); + + public void setEmptyText(@NotNull Project project) { + if (StudyTaskManager.getInstance(project).getToolWindowMode() == StudyToolWindowMode.EDITING) { + mySplitPane.setFirstComponent(myContentPanel); + StudyTaskManager.getInstance(project).setTurnEditingMode(true); + } + setTaskText(EMPTY_TASK_TEXT, null, project); + } + + public enum StudyToolWindowMode { + TEXT, EDITING + } + + + public void enterEditingMode(VirtualFile taskFile, Project project) { + final EditorFactory factory = EditorFactory.getInstance(); + Document document = FileDocumentManager.getInstance().getDocument(taskFile); + if (document == null) { + return; + } + WebBrowserManager.getInstance().setShowBrowserHover(false); + final EditorEx createdEditor = (EditorEx)factory.createEditor(document, project, taskFile, false); + Disposer.register(project, new Disposable() { + public void dispose() { + factory.releaseEditor(createdEditor); + } + }); + JComponent editorComponent = createdEditor.getComponent(); + mySplitPane.setFirstComponent(editorComponent); + mySplitPane.repaint(); + + StudyTaskManager.getInstance(project).setToolWindowMode(StudyToolWindowMode.EDITING); + } + + + public void leaveEditingMode(Project project) { + WebBrowserManager.getInstance().setShowBrowserHover(true); + mySplitPane.setFirstComponent(myContentPanel); + StudyTaskManager.getInstance(project).setToolWindowMode(StudyToolWindowMode.TEXT); + StudyUtils.updateStudyToolWindow(project); + } } diff --git a/python/educational-core/student/student.iml b/python/educational-core/student/student.iml index e16fc06deca8..8702c7849f52 100644 --- a/python/educational-core/student/student.iml +++ b/python/educational-core/student/student.iml @@ -23,5 +23,6 @@ + \ No newline at end of file diff --git a/python/educational-python/course-creator-python/resources/META-INF/plugin.xml b/python/educational-python/course-creator-python/resources/META-INF/plugin.xml index e87961c59b40..2399560d07b7 100644 --- a/python/educational-python/course-creator-python/resources/META-INF/plugin.xml +++ b/python/educational-python/course-creator-python/resources/META-INF/plugin.xml @@ -21,27 +21,16 @@ + + - - - - - - - - - - - - - \ No newline at end of file diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCLanguageManager.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCLanguageManager.java index f66ecff6ddd3..8628bbdecc41 100644 --- a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCLanguageManager.java +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCLanguageManager.java @@ -3,6 +3,7 @@ package com.jetbrains.edu.coursecreator; import com.intellij.ide.fileTemplates.FileTemplate; import com.intellij.ide.fileTemplates.FileTemplateManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; import com.jetbrains.edu.learning.core.EduNames; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,4 +41,9 @@ public class PyCCLanguageManager implements CCLanguageManager { private static FileTemplate getInternalTemplateByName(@NotNull final Project project, String name) { return FileTemplateManager.getInstance(project).getInternalTemplate(name); } + + @Override + public boolean isTestFile(VirtualFile file) { + return EduNames.TESTS_FILE.equals(file.getName()); + } } diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java index ebffadfea5d2..4af7914d2da4 100644 --- a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java @@ -6,19 +6,25 @@ import com.intellij.facet.ui.ValidationResult; import com.intellij.ide.fileTemplates.FileTemplate; import com.intellij.ide.fileTemplates.FileTemplateManager; import com.intellij.ide.fileTemplates.FileTemplateUtil; +import com.intellij.openapi.application.PathManager; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.platform.DirectoryProjectGenerator; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiManager; -import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.coursecreator.actions.CCCreateLesson; import com.jetbrains.edu.coursecreator.actions.CCCreateTask; import com.jetbrains.edu.coursecreator.ui.CCNewProjectPanel; +import com.jetbrains.edu.learning.StudyProjectComponent; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.core.EduNames; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.python.PythonLanguage; import com.jetbrains.python.newProject.PythonProjectGenerator; import icons.CourseCreatorPythonIcons; import org.jetbrains.annotations.Nls; @@ -26,6 +32,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.io.File; public class PyCCProjectGenerator extends PythonProjectGenerator implements DirectoryProjectGenerator { @@ -55,25 +62,16 @@ public class PyCCProjectGenerator extends PythonProjectGenerator implements Dire public static void generateProject(@NotNull final Project project, @NotNull final VirtualFile baseDir, @NotNull final String name, @NotNull final String[] authors, @NotNull final String description) { - final CCProjectService service = CCProjectService.getInstance(project); - final Course course = new Course(); - course.setName(name); - course.setAuthors(authors); - course.setDescription(description); - course.setLanguage("Python"); - service.setCourse(course); + final Course course = getCourse(project, name, authors, description); final PsiDirectory projectDir = PsiManager.getInstance(project).findDirectory(baseDir); if (projectDir == null) return; new WriteCommandAction.Simple(project) { @Override protected void run() throws Throwable { - final FileTemplate template = FileTemplateManager.getInstance(project).getInternalTemplate("test_helper"); - try { - FileTemplateUtil.createFromTemplate(template, "test_helper.py", null, projectDir); - } - catch (Exception ignored) { - } + + createTestHelper(project, projectDir); + PsiDirectory lessonDir = new CCCreateLesson().createItem(null, project, projectDir, course); if (lessonDir == null) { LOG.error("Failed to create lesson"); @@ -84,6 +82,33 @@ public class PyCCProjectGenerator extends PythonProjectGenerator implements Dire }.execute(); } + private static void createTestHelper(@NotNull Project project, PsiDirectory projectDir) { + final FileTemplate template = FileTemplateManager.getInstance(project).getInternalTemplate(FileUtil.getNameWithoutExtension(EduNames.TEST_HELPER)); + try { + FileTemplateUtil.createFromTemplate(template, EduNames.TEST_HELPER, null, projectDir); + } + catch (Exception ignored) { + } + } + + @NotNull + private static Course getCourse(@NotNull Project project, @NotNull String name, @NotNull String[] authors, @NotNull String description) { + final Course course = new Course(); + course.setName(name); + course.setAuthors(authors); + course.setDescription(description); + course.setLanguage(PythonLanguage.getInstance().getID()); + course.setCourseMode(CCUtils.COURSE_MODE); + + File coursesDir = new File(PathManager.getConfigPath(), "courses"); + File courseDir = new File(coursesDir, name + "-" + project.getName()); + course.setCourseDirectory(courseDir.getPath()); + + StudyTaskManager.getInstance(project).setCourse(course); + StudyProjectComponent.getInstance(project).registerStudyToolWindow(course); + return course; + } + @NotNull @Override public ValidationResult validate(@NotNull String s) { diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCReferenceResolveProvider.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCReferenceResolveProvider.java index 95cf9321df3b..e1bb8ee267d6 100644 --- a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCReferenceResolveProvider.java +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCReferenceResolveProvider.java @@ -1,5 +1,6 @@ package com.jetbrains.edu.coursecreator; +import com.intellij.openapi.project.Project; import com.jetbrains.python.edu.PyEduUtils; import com.jetbrains.python.psi.PyQualifiedExpression; import com.jetbrains.python.psi.resolve.PyReferenceResolveProvider; @@ -13,7 +14,8 @@ public class PyCCReferenceResolveProvider implements PyReferenceResolveProvider @NotNull @Override public List resolveName(@NotNull final PyQualifiedExpression element) { - if (CCProjectService.getInstance(element.getProject()).getCourse() == null) { + Project project = element.getProject(); + if (!CCUtils.isCourseCreator(project)) { return Collections.emptyList(); } return PyEduUtils.getResolveResultFromContainingDirectory(element); diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCRunTests.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCRunTests.java deleted file mode 100644 index 5af7f8b164e8..000000000000 --- a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCRunTests.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.jetbrains.edu.coursecreator; - -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.project.Project; -import com.jetbrains.edu.coursecreator.actions.PyCCRunTestsAction; -import com.jetbrains.python.edu.PyExecuteFileExtensionPoint; -import org.jetbrains.annotations.NotNull; - -public class PyCCRunTests implements PyExecuteFileExtensionPoint { - - @NotNull - public AnAction getRunAction() { - return new PyCCRunTestsAction(); - } - - @Override - public boolean accept(Project project) { - return CCProjectService.getInstance(project).getCourse() != null; - } -} \ No newline at end of file diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/actions/PyCCRunTestsAction.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/actions/PyCCRunTestsAction.java deleted file mode 100644 index ebda0f8976f1..000000000000 --- a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/actions/PyCCRunTestsAction.java +++ /dev/null @@ -1,62 +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.coursecreator.actions; - -import com.intellij.execution.Executor; -import com.intellij.execution.ProgramRunnerUtil; -import com.intellij.execution.RunManager; -import com.intellij.execution.RunnerAndConfigurationSettings; -import com.intellij.execution.configurations.ConfigurationFactory; -import com.intellij.execution.executors.DefaultRunExecutor; -import com.intellij.ide.projectView.ProjectView; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileManager; -import com.intellij.util.PathUtil; -import com.jetbrains.python.run.PythonConfigurationType; -import com.jetbrains.python.run.PythonRunConfiguration; -import org.jetbrains.annotations.NotNull; - -public class PyCCRunTestsAction extends CCRunTestsAction { - - @Override - protected void executeTests(@NotNull final Project project, - @NotNull final VirtualFile virtualFile, - @NotNull final VirtualFile taskDir, - @NotNull final VirtualFile testFile) { - final ConfigurationFactory factory = PythonConfigurationType.getInstance().getConfigurationFactories()[0]; - final RunnerAndConfigurationSettings settings = - RunManager.getInstance(project).createRunConfiguration("test", factory); - - final PythonRunConfiguration configuration = (PythonRunConfiguration)settings.getConfiguration(); - configuration.setScriptName(testFile.getPath()); - configuration.setWorkingDirectory(taskDir.getPath()); - String taskFileName = virtualFile.getName(); - VirtualFile userFile = taskDir.findChild(taskFileName); - if (userFile == null) { - return; - } - VirtualFile ideaDir = project.getBaseDir().findChild(".idea"); - if (ideaDir == null) { - return; - } - VirtualFileManager.getInstance().refreshWithoutFileWatcher(true); - ProjectView.getInstance(project).refresh(); - configuration.setScriptParameters(PathUtil.toSystemDependentName(project.getBasePath()) + " " + PathUtil.toSystemDependentName(userFile.getPath())); - Executor executor = DefaultRunExecutor.getRunExecutorInstance(); - ProgramRunnerUtil.executeConfiguration(project, settings, executor); - } -} diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCCommandLineState.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCCommandLineState.java new file mode 100644 index 000000000000..0bde8098b28f --- /dev/null +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCCommandLineState.java @@ -0,0 +1,89 @@ +package com.jetbrains.edu.coursecreator.run; + +import com.intellij.execution.ExecutionException; +import com.intellij.execution.ExecutionResult; +import com.intellij.execution.Executor; +import com.intellij.execution.configurations.GeneralCommandLine; +import com.intellij.execution.configurations.ParamsGroup; +import com.intellij.execution.process.ProcessAdapter; +import com.intellij.execution.process.ProcessEvent; +import com.intellij.execution.process.ProcessHandler; +import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.checker.StudyCheckUtils; +import com.jetbrains.edu.learning.core.EduNames; +import com.jetbrains.edu.learning.core.EduUtils; +import com.jetbrains.edu.learning.courseFormat.Course; +import com.jetbrains.edu.learning.courseFormat.Task; +import com.jetbrains.python.run.CommandLinePatcher; +import com.jetbrains.python.run.PythonCommandLineState; +import org.jetbrains.annotations.NotNull; + +public class PyCCCommandLineState extends PythonCommandLineState { + private final PyCCRunTestConfiguration myRunConfiguration; + private final VirtualFile myTaskDir; + private final Task myTask; + + public PyCCCommandLineState(PyCCRunTestConfiguration runConfiguration, + ExecutionEnvironment env) { + super(runConfiguration, env); + myRunConfiguration = runConfiguration; + + VirtualFile testsFile = LocalFileSystem.getInstance().findFileByPath(myRunConfiguration.getPathToTest()); + assert testsFile != null; + myTaskDir = StudyUtils.getTaskDir(testsFile); + assert myTaskDir != null; + myTask = StudyUtils.getTask(myRunConfiguration.getProject(), myTaskDir); + assert myTask != null; + } + + @Override + protected void buildCommandLineParameters(GeneralCommandLine commandLine) { + ParamsGroup group = commandLine.getParametersList().getParamsGroup(GROUP_SCRIPT); + assert group != null; + + Project project = myRunConfiguration.getProject(); + Course course = StudyTaskManager.getInstance(project).getCourse(); + assert course != null; + + group.addParameter(myRunConfiguration.getPathToTest()); + group.addParameter(course.getCourseDirectory()); + + group.addParameter(getFirstTaskFilePath()); + } + + @NotNull + private String getFirstTaskFilePath() { + String firstTaskFileName = StudyUtils.getFirst(myTask.getTaskFiles().keySet()); + return myTaskDir.findChild(EduNames.SRC) != null ? + FileUtil.join(myTaskDir.getPath(), EduNames.SRC, firstTaskFileName) : + FileUtil.join(myTaskDir.getPath(), firstTaskFileName); + } + + @Override + public ExecutionResult execute(Executor executor, CommandLinePatcher... patchers) throws ExecutionException { + CCUtils.createResources(myRunConfiguration.getProject(), myTask, myTaskDir); + ApplicationManager.getApplication().runWriteAction(() -> StudyCheckUtils.flushWindows(myTask, myTaskDir)); + + return super.execute(executor, patchers); + } + + @Override + protected ProcessHandler doCreateProcess(GeneralCommandLine commandLine) throws ExecutionException { + ProcessHandler handler = super.doCreateProcess(commandLine); + handler.addProcessListener(new ProcessAdapter() { + @Override + public void processTerminated(ProcessEvent event) { + ApplicationManager.getApplication().invokeLater(() -> EduUtils.deleteWindowDescriptions(myTask, myTaskDir)); + } + }); + return handler; + } +} diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestConfiguration.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestConfiguration.java new file mode 100644 index 000000000000..aca074e73274 --- /dev/null +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestConfiguration.java @@ -0,0 +1,80 @@ +package com.jetbrains.edu.coursecreator.run; + +import com.intellij.execution.ExecutionException; +import com.intellij.execution.Executor; +import com.intellij.execution.configurations.ConfigurationFactory; +import com.intellij.execution.configurations.RunProfileState; +import com.intellij.execution.configurations.RuntimeConfigurationException; +import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.openapi.options.SettingsEditor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.JDOMExternalizerUtil; +import com.intellij.openapi.util.WriteExternalException; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.python.run.AbstractPythonRunConfiguration; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +class PyCCRunTestConfiguration extends AbstractPythonRunConfiguration { + + public static final String PATH_ATTR = "studyTest"; + private Project myProject; + private String myPathToTest; + + public PyCCRunTestConfiguration(Project project, ConfigurationFactory factory) { + super(project, factory); + myProject = project; + } + + @Nullable + @Override + public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) throws ExecutionException { + return new PyCCCommandLineState(this, environment); + } + + @Override + protected SettingsEditor createConfigurationEditor() { + return new PyCCSettingsEditor(myProject); + } + + public String getPathToTest() { + return myPathToTest; + } + + public void setPathToTest(String pathToTest) { + myPathToTest = pathToTest; + } + + @Override + public void readExternal(Element element) throws InvalidDataException { + super.readExternal(element); + myPathToTest = JDOMExternalizerUtil.readField(element, PATH_ATTR); + } + + @Override + public void writeExternal(Element element) throws WriteExternalException { + super.writeExternal(element); + JDOMExternalizerUtil.writeField(element, PATH_ATTR, myPathToTest); + } + + @Override + public void checkConfiguration() throws RuntimeConfigurationException { + super.checkConfiguration(); + String message = "Select valid path to the file with tests"; + VirtualFile testsFile = LocalFileSystem.getInstance().findFileByPath(myPathToTest); + if (testsFile == null) { + throw new RuntimeConfigurationException(message); + } + VirtualFile taskDir = StudyUtils.getTaskDir(testsFile); + if (taskDir == null) { + throw new RuntimeConfigurationException(message); + } + if (StudyUtils.getTask(myProject, taskDir) == null) { + throw new RuntimeConfigurationException(message); + } + } +} diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestsConfigurationFactory.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestsConfigurationFactory.java new file mode 100644 index 000000000000..1d2de895d0a2 --- /dev/null +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestsConfigurationFactory.java @@ -0,0 +1,19 @@ +package com.jetbrains.edu.coursecreator.run; + +import com.intellij.execution.configurations.ConfigurationFactory; +import com.intellij.execution.configurations.ConfigurationType; +import com.intellij.execution.configurations.RunConfiguration; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; + +public class PyCCRunTestsConfigurationFactory extends ConfigurationFactory { + protected PyCCRunTestsConfigurationFactory(@NotNull ConfigurationType type) { + super(type); + } + + @NotNull + @Override + public RunConfiguration createTemplateConfiguration(@NotNull Project project) { + return new PyCCRunTestConfiguration(project, this); + } +} diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestsConfigurationProducer.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestsConfigurationProducer.java new file mode 100644 index 000000000000..53ca0c996cc2 --- /dev/null +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestsConfigurationProducer.java @@ -0,0 +1,94 @@ +package com.jetbrains.edu.coursecreator.run; + +import com.intellij.execution.Location; +import com.intellij.execution.actions.ConfigurationContext; +import com.intellij.execution.actions.RunConfigurationProducer; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.learning.StudyUtils; +import com.jetbrains.edu.learning.core.EduNames; +import com.jetbrains.edu.learning.courseFormat.Task; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class PyCCRunTestsConfigurationProducer extends RunConfigurationProducer { + protected PyCCRunTestsConfigurationProducer() { + super(PyCCRunTestsConfigurationType.getInstance()); + } + + @Override + protected boolean setupConfigurationFromContext(PyCCRunTestConfiguration configuration, + ConfigurationContext context, + Ref sourceElement) { + Project project = context.getProject(); + if (!CCUtils.isCourseCreator(project)) { + return false; + } + + String testsPath = getTestPath(context); + if (testsPath == null) { + return false; + } + VirtualFile testsFile = LocalFileSystem.getInstance().findFileByPath(testsPath); + if (testsFile == null) { + return false; + } + + String generatedName = generateName(testsFile, project); + if (generatedName == null) { + return false; + } + + configuration.setPathToTest(testsPath); + configuration.setName(generatedName); + return true; + } + + @Nullable + private static String generateName(@NotNull VirtualFile testsFile, @NotNull Project project) { + VirtualFile taskDir = StudyUtils.getTaskDir(testsFile); + if (taskDir == null) { + return null; + } + Task task = StudyUtils.getTask(project, taskDir); + if (task == null) { + return null; + } + return task.getLesson().getName() + "/" + task.getName(); + } + + @Nullable + private static String getTestPath(@NotNull ConfigurationContext context) { + Location location = context.getLocation(); + if (location == null) { + return null; + } + VirtualFile file = location.getVirtualFile(); + if (file == null) { + return null; + } + VirtualFile taskDir = StudyUtils.getTaskDir(file); + if (taskDir == null) { + return null; + } + + String testsPath = taskDir.findChild(EduNames.SRC) != null ? + FileUtil.join(taskDir.getPath(), EduNames.SRC, EduNames.TESTS_FILE) : + FileUtil.join(taskDir.getPath(), EduNames.TESTS_FILE); + return file.getPath().equals(testsPath) ? testsPath : null; + } + + @Override + public boolean isConfigurationFromContext(PyCCRunTestConfiguration configuration, ConfigurationContext context) { + String path = getTestPath(context); + if (path == null) { + return false; + } + return path.equals(configuration.getPathToTest()); + } +} diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestsConfigurationType.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestsConfigurationType.java new file mode 100644 index 000000000000..ac05cf1d8ca4 --- /dev/null +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCRunTestsConfigurationType.java @@ -0,0 +1,41 @@ +package com.jetbrains.edu.coursecreator.run; + +import com.intellij.execution.configurations.ConfigurationFactory; +import com.intellij.execution.configurations.ConfigurationType; +import com.intellij.execution.configurations.ConfigurationTypeUtil; +import com.intellij.icons.AllIcons; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class PyCCRunTestsConfigurationType implements ConfigurationType { + @Override + public String getDisplayName() { + return "Run Study Tests"; + } + + @Override + public String getConfigurationTypeDescription() { + return "Study Test Runner"; + } + + @Override + public Icon getIcon() { + return AllIcons.Actions.Lightning; + } + + @NotNull + @Override + public String getId() { + return "ccruntests"; + } + + @Override + public ConfigurationFactory[] getConfigurationFactories() { + return new ConfigurationFactory[]{new PyCCRunTestsConfigurationFactory(this)}; + } + + public static PyCCRunTestsConfigurationType getInstance() { + return ConfigurationTypeUtil.findConfigurationType(PyCCRunTestsConfigurationType.class); + } +} diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCSettingEditor.form b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCSettingEditor.form new file mode 100644 index 000000000000..c84b034ed12f --- /dev/null +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCSettingEditor.form @@ -0,0 +1,34 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCSettingsEditor.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCSettingsEditor.java new file mode 100644 index 000000000000..806f6a02ad5e --- /dev/null +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/run/PyCCSettingsEditor.java @@ -0,0 +1,69 @@ +package com.jetbrains.edu.coursecreator.run; + +import com.intellij.openapi.module.Module; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.options.SettingsEditor; +import com.intellij.openapi.project.Project; +import com.jetbrains.python.run.AbstractPyCommonOptionsForm; +import com.jetbrains.python.run.AbstractPythonRunConfiguration; +import com.jetbrains.python.run.PyCommonOptionsFormData; +import com.jetbrains.python.run.PyCommonOptionsFormFactory; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.util.List; + +public class PyCCSettingsEditor extends SettingsEditor { + private AbstractPyCommonOptionsForm myForm; + private Project myProject; + private JTextField myPathToTestFileField; + private JPanel myPanel; + + public PyCCSettingsEditor(Project project) { + myProject = project; + } + + @Override + protected void resetEditorFrom(PyCCRunTestConfiguration s) { + AbstractPythonRunConfiguration.copyParams(s, myForm); + myPathToTestFileField.setText(s.getPathToTest()); + } + + @Override + protected void applyEditorTo(PyCCRunTestConfiguration s) throws ConfigurationException { + AbstractPythonRunConfiguration.copyParams(myForm, s); + s.setPathToTest(myPathToTestFileField.getText()); + } + + @NotNull + @Override + protected JComponent createEditor() { + final JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.add(myPanel, BorderLayout.NORTH); + myForm = createEnvPanel(); + mainPanel.add(myForm.getMainPanel(), BorderLayout.SOUTH); + return mainPanel; + } + + + @NotNull + private AbstractPyCommonOptionsForm createEnvPanel() { + return PyCommonOptionsFormFactory.getInstance().createForm(new PyCommonOptionsFormData() { + @Override + public Project getProject() { + return myProject; + } + + @Override + public List getValidModules() { + return AbstractPythonRunConfiguration.getValidModules(myProject); + } + + @Override + public boolean showConfigureInterpretersLink() { + return false; + } + }); + } +} diff --git a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java index 43f5fc5645f4..fdb110718b34 100644 --- a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java +++ b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyCheckAction.java @@ -16,6 +16,7 @@ import com.jetbrains.edu.learning.checker.StudyCheckTask; import com.jetbrains.edu.learning.checker.StudyCheckUtils; import com.jetbrains.edu.learning.checker.StudyTestRunner; import com.jetbrains.edu.learning.checker.StudyTestsOutputParser; +import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.StudyStatus; import com.jetbrains.edu.learning.courseFormat.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; @@ -103,9 +104,11 @@ public class PyStudyCheckAction extends StudyCheckAction { myTaskManger.setStatus(taskFile, StudyStatus.Failed); continue; } - CommandProcessor.getInstance().runUndoTransparentAction(() -> ApplicationManager.getApplication().runWriteAction(() -> { - StudyCheckUtils.runSmartTestProcess(myTaskDir, testRunner, name, taskFile, project); - })); + if (EduNames.STUDY.equals(myTaskManger.getCourse().getCourseMode())) { + CommandProcessor.getInstance().runUndoTransparentAction(() -> ApplicationManager.getApplication().runWriteAction(() -> { + StudyCheckUtils.runSmartTestProcess(myTaskDir, testRunner, name, taskFile, project); + })); + } } StudyCheckUtils.showTestResultPopUp(testsOutput.getMessage(), MessageType.ERROR.getPopupBackground(), project); StudyCheckUtils.navigateToFailedPlaceholder(myStudyState, myTask, myTaskDir, project);