<renameHandler implementation="com.jetbrains.edu.coursecreator.handlers.CCLessonRenameHandler" order="first"/>
<applicationService serviceInterface="com.jetbrains.edu.coursecreator.settings.CCSettings"
serviceImplementation="com.jetbrains.edu.coursecreator.settings.CCSettings"/>
+ <editorTabTitleProvider implementation="com.jetbrains.edu.coursecreator.CCTestsTabTitleProvider"/>
+ <editorNotificationProvider implementation="com.jetbrains.edu.coursecreator.CCStepEditorNotificationProvider"/>
</extensions>
<extensions defaultExtensionNs="Edu">
<studyActionsProvider implementation="com.jetbrains.edu.coursecreator.CCStudyActionsProvider"/>
</group>
<action id="UnpackCourse" class="com.jetbrains.edu.coursecreator.actions.CCFromCourseArchive"/>
+ <action class="com.jetbrains.edu.coursecreator.actions.CCNewStepAction" id="CC.NewStep">
+ <add-to-group group-id="AnswerPlaceholderGroup" relative-to-action="DeleteAllPlaceholders" anchor="after"/>
+ </action>
</actions>
+
</idea-plugin>
\ No newline at end of file
import com.intellij.lang.LanguageExtension;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
+import com.jetbrains.edu.learning.courseFormat.Task;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
default boolean isTestFile(VirtualFile file) {
return false;
}
+
+ default void createTestsForNewStep(@NotNull Project project, @NotNull Task task) {}
}
--- /dev/null
+package com.jetbrains.edu.coursecreator;
+
+import com.intellij.openapi.editor.colors.EditorColors;
+import com.intellij.openapi.editor.colors.EditorColorsManager;
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.ui.popup.ListSeparator;
+import com.intellij.openapi.ui.popup.PopupStep;
+import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.EditorNotificationPanel;
+import com.intellij.ui.EditorNotifications;
+import com.intellij.ui.awt.RelativePoint;
+import com.jetbrains.edu.coursecreator.actions.CCNewStepAction;
+import com.jetbrains.edu.learning.StudyStepManager;
+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;
+
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class CCStepEditorNotificationProvider extends EditorNotifications.Provider<EditorNotificationPanel> implements DumbAware {
+ private static final Key<EditorNotificationPanel> KEY = Key.create("edu.coursecreator.step");
+ public static final String SWITCH_STEP = "Switch step";
+ public static final Integer ADD_STEP_ID = -2;
+ private final Project myProject;
+
+ public CCStepEditorNotificationProvider(Project project) {
+ myProject = project;
+ }
+
+ @NotNull
+ @Override
+ public Key<EditorNotificationPanel> getKey() {
+ return KEY;
+ }
+
+ @Nullable
+ @Override
+ public EditorNotificationPanel createNotificationPanel(@NotNull VirtualFile file, @NotNull FileEditor fileEditor) {
+ if (!CCUtils.isCourseCreator(myProject)) {
+ return null;
+ }
+ boolean isTestFile = CCUtils.isTestsFile(myProject, file);
+ if (!isTestFile && StudyUtils.getTaskFile(myProject, file) == null) {
+ return null;
+ }
+ Task task = StudyUtils.getTaskForFile(myProject, file);
+ if (task == null || task.getAdditionalSteps().isEmpty()) {
+ return null;
+ }
+ int activeStepIndex = task.getActiveStepIndex() + 2;
+ int stepsNum = task.getAdditionalSteps().size() + 1;
+ EditorNotificationPanel panel = new EditorNotificationPanel() {
+ @Override
+ public Color getBackground() {
+ Color color = EditorColorsManager.getInstance().getGlobalScheme().getColor(EditorColors.GUTTER_BACKGROUND);
+ return color == null ? super.getBackground() : color;
+ }
+ };
+ String header = isTestFile ? "test" : "task file";
+ panel.setText("This is " + header + " for " + EduNames.STEP + " " + activeStepIndex + "/" + stepsNum);
+ panel.createActionLabel(SWITCH_STEP, () -> {
+ ArrayList<Integer> values = new ArrayList<>();
+ values.add(-1);
+ for (int i = 0; i < task.getAdditionalSteps().size(); i++) {
+ values.add(i);
+ }
+ values.add(ADD_STEP_ID);
+ JBPopupFactory.getInstance().createListPopup(new SwitchStepPopupStep(SWITCH_STEP, values, task, file)).show(RelativePoint.getSouthEastOf(panel));
+ });
+ return panel;
+ }
+
+ private class SwitchStepPopupStep extends BaseListPopupStep<Integer> {
+ private final Task myTask;
+ private final VirtualFile myFile;
+
+ public SwitchStepPopupStep(@Nullable String title,
+ List<Integer> values,
+ Task task, VirtualFile file) {
+ super(title, values);
+ myTask = task;
+ myFile = file;
+ }
+
+ @NotNull
+ @Override
+ public String getTextFor(Integer value) {
+ if (value.equals(ADD_STEP_ID)) {
+ return CCNewStepAction.NEW_STEP;
+ }
+ int stepNum = value + 2;
+ String text = EduNames.STEP + " " + stepNum;
+ if (value == myTask.getActiveStepIndex()) {
+ text += " (current step)";
+ }
+ return text;
+ }
+
+ @Override
+ public PopupStep onChosen(Integer selectedValue, boolean finalChoice) {
+ if (finalChoice) {
+ if (selectedValue.equals(ADD_STEP_ID)) {
+ return doFinalStep(() -> CCNewStepAction.addStep(myFile, myProject));
+ }
+ return doFinalStep(() -> StudyStepManager.switchStep(myProject, myTask, selectedValue));
+ } else {
+ if (hasSubstep(selectedValue)) {
+ return new ActionsPopupStep(myTask, selectedValue);
+ }
+ }
+ return super.onChosen(selectedValue, false);
+ }
+
+ @Override
+ public boolean hasSubstep(Integer selectedValue) {
+ return !selectedValue.equals(ADD_STEP_ID);
+ }
+
+ @Override
+ public int getDefaultOptionIndex() {
+ return myTask.getActiveStepIndex() + 1;
+ }
+
+ @Nullable
+ @Override
+ public ListSeparator getSeparatorAbove(Integer value) {
+ return value.equals(ADD_STEP_ID) ? new ListSeparator(): null;
+ }
+ }
+
+ private class ActionsPopupStep extends BaseListPopupStep<String> {
+
+ public static final String SELECT = "Select";
+ public static final String DELETE = "Delete";
+ private final Task myTask;
+ private final int myStepIndex;
+
+ public ActionsPopupStep(Task task, int stepIndex) {
+ super(null, Arrays.asList(SELECT, DELETE));
+ myTask = task;
+ myStepIndex = stepIndex;
+ }
+
+ @Override
+ public PopupStep onChosen(String selectedValue, boolean finalChoice) {
+ if (finalChoice) {
+ if (selectedValue.equals(SELECT)) {
+ StudyStepManager.switchStep(myProject, myTask, myStepIndex);
+ } else {
+ if (myStepIndex != myTask.getAdditionalSteps().size() - 1) {
+ //TODO: implement
+ } else {
+ StudyStepManager.deleteStep(myProject, myTask, myStepIndex);
+ }
+ return FINAL_CHOICE;
+ }
+ }
+ return super.onChosen(selectedValue, finalChoice);
+ }
+ }
+}
if (taskDir == null) {
return;
}
- CCUtils.createResources(project, task, taskDir);
+ CCUtils.updateResources(project, task, taskDir);
}
}
--- /dev/null
+package com.jetbrains.edu.coursecreator;
+
+import com.intellij.openapi.fileEditor.impl.EditorTabTitleProvider;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.jetbrains.edu.learning.StudyLanguageManager;
+import com.jetbrains.edu.learning.StudyTaskManager;
+import com.jetbrains.edu.learning.StudyUtils;
+import com.jetbrains.edu.learning.courseFormat.Course;
+import org.jetbrains.annotations.Nullable;
+
+public class CCTestsTabTitleProvider implements EditorTabTitleProvider {
+ @Nullable
+ @Override
+ public String getEditorTabTitle(Project project, VirtualFile file) {
+ if (!CCUtils.isCourseCreator(project)) {
+ return null;
+ }
+ if (!CCUtils.isTestsFile(project, file)) {
+ return null;
+ }
+ Course course = StudyTaskManager.getInstance(project).getCourse();
+ assert course != null;
+ StudyLanguageManager manager = StudyUtils.getLanguageManager(course);
+ if (manager == null) {
+ return null;
+ }
+ return manager.getTestFileName();
+ }
+}
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;
import com.intellij.openapi.roots.ModuleRootManager;
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.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.*;
+import com.jetbrains.edu.learning.courseFormat.Course;
+import com.jetbrains.edu.learning.courseFormat.StudyItem;
+import com.jetbrains.edu.learning.courseFormat.Task;
+import com.jetbrains.edu.learning.courseFormat.TaskFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
}
- public static void createResources(Project project, Task task, VirtualFile taskDir) {
- Map<String, TaskFile> files = task.getTaskFiles();
+ public static void updateResources(Project project, Task task, VirtualFile taskDir) {
+ Course course = StudyTaskManager.getInstance(project).getCourse();
+ if (course == null) {
+ return;
+ }
+ VirtualFile lessonVF = taskDir.getParent();
+ if (lessonVF == null) {
+ return;
+ }
+
+ String taskResourcesPath = FileUtil.join(course.getCourseDirectory(), lessonVF.getName(), taskDir.getName());
+ File taskResourceFile = new File(taskResourcesPath);
+ if (!taskResourceFile.exists()) {
+ if (!taskResourceFile.mkdirs()) {
+ LOG.info("Failed to create resources for task " + taskResourcesPath);
+ }
+ }
+ VirtualFile studentDir = LocalFileSystem.getInstance().findFileByIoFile(taskResourceFile);
+ if (studentDir == null) {
+ return;
+ }
+ Map<String, TaskFile> files = StudyUtils.getTaskFiles(task);
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) {
- LOG.info("pattern file for " + child.getPath() + " not found");
+ VirtualFile answerFile = taskDir.findChild(name);
+ if (answerFile == null) {
continue;
}
- DocumentUtil.writeInRunUndoTransparentAction(() -> {
- patternDocument.replaceString(0, patternDocument.getTextLength(), document.getCharsSequence());
- FileDocumentManager.getInstance().saveDocument(patternDocument);
+ ApplicationManager.getApplication().runWriteAction(() -> {
+ EduUtils.createStudentFile(CCUtils.class, project, answerFile, task.getActiveStepIndex(), studentDir, null);
});
- TaskFile target = new TaskFile();
- TaskFile.copy(entry.getValue(), target);
- for (AnswerPlaceholder placeholder : target.getAnswerPlaceholders()) {
- placeholder.setUseLength(false);
- }
- EduUtils.createStudentDocument(project, target, child, patternDocument);
}
}
}
@Override
public void fileDeleted(@NotNull VirtualFileEvent event) {
VirtualFile removedFile = event.getFile();
- if (removedFile.getPath().contains(CCUtils.GENERATED_FILES_FOLDER)) {
+ String path = removedFile.getPath();
+ if (path.contains(CCUtils.GENERATED_FILES_FOLDER)) {
return;
}
return;
}
Course course = StudyTaskManager.getInstance(project).getCourse();
- if (course == null) {
+ if (course == null || path.contains(course.getCourseDirectory())) {
return;
}
final TaskFile taskFile = StudyUtils.getTaskFile(project, removedFile);
if (task == null) {
return;
}
+ //TODO: remove from steps as well
task.getTaskFiles().remove(removedTaskFile.getName());
}
}
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.SelectionModel;
-import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.psi.PsiDocumentManager;
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;
answerPlaceholder.setTaskFile(taskFile);
taskFile.sortAnswerPlaceholders();
-
- computeInitialState(project, file, taskFile, document);
-
+ answerPlaceholder.setPossibleAnswer(model.hasSelection() ? model.getSelectedText() : EduNames.PLACEHOLDER);
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(new AnswerPlaceholder.MyInitialState(fromPlaceholder.getOffset(), fromPlaceholder.getLength()));
- }
- }
-
@Override
protected void performAnswerPlaceholderAction(@NotNull CCState state) {
if (canAddPlaceholder(state)) {
import com.intellij.openapi.vfs.VfsUtil;
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.coursecreator.CCLanguageManager;
import com.jetbrains.edu.coursecreator.CCUtils;
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.*;
+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.statistics.EduUsagesCollector;
import org.jetbrains.annotations.NotNull;
import java.io.*;
-import java.util.List;
-import java.util.Map;
import java.util.zip.ZipOutputStream;
public class CCCreateCourseArchive extends DumbAwareAction {
private static final Logger LOG = Logger.getInstance(CCCreateCourseArchive.class.getName());
+ public static final String GENERATE_COURSE_ARCHIVE = "Generate Course Archive";
private String myZipName;
private String myLocationDir;
}
public CCCreateCourseArchive() {
- super("Generate Course Archive", "Generate Course Archive", null);
+ super(GENERATE_COURSE_ARCHIVE, GENERATE_COURSE_ARCHIVE, null);
}
@Override
copyChild(archiveFolder, filter, child, fromFile);
}
- final List<Lesson> lessons = course.getLessons();
-
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
- final Map<TaskFile, TaskFile> savedTaskFiles = new HashMap<TaskFile, TaskFile>();
- replaceAnswerFilesWithTaskFiles(savedTaskFiles);
- generateJson(project, archiveFolder);
- resetTaskFiles(savedTaskFiles);
+ Course courseCopy = course.copy();
+ replaceAnswerFilesWithTaskFiles(courseCopy);
+ generateJson(archiveFolder, courseCopy);
VirtualFileManager.getInstance().refreshWithoutFileWatcher(false);
packCourse(archiveFolder, locationDir, zipName, showMessage);
synchronize(project);
}
- private void replaceAnswerFilesWithTaskFiles(Map<TaskFile, TaskFile> savedTaskFiles) {
- for (Lesson lesson : lessons) {
+ private void replaceAnswerFilesWithTaskFiles(Course courseCopy) {
+ for (Lesson lesson : courseCopy.getLessons()) {
final VirtualFile lessonDir = baseDir.findChild(EduNames.LESSON + String.valueOf(lesson.getIndex()));
if (lessonDir == null) continue;
for (Task task : lesson.getTaskList()) {
final VirtualFile taskDir = lessonDir.findChild(EduNames.TASK + String.valueOf(task.getIndex()));
if (taskDir == null) continue;
- for (final Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
- TaskFile taskFileCopy = new TaskFile();
- TaskFile taskFile = entry.getValue();
- TaskFile.copy(taskFile, taskFileCopy);
- savedTaskFiles.put(taskFile, taskFileCopy);
-
- VirtualFile userFileDir = VfsUtil.findRelativeFile(archiveFolder, lessonDir.getName(), taskDir.getName());
- if (userFileDir == null) {
+ VirtualFile studentFileDir = VfsUtil.findRelativeFile(archiveFolder, lessonDir.getName(), taskDir.getName());
+ if (studentFileDir == null) {
+ continue;
+ }
+ for (TaskFile taskFile : task.getTaskFiles().values()) {
+ VirtualFile answerFile = taskDir.findChild(taskFile.name);
+ if (answerFile == null) {
continue;
}
- String taskFileName = entry.getKey();
- EduUtils.createStudentFileFromAnswer(project, userFileDir, taskDir, taskFileName, taskFile);
+ EduUtils.createStudentFile(this, project, answerFile, -1, studentFileDir, task);
}
- }
}
}
+ }
});
-
-
}
private static void copyChild(VirtualFile archiveFolder, FileFilter filter, VirtualFile child, File fromFile) {
}
}
- private static void resetTaskFiles(Map<TaskFile, TaskFile> savedTaskFiles) {
- for (Map.Entry<TaskFile, TaskFile> entry : savedTaskFiles.entrySet()) {
- List<AnswerPlaceholder> placeholders = entry.getValue().getAnswerPlaceholders();
- for (AnswerPlaceholder placeholder : placeholders) {
- placeholder.setUseLength(false);
- }
- entry.getKey().setAnswerPlaceholders(placeholders);
- }
- }
-
private static void synchronize(@NotNull final Project project) {
VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
ProjectView.getInstance(project).refresh();
}
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
- private static void generateJson(@NotNull final Project project, VirtualFile parentDir) {
- final Course course = StudyTaskManager.getInstance(project).getCourse();
+ private static void generateJson(VirtualFile parentDir, Course course) {
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);
return;
}
final StudyState studyState = new StudyState(selectedEditor);
- VirtualFile taskTextFile = StudyUtils.findTaskDescriptionVirtualFile(studyState.getTaskDir());
+ VirtualFile taskTextFile = StudyUtils.findTaskDescriptionVirtualFile(project, studyState.getTaskDir());
if (taskTextFile == null) {
LOG.info("Failed to find task.html");
return;
--- /dev/null
+package com.jetbrains.edu.coursecreator.actions;
+
+import com.intellij.ide.fileTemplates.FileTemplate;
+import com.intellij.ide.fileTemplates.FileTemplateManager;
+import com.intellij.ide.fileTemplates.FileTemplateUtil;
+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.diagnostic.Logger;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.io.FileUtilRt;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.PsiManager;
+import com.jetbrains.edu.coursecreator.CCLanguageManager;
+import com.jetbrains.edu.coursecreator.CCUtils;
+import com.jetbrains.edu.coursecreator.settings.CCSettings;
+import com.jetbrains.edu.learning.StudyStepManager;
+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.Step;
+import com.jetbrains.edu.learning.courseFormat.Task;
+import com.jetbrains.edu.learning.courseFormat.TaskFile;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+public class CCNewStepAction extends DumbAwareAction {
+ private static final Logger LOG = Logger.getInstance(CCNewStepAction.class);
+ public static final String NEW_STEP = "New Step";
+
+ public CCNewStepAction() {
+ super(NEW_STEP);
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ DataContext dataContext = e.getDataContext();
+ VirtualFile virtualFile = CommonDataKeys.VIRTUAL_FILE.getData(dataContext);
+ Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
+ Project project = CommonDataKeys.PROJECT.getData(dataContext);
+ if (virtualFile == null || project == null || editor == null) {
+ return;
+ }
+
+ addStep(virtualFile, project);
+ }
+
+ public static void addStep(@NotNull VirtualFile virtualFile, @NotNull Project project) {
+ TaskFile taskFile = StudyUtils.getTaskFile(project, virtualFile);
+ if (taskFile == null) {
+ return;
+ }
+ VirtualFile taskDir = StudyUtils.getTaskDir(virtualFile);
+ if (taskDir == null) {
+ return;
+ }
+ Task task = taskFile.getTask();
+ List<Step> steps = task.getAdditionalSteps();
+ createTestsForNewStep(project, task);
+ createTaskDescriptionFile(project, taskDir, steps.size());
+ Step step = new Step(task);
+ steps.add(step);
+ StudyStepManager.switchStep(project, task, steps.size() - 1);
+ }
+
+ private static void createTestsForNewStep(Project project, Task task) {
+ Course course = StudyTaskManager.getInstance(project).getCourse();
+ if (course == null) {
+ return;
+ }
+ CCLanguageManager manager = CCUtils.getStudyLanguageManager(course);
+ if (manager == null) {
+ return;
+ }
+ manager.createTestsForNewStep(project, task);
+ }
+
+ private static void createTaskDescriptionFile(Project project, VirtualFile taskDir, int index) {
+ String taskDescriptionFileName = StudyUtils.getTaskDescriptionFileName(CCSettings.getInstance().useHtmlAsDefaultTaskFormat());
+ FileTemplate taskTextTemplate = FileTemplateManager.getInstance(project)
+ .getInternalTemplate(taskDescriptionFileName);
+ PsiDirectory taskPsiDir = PsiManager.getInstance(project).findDirectory(taskDir);
+ if (taskTextTemplate != null && taskPsiDir != null) {
+ String nextTaskTextName = FileUtil.getNameWithoutExtension(taskDescriptionFileName) +
+ EduNames.STEP_MARKER +
+ index + "." +
+ FileUtilRt.getExtension(taskDescriptionFileName);
+ try {
+ FileTemplateUtil.createFromTemplate(taskTextTemplate, nextTaskTextName, null, taskPsiDir);
+ }
+ catch (Exception e) {
+ LOG.error(e);
+ }
+ }
+ }
+
+ @Override
+ public void update(AnActionEvent e) {
+ DataContext dataContext = e.getDataContext();
+ Presentation presentation = e.getPresentation();
+ presentation.setEnabledAndVisible(false);
+ VirtualFile virtualFile = CommonDataKeys.VIRTUAL_FILE.getData(dataContext);
+ Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
+ Project project = CommonDataKeys.PROJECT.getData(dataContext);
+ if (virtualFile == null || project == null || editor == null) {
+ return;
+ }
+ if (CCUtils.isCourseCreator(project) && StudyUtils.getTaskForFile(project, virtualFile) != null) {
+ presentation.setEnabledAndVisible(true);
+ }
+ }
+}
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.diff.impl.util.LabeledEditor;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.ui.FrameWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.Pair;
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.coursecreator.CCUtils;
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.Course;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
-import com.jetbrains.edu.coursecreator.CCUtils;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.Calendar;
public class CCShowPreview extends DumbAwareAction {
- private static final Logger LOG = Logger.getInstance(CCShowPreview.class.getName());
+ public static final String SHOW_PREVIEW = "Show Preview";
public CCShowPreview() {
- super("Show Preview", "Show Preview", null);
+ super(SHOW_PREVIEW, SHOW_PREVIEW, null);
}
@Override
Messages.showInfoMessage("Preview is available for task files with answer placeholders only", "No Preview for This File");
return;
}
- final TaskFile taskFileCopy = new TaskFile();
- TaskFile.copy(taskFile, taskFileCopy);
-
VirtualFile generatedFilesFolder = CCUtils.getGeneratedFilesFolder(project, module);
return;
}
- ApplicationManager.getApplication().runWriteAction(() -> EduUtils.createStudentFileFromAnswer(project, generatedFilesFolder, taskDir.getVirtualFile(), virtualFile.getName(), taskFileCopy));
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ Pair<VirtualFile, TaskFile> pair =
+ EduUtils.createStudentFile(this, project, virtualFile, taskFile.getTask().getActiveStepIndex(), generatedFilesFolder, null);
+ if (pair != null) {
+ showPreviewDialog(project, pair.getFirst(), pair.getSecond());
+ }
+ }
+ });
+ }
- VirtualFile userFile = generatedFilesFolder.findChild(virtualFile.getName());
- if (userFile == null) {
- LOG.info("Generated file " + virtualFile.getName() + "was not found");
- return;
- }
+ private static void showPreviewDialog(@NotNull Project project, @NotNull VirtualFile userFile, @NotNull TaskFile taskFile) {
final FrameWrapper showPreviewFrame = new FrameWrapper(project);
- showPreviewFrame.setTitle(virtualFile.getName());
+ showPreviewFrame.setTitle(userFile.getName());
LabeledEditor labeledEditor = new LabeledEditor(null);
final EditorFactory factory = EditorFactory.getInstance();
Document document = FileDocumentManager.getInstance().getDocument(userFile);
factory.releaseEditor(createdEditor);
}
});
- for (AnswerPlaceholder answerPlaceholder : taskFileCopy.getAnswerPlaceholders()) {
+ for (AnswerPlaceholder answerPlaceholder : taskFile.getAnswerPlaceholders()) {
+ answerPlaceholder.setUseLength(true);
EduAnswerPlaceholderPainter.drawAnswerPlaceholder(createdEditor, answerPlaceholder, JBColor.BLUE);
}
JPanel header = new JPanel();
* represents a file which is invisible for student in student mode
*/
public class CCStudentInvisibleFileNode extends PsiFileNode{
+ private final String myName;
+
public CCStudentInvisibleFileNode(Project project,
PsiFile value,
ViewSettings viewSettings) {
super(project, value, viewSettings);
+ myName = value.getName();
}
+ public CCStudentInvisibleFileNode(Project project,
+ PsiFile value,
+ ViewSettings viewSettings,
+ String name) {
+ super(project, value, viewSettings);
+ myName = name;
+ }
+
+
@Override
protected void updateImpl(PresentationData data) {
super.updateImpl(data);
- String text = data.getPresentableText();
data.clearText();
- data.addText(text, SimpleTextAttributes.GRAY_ATTRIBUTES);
+ data.addText(myName, SimpleTextAttributes.GRAY_ATTRIBUTES);
}
}
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.PsiFile;
import com.jetbrains.edu.coursecreator.CCUtils;
+import com.jetbrains.edu.learning.StudyLanguageManager;
+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.Task;
import com.jetbrains.edu.learning.projectView.StudyTreeStructureProvider;
import org.jetbrains.annotations.NotNull;
for (AbstractTreeNode node : children) {
Project project = node.getProject();
- if (node.getValue() instanceof PsiDirectory) {
- String name = ((PsiDirectory)node.getValue()).getName();
- if ("zip".equals(FileUtilRt.getExtension(name))) {
- modifiedChildren.add(node);
- continue;
- }
+ if (project == null) {
+ continue;
+ }
+ if (node.getValue() instanceof PsiDirectory) {
+ String name = ((PsiDirectory)node.getValue()).getName();
+ if ("zip".equals(FileUtilRt.getExtension(name))) {
+ modifiedChildren.add(node);
+ continue;
}
- if (node instanceof PsiFileNode) {
- PsiFileNode fileNode = (PsiFileNode)node;
- VirtualFile virtualFile = fileNode.getVirtualFile();
- if (virtualFile == null) {
- continue;
- }
- if (project != null && StudyUtils.getTaskFile(project, virtualFile) == null
- && !StudyUtils.isTaskDescriptionFile(virtualFile.getName())) {
- modifiedChildren.add(new CCStudentInvisibleFileNode(project, ((PsiFileNode)node).getValue(), settings));
- }
+ }
+ if (node instanceof PsiFileNode) {
+ PsiFileNode fileNode = (PsiFileNode)node;
+ VirtualFile virtualFile = fileNode.getVirtualFile();
+ if (virtualFile == null) {
+ continue;
}
+ if (StudyUtils.getTaskFile(project, virtualFile) != null || StudyUtils.isTaskDescriptionFile(virtualFile.getName())) {
+ continue;
+ }
+ PsiFile psiFile = ((PsiFileNode)node).getValue();
+ if (!handleTests(project, virtualFile, psiFile, modifiedChildren, settings)) {
+ modifiedChildren.add(new CCStudentInvisibleFileNode(project, psiFile, settings));
+ }
+ }
}
return modifiedChildren;
}
+ private static boolean handleTests(Project project,
+ VirtualFile virtualFile,
+ PsiFile psiFile,
+ Collection<AbstractTreeNode> modifiedChildren,
+ ViewSettings settings) {
+ Course course = StudyTaskManager.getInstance(project).getCourse();
+ if (course == null) {
+ return false;
+ }
+ if (!CCUtils.isTestsFile(project, virtualFile)) {
+ return false;
+ }
+ VirtualFile taskDir = StudyUtils.getTaskDir(virtualFile);
+ if (taskDir == null) {
+ return false;
+ }
+ Task task = StudyUtils.getTask(project, taskDir);
+ if (task == null) {
+ return false;
+ }
+ if (isCurrentStep(task, virtualFile)) {
+ StudyLanguageManager manager = StudyUtils.getLanguageManager(course);
+ String testsFileName = manager != null ? manager.getTestFileName() : psiFile.getName();
+ modifiedChildren.add(new CCStudentInvisibleFileNode(project, psiFile, settings,
+ testsFileName));
+ }
+ return true;
+ }
+
+ private static boolean isCurrentStep(Task task, VirtualFile virtualFile) {
+ if (task.getAdditionalSteps().isEmpty()) {
+ return true;
+ }
+
+ boolean isStepTestFile = virtualFile.getName().contains(EduNames.STEP_MARKER);
+ if (task.getActiveStepIndex() == -1) {
+ return !isStepTestFile;
+ }
+ if (!isStepTestFile) {
+ return false;
+ }
+ String nameWithoutExtension = virtualFile.getNameWithoutExtension();
+ int stepMarkerStart = nameWithoutExtension.indexOf(EduNames.STEP_MARKER);
+ int stepIndex = Integer.valueOf(nameWithoutExtension.substring(EduNames.STEP_MARKER.length() + stepMarkerStart));
+ return stepIndex == task.getActiveStepIndex();
+ }
+
protected boolean needModify(@NotNull final AbstractTreeNode parent) {
Project project = parent.getProject();
if (project == null) {
taskFile.setUserCreated(true);
final String name = createdFile.getName();
taskFile.name = name;
+ //TODO: put to other steps as well
task.getTaskFiles().put(name, taskFile);
}
}
--- /dev/null
+package com.jetbrains.edu.learning;
+
+import com.intellij.ide.projectView.ProjectView;
+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.intellij.ui.EditorNotifications;
+import com.jetbrains.edu.learning.checker.StudyCheckUtils;
+import com.jetbrains.edu.learning.core.EduNames;
+import com.jetbrains.edu.learning.courseFormat.Task;
+import com.jetbrains.edu.learning.courseFormat.TaskFile;
+import com.jetbrains.edu.learning.ui.StudyToolWindow;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Map;
+
+public class StudyStepManager {
+
+ private static final Logger LOG = Logger.getInstance(StudyStepManager.class);
+
+ public static void switchStep(@NotNull Project project, @NotNull Task task, int step) {
+ if (task.getActiveStepIndex() == step) {
+ return;
+ }
+ task.setActiveStepIndex(step);
+
+ VirtualFile taskDir = task.getTaskDir(project);
+ if (taskDir == null) {
+ return;
+ }
+ VirtualFile srcDir = taskDir.findChild(EduNames.SRC);
+ if (srcDir != null) {
+ taskDir = srcDir;
+ }
+ for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
+ String name = entry.getKey();
+ VirtualFile virtualFile = taskDir.findChild(name);
+ if (virtualFile == null) {
+ continue;
+ }
+ EditorNotifications.getInstance(project).updateNotifications(virtualFile);
+ }
+ update(project, task, taskDir);
+
+ }
+
+ private static void update(@NotNull Project project, @NotNull Task task, VirtualFile taskDir) {
+ StudyCheckUtils.drawAllPlaceholders(project, task, taskDir);
+ ProjectView.getInstance(project).refresh();
+ StudyToolWindow toolWindow = StudyUtils.getStudyToolWindow(project);
+ if (toolWindow != null) {
+ String text = StudyUtils.getTaskTextFromTask(taskDir, task);
+ if (text == null) {
+ toolWindow.setEmptyText(project);
+ return;
+ }
+ toolWindow.setTaskText(text, taskDir, project);
+ }
+ }
+
+ public static void deleteStep(@NotNull Project project, @NotNull Task task, int index) {
+ //TODO: delete not only the last step
+ VirtualFile taskDir = task.getTaskDir(project);
+ if (taskDir == null) {
+ return;
+ }
+
+ ArrayList<VirtualFile> filesToDelete = new ArrayList<>();
+ for (VirtualFile file : taskDir.getChildren()) {
+ String stepSuffix = EduNames.STEP_MARKER + index;
+ if (file.getName().contains(stepSuffix)) {
+ filesToDelete.add(file);
+ }
+ }
+ for (VirtualFile file : filesToDelete) {
+ ApplicationManager.getApplication().runWriteAction(() -> {
+ try {
+ file.delete(StudyStepManager.class);
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ });
+ }
+
+ task.getAdditionalSteps().remove(index);
+ if (task.getActiveStepIndex() == index) {
+ switchStep(project, task, index - 1);
+ }
+ }
+}
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import javax.swing.*;
import java.awt.*;
import java.io.*;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
+import java.util.*;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
}
public static void drawAllWindows(Editor editor, TaskFile taskFile) {
- editor.getMarkupModel().removeAllHighlighters();
+ drawAllWindows(editor, taskFile, true);
+ }
+
+ public static void drawAllWindows(Editor editor, TaskFile taskFile, boolean removePrevHighlighters) {
+ if (removePrevHighlighters) {
+ editor.getMarkupModel().removeAllHighlighters();
+ }
final Project project = editor.getProject();
if (project == null) return;
final StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
if (task == null) {
return null;
}
- String text = task.getText();
- if (text != null) {
+
+ String text = getFromTask(task);
+ if (text != null && !text.isEmpty()) {
return text;
}
if (taskDirectory != null) {
+ String fileNameWithoutExtension = FileUtil.getNameWithoutExtension(EduNames.TASK_HTML);
+ int activeStepIndex = task.getActiveStepIndex();
+ if (activeStepIndex != -1) {
+ fileNameWithoutExtension += EduNames.STEP_MARKER + activeStepIndex;
+ }
final String prefix = String.format(ourPrefix, EditorColorsManager.getInstance().getGlobalScheme().getEditorFontSize());
- final String taskTextFileHtml = getTaskTextFromTaskName(taskDirectory, EduNames.TASK_HTML);
+ final String taskTextFileHtml = getTaskTextFromTaskName(taskDirectory, fileNameWithoutExtension + "." + FileUtilRt.getExtension(EduNames.TASK_HTML));
if (taskTextFileHtml != null) return prefix + taskTextFileHtml + ourPostfix;
- final String taskTextFileMd = getTaskTextFromTaskName(taskDirectory, EduNames.TASK_MD);
+ final String taskTextFileMd = getTaskTextFromTaskName(taskDirectory, fileNameWithoutExtension + "." + FileUtilRt.getExtension(EduNames.TASK_MD));
if (taskTextFileMd != null) return prefix + convertToHtml(taskTextFileMd) + ourPostfix;
}
return null;
}
+ @Nullable
+ private static String getFromTask(Task task) {
+ int index = task.getActiveStepIndex();
+ if (index == -1) {
+ return task.getText();
+ }
+ return task.getAdditionalSteps().get(index).getText();
+ }
+
@Nullable
private static String getTaskTextFromTaskName(@NotNull VirtualFile taskDirectory, @NotNull String taskTextFilename) {
VirtualFile taskTextFile = taskDirectory.findChild(taskTextFilename);
return null;
}
+ @Nullable
+ public static Task getTaskForFile(@NotNull Project project, @NotNull VirtualFile taskFile) {
+ VirtualFile taskDir = getTaskDir(taskFile);
+ if (taskDir == null) {
+ return null;
+ }
+ return getTask(project, taskDir);
+ }
+
// supposed to be called under progress
@Nullable
public static <T> T execCancelable(@NotNull final Callable<T> callable) {
}
public static boolean isTaskDescriptionFile(@NotNull final String fileName) {
- return EduNames.TASK_HTML.equals(fileName) || EduNames.TASK_MD.equals(fileName);
+ if (EduNames.TASK_HTML.equals(fileName) || EduNames.TASK_MD.equals(fileName)) {
+ return true;
+ }
+ return fileName.contains(EduNames.TASK) && fileName.contains(EduNames.STEP_MARKER);
}
@Nullable
- public static VirtualFile findTaskDescriptionVirtualFile(@NotNull final VirtualFile parent) {
- return ObjectUtils.chooseNotNull(parent.findChild(EduNames.TASK_HTML), parent.findChild(EduNames.TASK_MD));
+ public static VirtualFile findTaskDescriptionVirtualFile(@NotNull Project project, @NotNull VirtualFile taskDir) {
+ Task task = getTask(project, taskDir.getName().contains(EduNames.TASK) ? taskDir: taskDir.getParent());
+ if (task == null) {
+ return null;
+ }
+ String fileNameWithoutExtension = FileUtil.getNameWithoutExtension(EduNames.TASK_HTML);
+ int activeStepIndex = task.getActiveStepIndex();
+ if (activeStepIndex != -1) {
+ fileNameWithoutExtension += EduNames.STEP_MARKER + activeStepIndex;
+ }
+ return ObjectUtils.chooseNotNull(taskDir.findChild(fileNameWithoutExtension + "." + FileUtilRt.getExtension(EduNames.TASK_HTML)),
+ taskDir.findChild(fileNameWithoutExtension + "." + FileUtilRt.getExtension(EduNames.TASK_MD)));
}
@NotNull
JBPopupFactory.getInstance().createHtmlTextBalloonBuilder("Couldn't post your reaction", MessageType.ERROR, null).createBalloon();
showCheckPopUp(project, balloon);
}
+
+ public static void drawPlaceholdersFromOtherSteps(Editor editor, TaskFile taskFile, Task task) {
+ for (int i = -1; i < task.getAdditionalSteps().size(); i++) {
+ if (i == task.getActiveStepIndex()) {
+ continue;
+ }
+ List<AnswerPlaceholder> placeholders = getPlaceholders(i, task, taskFile);
+ for (AnswerPlaceholder placeholder : placeholders) {
+ EduAnswerPlaceholderPainter.drawAnswerPlaceholderFromPrevStep(editor, placeholder);
+ }
+ }
+ }
+
+ private static List<AnswerPlaceholder> getPlaceholders(int index, Task task, TaskFile taskFile) {
+ TaskFile prevTaskFile = index == -1 ? taskFile.getTask().getTaskFile(taskFile.name)
+ : task.getAdditionalSteps().get(index).getTaskFiles().get(taskFile.name);
+ if (prevTaskFile == null) {
+ return Collections.emptyList();
+ }
+ return prevTaskFile.getAnswerPlaceholders();
+ }
+
+
+ public static Map<String, TaskFile> getTaskFiles(@NotNull Task task) {
+ int activeStepIndex = task.getActiveStepIndex();
+ if (activeStepIndex == -1) {
+ return task.getTaskFiles();
+ }
+ return task.getAdditionalSteps().get(activeStepIndex).getTaskFiles();
+ }
}
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.Task;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
import com.jetbrains.edu.learning.StudyState;
import com.jetbrains.edu.learning.StudyUtils;
if (!studyState.isValid()) {
return;
}
- final TaskFile taskFile = studyState.getTaskFile();
+ TaskFile taskFile = studyState.getTaskFile();
+ Task task = taskFile.getTask();
+ int stepIndex = task.getActiveStepIndex();
+ if (stepIndex != -1) {
+ TaskFile stepTaskFile = task.getAdditionalSteps().get(stepIndex).getTaskFiles().get(taskFile.name);
+ if (stepTaskFile == null) {
+ return;
+ }
+ taskFile = stepTaskFile;
+ }
final Document document = studyState.getEditor().getDocument();
+ final TaskFile realTaskFile = taskFile;
CommandProcessor.getInstance().runUndoTransparentAction(() -> ApplicationManager.getApplication().runWriteAction(() -> {
- for (AnswerPlaceholder placeholder : taskFile.getAnswerPlaceholders()) {
+ for (AnswerPlaceholder placeholder : realTaskFile.getAnswerPlaceholders()) {
String answer = placeholder.getPossibleAnswer();
if (answer == null) {
continue;
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.*;
import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter;
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.StudyStatus;
-import com.jetbrains.edu.learning.courseFormat.TaskFile;
+import com.jetbrains.edu.learning.courseFormat.*;
import com.jetbrains.edu.learning.editor.StudyEditor;
import com.jetbrains.edu.learning.navigation.StudyNavigator;
import icons.InteractiveLearningIcons;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
+import java.util.List;
public class StudyRefreshTaskFileAction extends StudyActionWithShortcut {
public static final String ACTION_ID = "RefreshTaskAction";
@NotNull final Project project,
TaskFile taskFile,
String name) {
+ Task task = taskFile.getTask();
+ List<Step> additionalSteps = task.getAdditionalSteps();
+ if (!additionalSteps.isEmpty() && task.getActiveStepIndex() != -1) {
+ StudyStepManager.switchStep(project, task, -1);
+ }
if (!resetDocument(document, taskFile, name)) {
return false;
}
- taskFile.getTask().setStatus(StudyStatus.Unchecked);
- resetAnswerPlaceholders(taskFile, project);
+ task.setStatus(StudyStatus.Unchecked);
+ resetAnswerPlaceholders(taskFile);
ProjectView.getInstance(project).refresh();
StudyUtils.updateToolWindows(project);
return true;
Disposer.register(project, balloon);
}
- private static void resetAnswerPlaceholders(TaskFile selectedTaskFile, Project project) {
- final StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
+ private static void resetAnswerPlaceholders(TaskFile selectedTaskFile) {
for (AnswerPlaceholder answerPlaceholder : selectedTaskFile.getAnswerPlaceholders()) {
answerPlaceholder.reset();
- taskManager.setStatus(answerPlaceholder, StudyStatus.Unchecked);
+ answerPlaceholder.setStatus(StudyStatus.Unchecked);
+ }
+ List<Step> additionalSteps = selectedTaskFile.getTask().getAdditionalSteps();
+ if (!additionalSteps.isEmpty()) {
+ for (Step step : additionalSteps) {
+ TaskFile stepTaskFile = step.getTaskFiles().get(selectedTaskFile.name);
+ if (stepTaskFile == null) {
+ continue;
+ }
+ for (AnswerPlaceholder placeholder : stepTaskFile.getAnswerPlaceholders()) {
+ placeholder.reset();
+ placeholder.setStatus(StudyStatus.Unchecked);
+ }
+ }
}
}
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
-import com.jetbrains.edu.learning.StudyPluginConfigurator;
-import com.jetbrains.edu.learning.StudyState;
-import com.jetbrains.edu.learning.StudyTaskManager;
-import com.jetbrains.edu.learning.StudyUtils;
+import com.jetbrains.edu.learning.*;
import com.jetbrains.edu.learning.actions.StudyAfterCheckAction;
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.StudyStatus;
-import com.jetbrains.edu.learning.courseFormat.Task;
+import com.jetbrains.edu.learning.courseFormat.*;
import com.jetbrains.edu.learning.stepic.EduAdaptiveStepicConnector;
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
import com.jetbrains.edu.learning.stepic.StepicUser;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.util.List;
+
public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroundable {
private static final Logger LOG = Logger.getInstance(StudyCheckTask.class);
StudyCheckUtils.drawAllPlaceholders(myProject, myTask, myTaskDir);
ProjectView.getInstance(myProject).refresh();
clearState();
+
+ List<Step> additionalSteps = myTask.getAdditionalSteps();
+ if (additionalSteps.isEmpty() || myTask.getActiveStepIndex() == additionalSteps.size() - 1) {
+ return;
+ }
+ for (TaskFile taskFile : myTask.getTaskFiles().values()) {
+ for (AnswerPlaceholder placeholder : taskFile.getAnswerPlaceholders()) {
+ if (placeholder.getStatus() != StudyStatus.Solved) {
+ return;
+ }
+ }
+ }
+ ApplicationManager.getApplication().invokeLater(() -> StudyStepManager.switchStep(myProject, myTask, myTask.getActiveStepIndex() + 1));
}
protected void clearState() {
private void checkForEduCourse(@NotNull ProgressIndicator indicator) {
final StudyTestsOutputParser.TestsOutput testsOutput = getTestOutput(indicator);
-
+
if (testsOutput != null) {
if (testsOutput.isSuccess()) {
onTaskSolved(testsOutput.getMessage());
protected void onTaskSolved(String message) {
final Course course = StudyTaskManager.getInstance(myProject).getCourse();
- myTask.setStatus(StudyStatus.Solved);
+ List<Step> additionalSteps = myTask.getAdditionalSteps();
+ boolean noMoreSteps = additionalSteps.isEmpty() || myTask.getActiveStepIndex() == additionalSteps.size() - 1;
+ if (noMoreSteps) {
+ myTask.setStatus(StudyStatus.Solved);
+ }
+ else {
+ for (TaskFile taskFile : myTask.getTaskFiles().values()) {
+ for (AnswerPlaceholder placeholder : taskFile.getAnswerPlaceholders()) {
+ placeholder.setStatus(StudyStatus.Solved);
+ }
+ }
+ }
if (course != null) {
if (course.isAdaptive()) {
ApplicationManager.getApplication().invokeLater(
});
}
else {
+ String successMessage = message;
+ if (!noMoreSteps) {
+ int stepNum = additionalSteps.size() + 1;
+ int currentStep = myTask.getActiveStepIndex() + 2;
+ successMessage = "Step " + currentStep + " from " + stepNum + " solved";
+ }
+ String finalSuccessMessage = successMessage;
ApplicationManager.getApplication()
- .invokeLater(() -> StudyCheckUtils.showTestResultPopUp(message, MessageType.INFO.getPopupBackground(), myProject));
+ .invokeLater(() -> StudyCheckUtils.showTestResultPopUp(finalSuccessMessage, MessageType.INFO.getPopupBackground(), myProject));
}
}
}
public static void drawAllPlaceholders(@NotNull final Project project, @NotNull final Task task, @NotNull final VirtualFile taskDir) {
for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
String name = entry.getKey();
- TaskFile taskFile = entry.getValue();
VirtualFile virtualFile = taskDir.findChild(name);
if (virtualFile == null) {
continue;
}
- FileEditor fileEditor = FileEditorManager.getInstance(project).getSelectedEditor(virtualFile);
- if (fileEditor instanceof StudyEditor) {
- StudyEditor studyEditor = (StudyEditor)fileEditor;
- StudyUtils.drawAllWindows(studyEditor.getEditor(), taskFile);
+ if (FileEditorManager.getInstance(project).isFileOpen(virtualFile)) {
+ FileEditor fileEditor = FileEditorManager.getInstance(project).getSelectedEditor(virtualFile);
+ if (fileEditor instanceof StudyEditor) {
+ Editor editor = ((StudyEditor)fileEditor).getEditor();
+ editor.getMarkupModel().removeAllHighlighters();
+ for (TaskFile taskFile : task.getTaskFiles().values()) {
+ StudyUtils.drawPlaceholdersFromOtherSteps(editor, taskFile, task);
+ }
+ TaskFile currentTaskFile = StudyUtils.getTaskFile(project, virtualFile);
+ if (currentTaskFile != null) {
+ StudyUtils.drawAllWindows(editor, currentTaskFile, false);
+ }
+ }
}
}
}
VirtualFile fileToNavigate = studyState.getVirtualFile();
final StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
if (!taskManager.hasFailedAnswerPlaceholders(selectedTaskFile)) {
- for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
+ for (Map.Entry<String, TaskFile> entry : StudyUtils.getTaskFiles(task).entrySet()) {
String name = entry.getKey();
TaskFile taskFile = entry.getValue();
if (taskManager.hasFailedAnswerPlaceholders(taskFile)) {
public static void flushWindows(@NotNull final Task task, @NotNull final VirtualFile taskDir) {
- for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
+ for (Map.Entry<String, TaskFile> entry : StudyUtils.getTaskFiles(task).entrySet()) {
String name = entry.getKey();
TaskFile taskFile = entry.getValue();
VirtualFile virtualFile = taskDir.findChild(name);
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
+import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.editor.impl.DocumentImpl;
import com.intellij.openapi.editor.markup.*;
import com.intellij.openapi.project.Project;
import com.intellij.ui.JBColor;
+import com.intellij.ui.SimpleTextAttributes;
import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
import org.jetbrains.annotations.NotNull;
EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
final TextAttributes textAttributes = new TextAttributes(scheme.getDefaultForeground(), scheme.getDefaultBackground(), null,
EffectType.BOXED, Font.PLAIN);
+ textAttributes.setEffectColor(color);
+ drawAnswerPlaceholder(editor, placeholder, textAttributes, PLACEHOLDERS_LAYER);
+ }
+
+ public static void drawAnswerPlaceholder(@NotNull Editor editor,
+ @NotNull AnswerPlaceholder placeholder,
+ TextAttributes textAttributes,
+ int placeholdersLayer) {
final Project project = editor.getProject();
assert project != null;
final int startOffset = placeholder.getOffset();
}
final int length = placeholder.getRealLength();
final int endOffset = startOffset + length;
- textAttributes.setEffectColor(color);
RangeHighlighter
- highlighter = editor.getMarkupModel().addRangeHighlighter(startOffset, endOffset, PLACEHOLDERS_LAYER,
+ highlighter = editor.getMarkupModel().addRangeHighlighter(startOffset, endOffset, placeholdersLayer,
textAttributes, HighlighterTargetArea.EXACT_RANGE);
highlighter.setGreedyToLeft(true);
highlighter.setGreedyToRight(true);
}
+ public static void drawAnswerPlaceholderFromPrevStep(@NotNull Editor editor, @NotNull AnswerPlaceholder placeholder) {
+ EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
+ Color color = scheme.getColor(EditorColors.TEARLINE_COLOR);
+ SimpleTextAttributes attributes = SimpleTextAttributes.GRAY_ATTRIBUTES;
+ final TextAttributes textAttributes = new TextAttributes(attributes.getFgColor(), color, null,
+ null, attributes.getFontStyle());
+
+ drawAnswerPlaceholder(editor, placeholder, textAttributes, HighlighterLayer.LAST);
+ }
+
public static void createGuardedBlock(Editor editor, List<RangeMarker> blocks, int start, int end) {
RangeHighlighter rh = editor.getMarkupModel()
.addRangeHighlighter(start, end, PLACEHOLDERS_LAYER, null, HighlighterTargetArea.EXACT_RANGE);
import com.intellij.openapi.editor.impl.event.DocumentEventImpl;
import com.intellij.openapi.util.TextRange;
import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
+import com.jetbrains.edu.learning.courseFormat.Step;
+import com.jetbrains.edu.learning.courseFormat.Task;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
import java.util.ArrayList;
myTrackLength = trackLength;
}
- //remembering old end before document change because of problems
- // with fragments containing "\n"
@Override
public void beforeDocumentChange(DocumentEvent e) {
if (!myTaskFile.isTrackChanges()) {
}
myTaskFile.setHighlightErrors(true);
myAnswerPlaceholders.clear();
- for (AnswerPlaceholder answerPlaceholder : myTaskFile.getAnswerPlaceholders()) {
+ for (AnswerPlaceholder answerPlaceholder : getAllPlaceholders()) {
int twStart = answerPlaceholder.getOffset();
int length = answerPlaceholder.getRealLength();
int twEnd = twStart + length;
}
}
+ private List<AnswerPlaceholder> getAllPlaceholders() {
+ Task task = myTaskFile.getTask();
+ if (task == null || task.getAdditionalSteps().isEmpty()) {
+ return myTaskFile.getAnswerPlaceholders();
+ }
+ List<AnswerPlaceholder> placeholders = new ArrayList<>();
+ String name = myTaskFile.name;
+ TaskFile initialStepTaskFile = task.getTaskFile(name);
+ if (initialStepTaskFile == null) {
+ return placeholders;
+ }
+ placeholders.addAll(initialStepTaskFile.getAnswerPlaceholders());
+ for (Step step : task.getAdditionalSteps()) {
+ TaskFile stepTaskFile = step.getTaskFiles().get(name);
+ if (stepTaskFile == null) {
+ continue;
+ }
+ placeholders.addAll(stepTaskFile.getAnswerPlaceholders());
+ }
+ return placeholders;
+ }
+
@Override
public void documentChanged(DocumentEvent e) {
if (!myTaskFile.isTrackChanges()) {
AnswerPlaceholder answerPlaceholder = answerPlaceholderWrapper.getAnswerPlaceholder();
int length = twEnd - twStart;
answerPlaceholder.setOffset(twStart);
- if (!answerPlaceholder.getUseLength()) {
- answerPlaceholder.setPossibleAnswer(document.getText(TextRange.create(twStart, twStart + length)));
- }
- else if (myTrackLength) {
- answerPlaceholder.setLength(length);
+ if (myTrackLength) {
+ if (answerPlaceholder.getUseLength()) {
+ answerPlaceholder.setLength(length);
+ } else {
+ answerPlaceholder.setPossibleAnswer(document.getText(TextRange.create(twStart, twStart + answerPlaceholder.getRealLength())));
+ }
}
}
}
public static final String ANSWER_PLACEHOLDER = "Answer Placeholder";
public static final String PLACEHOLDER = "placeholder";
public static final String SRC = "src";
+
+ public static final String STEP_MARKER = "_step";
+ public static final String STEP = "step";
private EduNames() {
}
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.psi.PsiDirectory;
+import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.courseFormat.*;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.Map;
+import java.util.*;
public class EduUtils {
private EduUtils() {
}
+
private static final Logger LOG = Logger.getInstance(EduUtils.class.getName());
public static final Comparator<StudyItem> INDEX_COMPARATOR = (o1, o2) -> o1.getIndex() - o2.getIndex();
}
try {
return Integer.parseInt(fullName.substring(logicalName.length())) - 1;
- } catch(NumberFormatException e) {
+ }
+ catch (NumberFormatException e) {
return -1;
}
}
VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
}
- public static void createStudentFileFromAnswer(@NotNull final Project project,
- @NotNull final VirtualFile userFileDir,
- @NotNull final VirtualFile answerFileDir,
- @NotNull final String taskFileName, @NotNull final TaskFile taskFile) {
- VirtualFile file = userFileDir.findChild(taskFileName);
- if (file != null) {
- try {
- file.delete(project);
- }
- catch (IOException e) {
- LOG.error(e);
- }
- }
- if (taskFile.getAnswerPlaceholders().isEmpty()) {
- //do not need to replace anything, just copy
- VirtualFile answerFile = answerFileDir.findChild(taskFileName);
- if (answerFile != null) {
- try {
- answerFile.copy(answerFileDir, userFileDir, taskFileName);
- }
- catch (IOException e) {
- LOG.error(e);
- }
- }
- return;
- }
+ public static VirtualFile copyFile(Object requestor, VirtualFile toDir, VirtualFile file) {
+ String name = file.getName();
try {
- userFileDir.createChildData(project, taskFileName);
+ VirtualFile userFile = toDir.findChild(name);
+ if (userFile != null) {
+ userFile.delete(requestor);
+ }
+ return VfsUtilCore.copyFile(requestor, file, toDir);
}
catch (IOException e) {
- LOG.error(e);
+ LOG.info("Failed to create file " + name + " in folder " + toDir.getPath(), e);
}
+ return null;
+ }
- file = userFileDir.findChild(taskFileName);
- if (file == null) {
- LOG.info("Failed to find task file " + taskFileName);
- return;
+ @Nullable
+ public static Pair<VirtualFile, TaskFile> createStudentFile(Object requestor,
+ Project project,
+ VirtualFile answerFile,
+ int stepIndex,
+ VirtualFile parentDir,
+ @Nullable Task task) {
+
+ VirtualFile studentFile = copyFile(requestor, parentDir, answerFile);
+ if (studentFile == null) {
+ return null;
+ }
+ Document studentDocument = FileDocumentManager.getInstance().getDocument(studentFile);
+ if (studentDocument == null) {
+ return null;
}
- VirtualFile answerFile = answerFileDir.findChild(taskFileName);
- if (answerFile == null) {
- return;
+ if (task == null) {
+ task = StudyUtils.getTaskForFile(project, answerFile);
+ if (task == null) {
+ return null;
+ }
+ task = task.copy();
}
- final Document answerDocument = FileDocumentManager.getInstance().getDocument(answerFile);
- if (answerDocument == null) {
- return;
+ Map<Integer, TaskFile> taskFileSteps = getTaskFileSteps(task, answerFile.getName());
+ TaskFile initialTaskFile = taskFileSteps.get(-1);
+ if (initialTaskFile == null) {
+ return null;
}
- final Document document = FileDocumentManager.getInstance().getDocument(file);
- if (document == null) return;
+ EduDocumentListener listener = new EduDocumentListener(initialTaskFile, false);
+ studentDocument.addDocumentListener(listener);
- CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction(() -> {
- document.replaceString(0, document.getTextLength(), answerDocument.getCharsSequence());
- FileDocumentManager.getInstance().saveDocument(document);
- }), "Create Student File", "Create Student File");
- createStudentDocument(project, taskFile, file, document);
+ Pair<VirtualFile, TaskFile> result = null;
+ for (Map.Entry<Integer, TaskFile> entry : taskFileSteps.entrySet()) {
+ Integer index = entry.getKey();
+ if (index < stepIndex) {
+ continue;
+ }
+ TaskFile stepTaskFile = entry.getValue();
+ if (index == stepIndex) {
+ result = Pair.createNonNull(studentFile, stepTaskFile);
+ }
+ for (AnswerPlaceholder placeholder : stepTaskFile.getAnswerPlaceholders()) {
+ replaceAnswerPlaceholder(project, studentDocument, placeholder);
+ }
+ }
+ studentDocument.removeDocumentListener(listener);
+ return result;
}
- 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();
- for (int i = taskFile.getAnswerPlaceholders().size() - 1; i >= 0; i--) {
- final AnswerPlaceholder answerPlaceholder = taskFile.getAnswerPlaceholders().get(i);
- int offset = answerPlaceholder.getOffset();
- if (offset > document.getTextLength() || offset + answerPlaceholder.getPossibleAnswerLength() > document.getTextLength()) {
- LOG.error("Wrong startOffset: " + answerPlaceholder.getOffset() + "; document: " + file.getPath());
- return;
+ public static Map<Integer, TaskFile> getTaskFileSteps(Task task, String name) {
+ Map<Integer, TaskFile> files = new HashMap<>();
+ files.put( -1, task.getTaskFile(name));
+ List<Step> additionalSteps = task.getAdditionalSteps();
+ if (!additionalSteps.isEmpty()) {
+ for (int i = 0; i < additionalSteps.size(); i++) {
+ files.put(i, additionalSteps.get(i).getTaskFiles().get(name));
}
- replaceAnswerPlaceholder(project, document, answerPlaceholder);
}
- CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction(() -> FileDocumentManager.getInstance().saveDocument(document)), "Create Student File", "Create Student File");
- document.removeDocumentListener(listener);
+ return files;
}
private static void replaceAnswerPlaceholder(@NotNull final Project project,
final String taskText = answerPlaceholder.getTaskText();
final int offset = answerPlaceholder.getOffset();
CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction(() -> {
- document.replaceString(offset, offset + answerPlaceholder.getPossibleAnswerLength(), taskText);
+ document.replaceString(offset, offset + answerPlaceholder.getRealLength(), taskText);
FileDocumentManager.getInstance().saveDocument(document);
}), "Replace Answer Placeholders", "Replace Answer Placeholders");
}
public static void deleteWindowDescriptions(@NotNull final Task task, @NotNull final VirtualFile taskDir) {
- for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
+ for (Map.Entry<String, TaskFile> entry : StudyUtils.getTaskFiles(task).entrySet()) {
String name = entry.getKey();
VirtualFile virtualFile = taskDir.findChild(name);
if (virtualFile == null) {
import com.google.gson.annotations.SerializedName;
import com.intellij.lang.Language;
import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.xmlb.XmlSerializer;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.core.EduUtils;
import com.jetbrains.edu.learning.stepic.StepicUser;
+import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
public void setCourseMode(String courseMode) {
this.courseMode = courseMode;
}
+
+ public Course copy() {
+ Element element = XmlSerializer.serialize(this);
+ Course copy = XmlSerializer.deserialize(element, Course.class);
+ if (copy == null) {
+ return null;
+ }
+ copy.initCourse(true);
+ return copy;
+ }
}
--- /dev/null
+package com.jetbrains.edu.learning.courseFormat;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+import com.intellij.util.containers.HashMap;
+
+import java.io.Serializable;
+import java.util.Map;
+
+public class Step implements Serializable{
+
+ @SerializedName("task_files")
+ @Expose
+ private Map<String, TaskFile> myTaskFiles = new HashMap<>();
+ private String myText = "";
+
+ public Step() {
+ }
+
+ public Step(Task task) {
+ for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
+ String name = entry.getKey();
+ TaskFile taskFile = new TaskFile();
+ taskFile.name = name;
+ taskFile.setTask(task);
+ myTaskFiles.put(name, taskFile);
+ }
+ }
+
+ public Map<String, TaskFile> getTaskFiles() {
+ return myTaskFiles;
+ }
+
+ public void setTaskFiles(Map<String, TaskFile> taskFiles) {
+ myTaskFiles = taskFiles;
+ }
+
+ public void init(Task task, boolean isRestarted) {
+ for (TaskFile file : myTaskFiles.values()) {
+ file.initTaskFile(task, isRestarted);
+ }
+ }
+
+ public String getText() {
+ return myText;
+ }
+
+ public void setText(String text) {
+ myText = text;
+ }
+}
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.xmlb.XmlSerializer;
import com.intellij.util.xmlb.annotations.Transient;
import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.core.EduNames;
+import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
@Transient private Lesson myLesson;
+ @Expose
+ @SerializedName("additionalSteps")
+ private List<Step> myAdditionalSteps = new ArrayList<>();
+ private int myActiveStepIndex = -1;
+
public Task() {}
public Task(@NotNull final String name) {
for (TaskFile taskFile : getTaskFiles().values()) {
taskFile.initTaskFile(this, isRestarted);
}
+ for (Step step : myAdditionalSteps) {
+ step.init(this, isRestarted);
+ }
}
public String getName() {
@Nullable
public TaskFile getFile(@NotNull final String fileName) {
- return taskFiles.get(fileName);
+ if (myActiveStepIndex == -1) {
+ return taskFiles.get(fileName);
+ }
+ Step step = myAdditionalSteps.get(myActiveStepIndex);
+ return step.getTaskFiles().get(fileName);
}
@Transient
if (!StringUtil.isEmptyOrSpaces(text)) return text;
final VirtualFile taskDir = getTaskDir(project);
if (taskDir != null) {
- final VirtualFile file = StudyUtils.findTaskDescriptionVirtualFile(taskDir);
+ final VirtualFile file = StudyUtils.findTaskDescriptionVirtualFile(project, taskDir);
if (file == null) return "";
final Document document = FileDocumentManager.getInstance().getDocument(file);
if (document != null) {
public void setStatus(StudyStatus status) {
myStatus = status;
- for (TaskFile taskFile : taskFiles.values()) {
+ for (TaskFile taskFile : StudyUtils.getTaskFiles(this).values()) {
for (AnswerPlaceholder placeholder : taskFile.getAnswerPlaceholders()) {
placeholder.setStatus(status);
}
}
}
+
+ public List<Step> getAdditionalSteps() {
+ return myAdditionalSteps;
+ }
+
+ public void setAdditionalSteps(List<Step> additionalSteps) {
+ myAdditionalSteps = additionalSteps;
+ }
+
+ public int getActiveStepIndex() {
+ return myActiveStepIndex;
+ }
+
+ public void setActiveStepIndex(int activeStepIndex) {
+ myActiveStepIndex = activeStepIndex;
+ }
+
+ public Task copy() {
+ Element element = XmlSerializer.serialize(this);
+ Task copy = XmlSerializer.deserialize(element, Task.class);
+ if (copy == null) {
+ return null;
+ }
+ copy.initTask(null, true);
+ return copy;
+ }
}
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.Task;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
import com.jetbrains.edu.learning.navigation.StudyNavigator;
import com.jetbrains.edu.learning.ui.StudyToolWindowFactory;
editor.addEditorMouseListener(new WindowSelectionListener(taskFile));
}
}
+ Task task = taskFile.getTask();
+ if (!task.getAdditionalSteps().isEmpty()) {
+ StudyUtils.drawPlaceholdersFromOtherSteps(editor, taskFile, task);
+ }
}
}
}
import java.util.Map;
public class StudyDirectoryNode extends PsiDirectoryNode {
+ public static final JBColor LIGHT_GREEN = new JBColor(new Color(0, 134, 0), new Color(98, 150, 85));
protected final PsiDirectory myValue;
protected final Project myProject;
break;
}
case Solved: {
- updatePresentation(data, additionalName, new JBColor(new Color(0, 134, 0), new Color(98, 150, 85)), InteractiveLearningIcons.LessonCompl);
+ updatePresentation(data, additionalName, LIGHT_GREEN, InteractiveLearningIcons.LessonCompl);
break;
}
case Failed: {
}
}
- protected void setStudyAttributes(Task task, PresentationData data, String additionalName) {
+ protected void setStudyAttributes(Task task, PresentationData data, String name) {
StudyStatus taskStatus = task.getStatus();
+ String additionalInfo = task.getAdditionalSteps().isEmpty() ? null : getStepInfo(task);
switch (taskStatus) {
case Unchecked: {
- updatePresentation(data, additionalName, JBColor.BLACK, InteractiveLearningIcons.Task);
+ updatePresentation(data, name, JBColor.BLACK, InteractiveLearningIcons.Task, additionalInfo);
break;
}
case Solved: {
- updatePresentation(data, additionalName, new JBColor(new Color(0, 134, 0), new Color(98, 150, 85)),
- InteractiveLearningIcons.TaskCompl);
+ updatePresentation(data, name, LIGHT_GREEN, InteractiveLearningIcons.TaskCompl, additionalInfo);
break;
}
case Failed: {
- updatePresentation(data, additionalName, JBColor.RED, InteractiveLearningIcons.TaskProbl);
+ updatePresentation(data, name, JBColor.RED, InteractiveLearningIcons.TaskProbl, additionalInfo);
}
}
}
- protected static void updatePresentation(PresentationData data, String additionalName, JBColor color, Icon icon) {
+ private static String getStepInfo(Task task) {
+ int index = task.getActiveStepIndex() + 2;
+ int number = task.getAdditionalSteps().size() + 1;
+ return "step " + index + "/" + number;
+ }
+
+ protected static void updatePresentation(PresentationData data, String name, JBColor color, Icon icon) {
+ updatePresentation(data, name, color, icon, null);
+ }
+
+ protected static void updatePresentation(PresentationData data, String name, JBColor color, Icon icon, String additionalInfo) {
data.clearText();
- data.addText(additionalName, new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, color));
+ data.addText(name, new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, color));
+ if (additionalInfo != null) {
+ data.addText(" (" + additionalInfo + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES);
+ }
data.setIcon(icon);
}
FileEditorManager.getInstance(myProject).closeFile(openFile);
}
VirtualFile child = null;
- Map<String, TaskFile> taskFiles = task.getTaskFiles();
+ Map<String, TaskFile> taskFiles = StudyUtils.getTaskFiles(task);
for (Map.Entry<String, TaskFile> entry: taskFiles.entrySet()) {
VirtualFile file = taskDir.findChild(entry.getKey());
if (file != null) {
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.edu.learning.core.EduNames;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
+import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
source.files = new ArrayList<TaskFile>();
source.title = task.getName();
for (final Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
- final TaskFile taskFile = new TaskFile();
- TaskFile.copy(entry.getValue(), taskFile);
ApplicationManager.getApplication().runWriteAction(() -> {
final VirtualFile taskDir = task.getTaskDir(project);
assert taskDir != null;
VirtualFile ideaDir = project.getBaseDir().findChild(".idea");
assert ideaDir != null;
- EduUtils.createStudentFileFromAnswer(project, ideaDir, taskDir, entry.getKey(), taskFile);
- });
- taskFile.name = entry.getKey();
-
- VirtualFile ideaDir = project.getBaseDir().findChild(".idea");
- if (ideaDir == null) return null;
- final VirtualFile file = ideaDir.findChild(taskFile.name);
- try {
- if (file != null) {
- if (EduUtils.isImage(taskFile.name)) {
- taskFile.text = Base64.encodeBase64URLSafeString(FileUtil.loadBytes(file.getInputStream()));
- }
- else {
- taskFile.text = FileUtil.loadTextAndClose(file.getInputStream());
- }
+ String name = entry.getKey();
+ VirtualFile answerFile = taskDir.findChild(name);
+ Pair<VirtualFile, TaskFile> pair = EduUtils.createStudentFile(StepicWrappers.class, project, answerFile, -1, ideaDir, null);
+ if (pair == null) {
+ return;
}
- }
- catch (IOException e) {
- LOG.error("Can't find file " + file.getPath());
- }
-
- source.files.add(taskFile);
+ VirtualFile virtualFile = pair.getFirst();
+ TaskFile taskFile = pair.getSecond();
+ try {
+ InputStream stream = virtualFile.getInputStream();
+ taskFile.text =
+ EduUtils.isImage(name) ? Base64.encodeBase64URLSafeString(FileUtil.loadBytes(stream)) : FileUtil.loadTextAndClose(stream);
+ }
+ catch (IOException e) {
+ LOG.error("Can't find file " + virtualFile.getPath());
+ }
+ source.files.add(taskFile);
+ });
}
return source;
}
static class AssignmentsWrapper {
List<Assignment> assignments;
}
-
+
static class Assignment {
int id;
int step;
}
-
+
static class ViewsWrapper {
View view;
this.view = new View(assignment, step);
}
}
-
+
static class View {
int assignment;
int step;
this.step = step;
}
}
-
+
static class Enrollment {
String course;
course = courseId;
}
}
+
static class EnrollmentWrapper {
Enrollment enrollment;
}
public void setTaskText(String text, VirtualFile taskDirectory, Project project) {
- if (StudyTaskManager.getInstance(project).isTurnEditingMode()) {
+ if (!EMPTY_TASK_TEXT.equals(text) && StudyTaskManager.getInstance(project).isTurnEditingMode()) {
if (taskDirectory == null) {
LOG.info("Failed to enter editing mode for StudyToolWindow");
return;
}
- VirtualFile taskTextFile = StudyUtils.findTaskDescriptionVirtualFile(taskDirectory);
+ VirtualFile taskTextFile = StudyUtils.findTaskDescriptionVirtualFile(project, taskDirectory);
enterEditingMode(taskTextFile, project);
StudyTaskManager.getInstance(project).setTurnEditingMode(false);
}
+ if (taskDirectory != null && StudyTaskManager.getInstance(project).getToolWindowMode() == StudyToolWindowMode.EDITING) {
+ VirtualFile taskTextFile = StudyUtils.findTaskDescriptionVirtualFile(project, taskDirectory);
+ enterEditingMode(taskTextFile, project);
+ }
else {
setText(text);
}
import com.intellij.ide.fileTemplates.FileTemplate;
import com.intellij.ide.fileTemplates.FileTemplateManager;
+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.project.Project;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.DocumentUtil;
+import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.core.EduNames;
+import com.jetbrains.edu.learning.courseFormat.Task;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
+import java.io.IOException;
public class PyCCLanguageManager implements CCLanguageManager {
+ private static final Logger LOG = Logger.getInstance(PyCCLanguageManager.class);
+
@Nullable
@Override
public String getDefaultTaskFileExtension() {
@Override
public boolean isTestFile(VirtualFile file) {
- return EduNames.TESTS_FILE.equals(file.getName());
+ String name = file.getName();
+ if (EduNames.TESTS_FILE.equals(name)) {
+ return true;
+ }
+ return name.contains(FileUtil.getNameWithoutExtension(EduNames.TESTS_FILE)) && name.contains(EduNames.STEP_MARKER);
}
+
+ @Override
+ public void createTestsForNewStep(@NotNull Project project, @NotNull Task task) {
+ VirtualFile taskDir = task.getTaskDir(project);
+ if (taskDir == null) {
+ return;
+ }
+
+ int prevStepIndex = task.getActiveStepIndex();
+ String name = prevStepIndex == -1 ? EduNames.TESTS_FILE : getStepTestsFileName(prevStepIndex);
+ VirtualFile testsFile = taskDir.findChild(name);
+ if (testsFile == null) {
+ return;
+ }
+ Document document = FileDocumentManager.getInstance().getDocument(testsFile);
+ if (document == null) {
+ return;
+ }
+ CharSequence prevTestText = document.getCharsSequence();
+ String nextStepTestsFileName = getStepTestsFileName(prevStepIndex + 1);
+ ApplicationManager.getApplication().runWriteAction(() -> {
+ try {
+ VirtualFile nextStepTestsFile = taskDir.createChildData(this, nextStepTestsFileName);
+ StudyTaskManager.getInstance(project).addInvisibleFiles(nextStepTestsFile.getPath());
+ Document nextStepDocument = FileDocumentManager.getInstance().getDocument(nextStepTestsFile);
+ if (nextStepDocument == null) {
+ return;
+ }
+ int index = prevStepIndex + 2;
+ //TODO: text for header
+ String header = "# This is test for step " + index + ". We've already copied tests from previous step here.\n\n";
+ DocumentUtil.writeInRunUndoTransparentAction(() -> {
+ nextStepDocument.insertString(0, header);
+ nextStepDocument.insertString(header.length(), prevTestText);
+ FileDocumentManager.getInstance().saveDocument(nextStepDocument);
+ });
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ });
+ }
+
+ @NotNull
+ public static String getStepTestsFileName(int index) {
+ if (index == -1) {
+ return EduNames.TESTS_FILE;
+ }
+ return FileUtil.getNameWithoutExtension(EduNames.TESTS_FILE) +
+ EduNames.STEP_MARKER +
+ index + "." +
+ FileUtilRt.getExtension(EduNames.TESTS_FILE);
+ }
}
@Override
public ExecutionResult execute(Executor executor, CommandLinePatcher... patchers) throws ExecutionException {
- CCUtils.createResources(myRunConfiguration.getProject(), myTask, myTaskDir);
+ CCUtils.updateResources(myRunConfiguration.getProject(), myTask, myTaskDir);
ApplicationManager.getApplication().runWriteAction(() -> StudyCheckUtils.flushWindows(myTask, myTaskDir));
return super.execute(executor, patchers);
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.jetbrains.edu.coursecreator.CCUtils;
+import com.jetbrains.edu.coursecreator.PyCCLanguageManager;
import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.courseFormat.Task;
return null;
}
+ Task task = StudyUtils.getTask(location.getProject(), taskDir);
+ if (task == null) {
+ return null;
+ }
+ String testsFileName = PyCCLanguageManager.getStepTestsFileName(task.getActiveStepIndex());
String taskDirPath = FileUtil.toSystemDependentName(taskDir.getPath());
String testsPath = taskDir.findChild(EduNames.SRC) != null ?
- FileUtil.join(taskDirPath, EduNames.SRC, EduNames.TESTS_FILE) :
- FileUtil.join(taskDirPath, EduNames.TESTS_FILE);
+ FileUtil.join(taskDirPath, EduNames.SRC, testsFileName) :
+ FileUtil.join(taskDirPath, testsFileName);
String filePath = FileUtil.toSystemDependentName(file.getPath());
return filePath.equals(testsPath) ? testsPath : null;
}
ApplicationManager.getApplication().invokeLater(() -> {
if (myTaskDir == null) return;
myTask.setStatus(StudyStatus.Failed);
- for (Map.Entry<String, TaskFile> entry : myTask.getTaskFiles().entrySet()) {
+ Map<String, TaskFile> taskFiles = StudyUtils.getTaskFiles(myTask);
+ for (Map.Entry<String, TaskFile> entry : taskFiles.entrySet()) {
final String name = entry.getKey();
final TaskFile taskFile = entry.getValue();
if (taskFile.getAnswerPlaceholders().size() < 2) {
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.vfs.VirtualFile;
+import com.jetbrains.edu.learning.checker.StudyTestRunner;
+import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.Task;
-import com.jetbrains.edu.learning.checker.StudyTestRunner;
import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
LOG.info("Language manager is null for " + course.getLanguageById().getDisplayName());
return null;
}
- final File testRunner = new File(myTaskDir.getPath(), manager.getTestFileName());
+
+ String testsFileName = manager.getTestFileName();
+ int activeStepIndex = myTask.getActiveStepIndex();
+ if (activeStepIndex != -1) {
+ testsFileName = FileUtil.getNameWithoutExtension(testsFileName) + EduNames.STEP_MARKER + activeStepIndex + "." + FileUtilRt.getExtension(testsFileName);
+ }
+ final File testRunner = new File(myTaskDir.getPath(), testsFileName);
final GeneralCommandLine commandLine = new GeneralCommandLine();
commandLine.withWorkDirectory(myTaskDir.getPath());
final Map<String, String> env = commandLine.getEnvironment();