Merge remote-tracking branch 'origin/liana/course-creator-improvements'

# Conflicts:
#	python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCLessonMoveHandlerDelegate.java
#	python/educational-core/student/resources/META-INF/plugin.xml
#	python/educational-core/student/src/com/jetbrains/edu/learning/StudyPluginConfigurator.java
#	python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java
#	python/educational-core/student/src/com/jetbrains/edu/learning/StudyTaskManager.java
#	python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java
#	python/educational-core/student/src/com/jetbrains/edu/learning/editor/StudyEditorFactoryListener.java
#	python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudySwingToolWindow.java
This commit is contained in:
liana.bakradze
2016-04-23 09:49:06 +03:00
85 changed files with 1635 additions and 1399 deletions
@@ -19,18 +19,17 @@
<projectService serviceImplementation="com.jetbrains.edu.coursecreator.CCProjectService"/>
<treeStructureProvider implementation="com.jetbrains.edu.coursecreator.projectView.CCTreeStructureProvider"/>
<refactoring.elementListenerProvider implementation="com.jetbrains.edu.coursecreator.CCRefactoringElementListenerProvider"/>
<refactoring.moveHandler implementation="com.jetbrains.edu.coursecreator.CCLessonMoveHandlerDelegate" order="first"/>
<refactoring.moveHandler implementation="com.jetbrains.edu.coursecreator.CCTaskMoveHandlerDelegate" order="first"/>
<renameHandler implementation="com.jetbrains.edu.coursecreator.CCTaskRenameHandler" order="first"/>
<renameHandler implementation="com.jetbrains.edu.coursecreator.CCLessonRenameHandler" order="first"/>
<refactoring.moveHandler implementation="com.jetbrains.edu.coursecreator.handlers.CCLessonMoveHandlerDelegate" order="first"/>
<refactoring.moveHandler implementation="com.jetbrains.edu.coursecreator.handlers.CCTaskMoveHandlerDelegate" order="first"/>
<renameHandler implementation="com.jetbrains.edu.coursecreator.handlers.CCTaskRenameHandler" order="first"/>
<renameHandler implementation="com.jetbrains.edu.coursecreator.handlers.CCLessonRenameHandler" order="first"/>
</extensions>
<extensions defaultExtensionNs="Edu">
<studyActionsProvider implementation="com.jetbrains.edu.coursecreator.CCStudyActionsProvider"/>
<studyActionListener implementation="com.jetbrains.edu.coursecreator.CCStudyActionListener"/>
</extensions>
<application-components>
<!-- Add your application components here -->
</application-components>
<project-components>
<!-- Add your project components here -->
<component>
<implementation-class>com.jetbrains.edu.coursecreator.CCProjectComponent</implementation-class>
</component>
@@ -46,49 +45,40 @@
<action id="CreateLesson" class="com.jetbrains.edu.coursecreator.actions.CCCreateLesson">
<add-to-group group-id="NewGroup" anchor="before" relative-to-action="NewFile"/>
</action>
<action id="ChangeCourseInfo" class="com.jetbrains.edu.coursecreator.actions.CCChangeCourseInfo">
<add-to-group group-id="ProjectViewPopupMenu" anchor="before" relative-to-action="PackCourse"/>
</action>
<action id="CreateTask" class="com.jetbrains.edu.coursecreator.actions.CCCreateTask">
<add-to-group group-id="NewGroup" anchor="before" relative-to-action="NewFile"/>
</action>
<action id="CreateTaskFile" class="com.jetbrains.edu.coursecreator.actions.CCCreateTaskFile">
<add-to-group group-id="NewGroup" anchor="before" relative-to-action="NewFile"/>
</action>
<action id="AddTaskFile" class="com.jetbrains.edu.coursecreator.actions.CCAddAsTaskFile" text="Add As Task File">
<add-to-group group-id="ProjectViewPopupMenu" anchor="first"/>
</action>
<action id="AddTaskWindow" class="com.jetbrains.edu.coursecreator.actions.CCAddAnswerPlaceholder">
<add-to-group group-id="EditorPopupMenu" anchor="before" relative-to-action="CopyReference"/>
</action>
<action id="ShowTaskWindowDetails" class="com.jetbrains.edu.coursecreator.actions.CCShowAnswerPlaceholderDetails">
<add-to-group group-id="EditorPopupMenu" anchor="before" relative-to-action="CopyReference"/>
</action>
<action id="DeleteTaskWindow" class="com.jetbrains.edu.coursecreator.actions.CCDeleteAnswerPlaceholder">
<add-to-group group-id="EditorPopupMenu" anchor="before" relative-to-action="CopyReference"/>
</action>
<action id="ShowPreview" class="com.jetbrains.edu.coursecreator.actions.CCShowPreview">
<add-to-group group-id="ProjectViewPopupMenu" anchor="first"/>
<add-to-group group-id="EditorTabPopupMenu"/>
</action>
<action id="PackCourse" class="com.jetbrains.edu.coursecreator.actions.CCCreateCourseArchive">
<add-to-group group-id="ProjectViewPopupMenu" anchor="before" relative-to-action="CutCopyPasteGroup"/>
<add-to-group group-id="FileMenu" relative-to-action="FileMainSettingsGroup" anchor="before"/>
</action>
<action id="UnpackCourse" class="com.jetbrains.edu.coursecreator.actions.CCFromCourseArchive"/>
<action id="PushLesson" class="com.jetbrains.edu.coursecreator.actions.CCPushLesson">
<add-to-group group-id="ProjectViewPopupMenu" anchor="before" relative-to-action="CutCopyPasteGroup"/>
<add-to-group group-id="FileMenu" relative-to-action="FileMainSettingsGroup" anchor="before"/>
</action>
<action id="PushCourse" class="com.jetbrains.edu.coursecreator.actions.CCPushCourse">
<add-to-group group-id="ProjectViewPopupMenu" anchor="before" relative-to-action="CutCopyPasteGroup"/>
<add-to-group group-id="FileMenu" relative-to-action="FileMainSettingsGroup" anchor="before"/>
</action>
<action id="DeleteAllPlaceholders" class="com.jetbrains.edu.coursecreator.actions.CCDeleteAllAnswerPlaceholdersAction">
<group id="CCProjectViewGroup">
<action id="AddTaskFile" class="com.jetbrains.edu.coursecreator.actions.CCAddAsTaskFile" text="Add as Task File"/>
<action id="HideTaskFile" class="com.jetbrains.edu.coursecreator.actions.CCHideFromStudent" text="Hide from Student"/>
<action id="ShowPreview" class="com.jetbrains.edu.coursecreator.actions.CCShowPreview">
<add-to-group group-id="EditorTabPopupMenu"/>
</action>
<action id="DeleteAllPlaceholders" class="com.jetbrains.edu.coursecreator.actions.CCDeleteAllAnswerPlaceholdersAction"/>
<action id="PackCourse" class="com.jetbrains.edu.coursecreator.actions.CCCreateCourseArchive">
<add-to-group group-id="FileMenu" relative-to-action="FileMainSettingsGroup" anchor="before"/>
</action>
<action id="PushLesson" class="com.jetbrains.edu.coursecreator.actions.CCPushLesson">
<add-to-group group-id="FileMenu" relative-to-action="FileMainSettingsGroup" anchor="before"/>
</action>
<action id="PushCourse" class="com.jetbrains.edu.coursecreator.actions.CCPushCourse">
<add-to-group group-id="FileMenu" relative-to-action="FileMainSettingsGroup" anchor="before"/>
</action>
<action id="ChangeCourseInfo" class="com.jetbrains.edu.coursecreator.actions.CCChangeCourseInfo"/>
<separator/>
<add-to-group group-id="ProjectViewPopupMenu" anchor="before" relative-to-action="CutCopyPasteGroup"/>
<add-to-group group-id="EditorPopupMenu" anchor="after" relative-to-action="DeleteTaskWindow"/>
</action>
</group>
<group id="AnswerPlaceholderGroup">
<action id="EditAnswerPlaceholder" class="com.jetbrains.edu.coursecreator.actions.CCEditAnswerPlaceholder"/>
<action id="AddTaskWindow" class="com.jetbrains.edu.coursecreator.actions.CCAddAnswerPlaceholder"/>
<reference ref="DeleteAllPlaceholders"/>
<separator/>
<add-to-group group-id="EditorPopupMenu" anchor="first"/>
</group>
<action id="UnpackCourse" class="com.jetbrains.edu.coursecreator.actions.CCFromCourseArchive"/>
</actions>
</idea-plugin>
@@ -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();
}
}
@@ -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;
}
}
@@ -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);
}
}
@@ -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<CCProjectService> {
private Course myCourse;
private static final Map<Document, EduDocumentListener> myDocumentListeners = new HashMap<Document, EduDocumentListener>();
@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<AnswerPlaceholder> 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<Lesson> lessons = myCourse.getLessons();
if (!EduUtils.indexIsValid(lessonIndex, lessons)) {
return null;
}
Lesson lesson = lessons.get(lessonIndex);
int taskIndex = EduUtils.getIndex(taskDirName, EduNames.TASK);
List<Task> 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;
}
@@ -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<String, TaskFile> 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<String, TaskFile> taskFiles = task.getTaskFiles();
TaskFile taskFile = task.getTaskFile(oldName);
taskFiles.remove(oldName);
taskFiles.put(file.getName(), taskFile);
CCUtils.createResourceFile(file.getVirtualFile(), course, taskDir.getVirtualFile());
}
@Override
@@ -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);
}
}
@@ -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()};
}
}
@@ -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<String, TaskFile> files = task.getTaskFiles();
for (Map.Entry<String, TaskFile> 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);
}
}
}
@@ -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<VirtualFile, StudyItem>() {
@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;
@@ -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<AnswerPlaceholder> 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<AnswerPlaceholder> 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;
}
}
@@ -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);
}
}
@@ -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;
}
@@ -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;
}
}
@@ -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);
@@ -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) {
@@ -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<PsiDirectory> 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
@@ -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);
}
}
@@ -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<AnswerPlaceholder> placeholders = new ArrayList<AnswerPlaceholder>(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<AnswerPlaceholder> myPlaceholders;
private final Editor myEditor;
private final VirtualFile myFile;
private final Project myProject;
TaskFile myTaskFile;
public ClearPlaceholders(TaskFile taskFile, List<AnswerPlaceholder> placeholders, Editor editor, VirtualFile file, Project project) {
public ClearPlaceholders(TaskFile taskFile, List<AnswerPlaceholder> 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
@@ -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<AnswerPlaceholder> 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);
}
}
}
@@ -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);
}
}
@@ -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);
}
}
}
@@ -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<String, TaskFile> 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);
}
});
@@ -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<String, TaskFile> 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;
}
}
@@ -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;
}
@@ -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;
}
@@ -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<String, TaskFile> 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<String, TaskFile> 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;
}
}
@@ -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));
@@ -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);
}
@@ -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);
}
}
@@ -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;
@@ -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();
}
@@ -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);
}
}
@@ -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;
@@ -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;
}
}
@@ -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<AbstractTreeNode> modify(@NotNull AbstractTreeNode parent,
@@ -26,18 +25,16 @@ public class CCTreeStructureProvider implements TreeStructureProvider, DumbAware
if (!needModify(parent)) {
return children;
}
Collection<AbstractTreeNode> nodes = new ArrayList<AbstractTreeNode>();
Collection<AbstractTreeNode> 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<AbstractTreeNode> selected, String dataName) {
return null;
return CCUtils.isCourseCreator(project);
}
}
@@ -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());
@@ -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) {
@@ -36,7 +36,9 @@
<with attribute="implementationClass" implements="com.jetbrains.edu.learning.StudyLanguageManager"/>
</extensionPoint>
<extensionPoint qualifiedName="Edu.studyPluginConfigurator" interface="com.jetbrains.edu.learning.StudyPluginConfigurator"/>
<extensionPoint qualifiedName="Edu.studyActionsProvider" interface="com.jetbrains.edu.learning.StudyActionsProvider"/>
<extensionPoint qualifiedName="Edu.studyTwitterPluginConfigurator" interface="com.jetbrains.edu.learning.StudyTwitterPluginConfigurator"/>
<extensionPoint qualifiedName="Edu.studyActionListener" interface="com.jetbrains.edu.learning.StudyActionListener"/>
<extensionPoint qualifiedName="Edu.optionsProvider" beanClass="com.jetbrains.edu.learning.settings.StudyOptionsProviderEP">>
<with attribute="instance" implements="com.jetbrains.edu.learning.settings.StudyOptionsProvider"/>
</extensionPoint>
@@ -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<StudyActionListener> EP_NAME = ExtensionPointName.create("Edu.studyActionListener");
void beforeCheck(AnActionEvent event);
}
@@ -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<StudyActionsProvider> EP_NAME = ExtensionPointName.create("Edu.studyActionsProvider");
AnAction[] getActions();
}
@@ -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()));
}
}
@@ -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);
}
};
}
@@ -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]);
@@ -40,6 +40,6 @@ public interface StudyPluginConfigurator {
StudyAfterCheckAction[] getAfterCheckActions();
@NotNull String getLanguageScriptUrl();
boolean accept(@NotNull final Project project);
}
@@ -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<AnAction> 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<Lesson> lessons = course.getLessons();
if (StudyUtils.indexIsValid(lessonIndex, lessons)) {
final Lesson lesson = lessons.get(lessonIndex);
final List<Task> 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<Lesson> lessons = course.getLessons();
if (StudyUtils.indexIsValid(lessonIndex, lessons)) {
final Lesson lesson = lessons.get(lessonIndex);
final List<Task> 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);
}
}
}
}
}
}
}
@@ -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<Element>, Dumb
public Map<Task, List<UserTest>> myUserTests = new HashMap<>();
public List<String> 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<Element>, Dumb
@NotNull
public List<UserTest> getUserTests(@NotNull final Task task) {
final List<UserTest> userTests = myUserTests.get(task);
return userTests != null ? userTests : Collections.emptyList();
return userTests != null ? userTests : Collections.<UserTest>emptyList();
}
public void removeUserTest(@NotNull final Task task, @NotNull final UserTest userTest) {
@@ -260,4 +263,20 @@ public class StudyTaskManager implements PersistentStateComponent<Element>, 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;
}
}
@@ -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;
}
}
@@ -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();
}
@@ -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<Boolean> 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);
}
@@ -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);
}
}
@@ -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);
}
});
@@ -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);
}
}
@@ -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";
@@ -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);
}
@@ -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);
@@ -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);
}
@@ -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(() -> {
@@ -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<RangeMarker> 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);
@@ -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<AnswerPlaceholderWrapper> myAnswerPlaceholders = new ArrayList<AnswerPlaceholderWrapper>();
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) {
@@ -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() {
}
@@ -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();
@@ -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;
@@ -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;
}
}
@@ -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) {
@@ -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));
}
}
}
}
@@ -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<AbstractTreeNode> modify(@NotNull AbstractTreeNode parent,
@NotNull Collection<AbstractTreeNode> children,
ViewSettings settings) {
if (!isCourseBasedProject(parent)) {
if (!needModify(parent)) {
return children;
}
Collection<AbstractTreeNode> 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
@@ -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);
}
@@ -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);
}
}
@@ -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);
}
}
@@ -23,5 +23,6 @@
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module" module-name="xml" />
</component>
</module>
@@ -21,27 +21,16 @@
<extensions defaultExtensionNs="com.intellij">
<directoryProjectGenerator implementation="com.jetbrains.edu.coursecreator.PyCCProjectGenerator"/>
<configurationType implementation="com.jetbrains.edu.coursecreator.run.PyCCRunTestsConfigurationType"/>
<runConfigurationProducer implementation="com.jetbrains.edu.coursecreator.run.PyCCRunTestsConfigurationProducer"/>
</extensions>
<extensions defaultExtensionNs="Edu">
<CCLanguageManager implementationClass="com.jetbrains.edu.coursecreator.PyCCLanguageManager" language="Python"/>
<executeFile implementation="com.jetbrains.edu.coursecreator.PyCCRunTests"/>
</extensions>
<extensions defaultExtensionNs="Pythonid">
<pyReferenceResolveProvider implementation="com.jetbrains.edu.coursecreator.PyCCReferenceResolveProvider"/>
</extensions>
<application-components>
<!-- Add your application components here -->
</application-components>
<project-components>
<!-- Add your project components here -->
</project-components>
<actions>
<action id="CCRunTests" class="com.jetbrains.edu.coursecreator.actions.PyCCRunTestsAction" text="Run Tests" description="Run tests"/>
</actions>
</idea-plugin>
@@ -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());
}
}
@@ -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) {
@@ -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<RatedResolveResult> 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);
@@ -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;
}
}
@@ -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);
}
}
@@ -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;
}
}
@@ -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<PyCCRunTestConfiguration> {
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<PyCCRunTestConfiguration> 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);
}
}
}
@@ -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);
}
}
@@ -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<PyCCRunTestConfiguration> {
protected PyCCRunTestsConfigurationProducer() {
super(PyCCRunTestsConfigurationType.getInstance());
}
@Override
protected boolean setupConfigurationFromContext(PyCCRunTestConfiguration configuration,
ConfigurationContext context,
Ref<PsiElement> 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());
}
}
@@ -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);
}
}
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.edu.coursecreator.run.PyCCSettingsEditor">
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="ae76e" class="javax.swing.JTextField" binding="myPathToTestFileField">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
<vspacer id="d7ebc">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="704f6" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Path to Test File"/>
</properties>
</component>
</children>
</grid>
</form>
@@ -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<PyCCRunTestConfiguration> {
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<Module> getValidModules() {
return AbstractPythonRunConfiguration.getValidModules(myProject);
}
@Override
public boolean showConfigureInterpretersLink() {
return false;
}
});
}
}
@@ -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);