diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCSubtaskEditorNotificationProvider.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCSubtaskEditorNotificationProvider.java index d0911b18080f..a2a49225e155 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCSubtaskEditorNotificationProvider.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCSubtaskEditorNotificationProvider.java @@ -74,10 +74,10 @@ public class CCSubtaskEditorNotificationProvider extends EditorNotifications.Pro return color == null ? super.getBackground() : color; } }; - String header = isTestFile ? "test" : "task file"; + String header = (isTestFile ? "test" : "task") + " file"; int activeSubtaskIndex = task.getActiveSubtaskIndex() + 1; int subtaskSize = task.getLastSubtaskIndex() + 1; - panel.setText("This is " + header + " for " + EduNames.SUBTASK + " " + activeSubtaskIndex + "/" + subtaskSize); + panel.setText("This is a " + header + " for " + EduNames.SUBTASK + " " + activeSubtaskIndex + "/" + subtaskSize); panel.createActionLabel(SWITCH_SUBTASK, () -> { ArrayList values = new ArrayList<>(); for (int i = 0; i <= task.getLastSubtaskIndex(); i++) { diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java index ea697bdc1ded..2dd6270f5d67 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java @@ -13,6 +13,8 @@ import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.DumbModePermission; +import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModuleRootModificationUtil; import com.intellij.openapi.roots.ProjectRootManager; @@ -48,7 +50,7 @@ public class CCUtils { public static final String GENERATED_FILES_FOLDER = ".coursecreator"; public static final String COURSE_MODE = "Course Creator"; - public static int getSubtaskIndex(Project project, VirtualFile file) { + public static int getSubtaskIndex(@NotNull Project project, @NotNull VirtualFile file) { String fileName = file.getName(); String name = FileUtil.getNameWithoutExtension(fileName); boolean canBeSubtaskFile = isTestsFile(project, file) || StudyUtils.isTaskDescriptionFile(fileName); @@ -65,7 +67,8 @@ public class CCUtils { } try { return Integer.valueOf(index); - } catch (NumberFormatException e) { + } + catch (NumberFormatException e) { return -1; } } @@ -147,21 +150,27 @@ public class CCUtils { return folder; } final Ref generatedRoot = new Ref<>(); - ApplicationManager.getApplication().runWriteAction(new Runnable() { + DumbService.allowStartingDumbModeInside(DumbModePermission.MAY_START_BACKGROUND, new Runnable() { @Override public void run() { - try { - generatedRoot.set(baseDir.createChildDirectory(this, GENERATED_FILES_FOLDER)); - VirtualFile contentRootForFile = - ProjectRootManager.getInstance(module.getProject()).getFileIndex().getContentRootForFile(generatedRoot.get()); - if (contentRootForFile == null) { - return; + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + try { + generatedRoot.set(baseDir.createChildDirectory(this, GENERATED_FILES_FOLDER)); + VirtualFile contentRootForFile = + ProjectRootManager.getInstance(module.getProject()).getFileIndex().getContentRootForFile(generatedRoot.get()); + if (contentRootForFile == null) { + return; + } + ModuleRootModificationUtil.updateExcludedFolders(module, contentRootForFile, Collections.emptyList(), + Collections.singletonList(generatedRoot.get().getUrl())); + } + catch (IOException e) { + LOG.info("Failed to create folder for generated files", e); + } } - ModuleRootModificationUtil.updateExcludedFolders(module, contentRootForFile, Collections.emptyList(), Collections.singletonList(generatedRoot.get().getUrl())); - } - catch (IOException e) { - LOG.info("Failed to create folder for generated files", e); - } + }); } }); return generatedRoot.get(); @@ -292,9 +301,9 @@ public class CCUtils { } public static void createTaskContent(@NotNull Project project, - @Nullable IdeView view, - @NotNull Course course, - PsiDirectory taskDirectory) { + @Nullable IdeView view, + @NotNull Course course, + PsiDirectory taskDirectory) { CCLanguageManager manager = getStudyLanguageManager(course); if (manager == null) { return; diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/placeholder/CCActivatePlaceholder.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/placeholder/CCActivatePlaceholder.java index e34d3246ec76..0bb15cfbbe29 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/placeholder/CCActivatePlaceholder.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/placeholder/CCActivatePlaceholder.java @@ -1,9 +1,11 @@ package com.jetbrains.edu.coursecreator.actions.placeholder; import com.intellij.openapi.util.TextRange; +import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholderSubtaskInfo; import com.jetbrains.edu.learning.courseFormat.TaskFile; +import org.jetbrains.annotations.NotNull; public class CCActivatePlaceholder extends CCSubtaskPlaceholderAction { @@ -14,9 +16,9 @@ public class CCActivatePlaceholder extends CCSubtaskPlaceholderAction { } @Override - protected AnswerPlaceholderSubtaskInfo getInfo(CCState state, + protected AnswerPlaceholderSubtaskInfo getInfo(@NotNull CCState state, int subtaskIndex, - AnswerPlaceholder existingPlaceholder) { + @NotNull AnswerPlaceholder existingPlaceholder) { int visibleLength = existingPlaceholder.getVisibleLength(subtaskIndex); int placeholderOffset = existingPlaceholder.getOffset(); String possibleAnswer = state.getEditor().getDocument().getText(TextRange.create(placeholderOffset, placeholderOffset + visibleLength)); @@ -31,18 +33,18 @@ public class CCActivatePlaceholder extends CCSubtaskPlaceholderAction { } @Override - protected void redoAction(AnswerPlaceholder existingPlaceholder, int subtaskIndex, AnswerPlaceholderSubtaskInfo info) { + protected void redoAction(@NotNull AnswerPlaceholder existingPlaceholder, int subtaskIndex, @NotNull AnswerPlaceholderSubtaskInfo info) { existingPlaceholder.getSubtaskInfos().put(subtaskIndex, info); } @Override - protected void undoAction(AnswerPlaceholder existingPlaceholder, int subtaskIndex, AnswerPlaceholderSubtaskInfo info) { + protected void undoAction(@NotNull AnswerPlaceholder existingPlaceholder, int subtaskIndex, @NotNull AnswerPlaceholderSubtaskInfo info) { existingPlaceholder.getSubtaskInfos().remove(subtaskIndex); } @Override protected boolean isAvailable(TaskFile taskFile, int offset) { - AnswerPlaceholder existingPlaceholder = taskFile.getAnswerPlaceholder(offset, taskFile.getAnswerPlaceholders()); + AnswerPlaceholder existingPlaceholder = StudyUtils.getAnswerPlaceholder(offset, taskFile.getAnswerPlaceholders()); return existingPlaceholder != null && !existingPlaceholder.isActive(); } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/placeholder/CCAddAnswerPlaceholder.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/placeholder/CCAddAnswerPlaceholder.java index d7220ff51919..9651adf4e9dc 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/placeholder/CCAddAnswerPlaceholder.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/placeholder/CCAddAnswerPlaceholder.java @@ -145,7 +145,7 @@ public class CCAddAnswerPlaceholder extends CCAnswerPlaceholderAction { return !arePlaceholdersIntersect(taskFile, start, end); } int offset = editor.getCaretModel().getOffset(); - return taskFile.getAnswerPlaceholder(offset, taskFile.getAnswerPlaceholders()) == null; + return StudyUtils.getAnswerPlaceholder(offset, taskFile.getAnswerPlaceholders()) == null; } protected CCCreateAnswerPlaceholderDialog createDialog(Project project, AnswerPlaceholder answerPlaceholder) { diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/placeholder/CCDeactivatePlaceholder.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/placeholder/CCDeactivatePlaceholder.java index 75db3a889eaa..12f462e09d98 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/placeholder/CCDeactivatePlaceholder.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/placeholder/CCDeactivatePlaceholder.java @@ -3,6 +3,7 @@ package com.jetbrains.edu.coursecreator.actions.placeholder; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholderSubtaskInfo; import com.jetbrains.edu.learning.courseFormat.TaskFile; +import org.jetbrains.annotations.NotNull; import java.util.Collections; @@ -14,14 +15,14 @@ public class CCDeactivatePlaceholder extends CCSubtaskPlaceholderAction { super(TITLE, TITLE); } - protected void undoAction(AnswerPlaceholder existingPlaceholder, int subtaskIndex, AnswerPlaceholderSubtaskInfo info) { + protected void undoAction(@NotNull AnswerPlaceholder existingPlaceholder, int subtaskIndex, @NotNull AnswerPlaceholderSubtaskInfo info) { existingPlaceholder.getSubtaskInfos().put(subtaskIndex, info); } @Override - protected AnswerPlaceholderSubtaskInfo getInfo(CCState state, + protected AnswerPlaceholderSubtaskInfo getInfo(@NotNull CCState state, int subtaskIndex, - AnswerPlaceholder existingPlaceholder) { + @NotNull AnswerPlaceholder existingPlaceholder) { return existingPlaceholder.getSubtaskInfos().get(subtaskIndex); } @@ -30,7 +31,7 @@ public class CCDeactivatePlaceholder extends CCSubtaskPlaceholderAction { return TITLE; } - protected void redoAction(AnswerPlaceholder existingPlaceholder, int subtaskIndex, AnswerPlaceholderSubtaskInfo info) { + protected void redoAction(@NotNull AnswerPlaceholder existingPlaceholder, int subtaskIndex, @NotNull AnswerPlaceholderSubtaskInfo info) { existingPlaceholder.getSubtaskInfos().remove(subtaskIndex); } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/placeholder/CCDeleteAnswerPlaceholder.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/placeholder/CCDeleteAnswerPlaceholder.java index 559385f3fbff..508da6af06b0 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/placeholder/CCDeleteAnswerPlaceholder.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/placeholder/CCDeleteAnswerPlaceholder.java @@ -23,21 +23,20 @@ public class CCDeleteAnswerPlaceholder extends CCAnswerPlaceholderAction { Project project = state.getProject(); TaskFile taskFile = state.getTaskFile(); AnswerPlaceholder answerPlaceholder = state.getAnswerPlaceholder(); - EduUtils.runUndoableAction(project, "Delete Answer Placeholder", new CCAddAnswerPlaceholder.AddAction(answerPlaceholder, taskFile, state.getEditor()) { - @Override - public void undo() throws UnexpectedUndoException { - super.redo(); - } + EduUtils.runUndoableAction(project, "Delete Answer Placeholder", + new CCAddAnswerPlaceholder.AddAction(answerPlaceholder, taskFile, state.getEditor()) { + @Override + public void undo() throws UnexpectedUndoException { + super.redo(); + } - @Override - public void redo() throws UnexpectedUndoException { - super.undo(); - } - }); + @Override + public void redo() throws UnexpectedUndoException { + super.undo(); + } + }); } - - private static boolean canDeletePlaceholder(@NotNull CCState state) { if (state.getEditor().getSelectionModel().hasSelection()) { return false; @@ -59,5 +58,4 @@ public class CCDeleteAnswerPlaceholder extends CCAnswerPlaceholderAction { presentation.setEnabledAndVisible(true); } } - } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/placeholder/CCSubtaskPlaceholderAction.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/placeholder/CCSubtaskPlaceholderAction.java index 77d6aa13ee8e..550ad1a8e4d5 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/placeholder/CCSubtaskPlaceholderAction.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/placeholder/CCSubtaskPlaceholderAction.java @@ -24,7 +24,7 @@ public abstract class CCSubtaskPlaceholderAction extends CCAnswerPlaceholderActi final int offset = editor.getCaretModel().getOffset(); TaskFile taskFile = state.getTaskFile(); int subtaskIndex = state.getTaskFile().getTask().getActiveSubtaskIndex(); - AnswerPlaceholder existingPlaceholder = taskFile.getAnswerPlaceholder(offset, taskFile.getAnswerPlaceholders()); + AnswerPlaceholder existingPlaceholder = StudyUtils.getAnswerPlaceholder(offset, taskFile.getAnswerPlaceholders()); if (existingPlaceholder == null) { return; } @@ -47,13 +47,13 @@ public abstract class CCSubtaskPlaceholderAction extends CCAnswerPlaceholderActi }); } - protected abstract AnswerPlaceholderSubtaskInfo getInfo(CCState state, int subtaskIndex, AnswerPlaceholder existingPlaceholder); + protected abstract AnswerPlaceholderSubtaskInfo getInfo(@NotNull CCState state, int subtaskIndex, @NotNull AnswerPlaceholder existingPlaceholder); protected abstract String getTitle(); - protected abstract void redoAction(AnswerPlaceholder existingPlaceholder, int subtaskIndex, AnswerPlaceholderSubtaskInfo info); + protected abstract void redoAction(@NotNull AnswerPlaceholder existingPlaceholder, int subtaskIndex, @NotNull AnswerPlaceholderSubtaskInfo info); - protected abstract void undoAction(AnswerPlaceholder existingPlaceholder, int subtaskIndex, AnswerPlaceholderSubtaskInfo info); + protected abstract void undoAction(@NotNull AnswerPlaceholder existingPlaceholder, int subtaskIndex, @NotNull AnswerPlaceholderSubtaskInfo info); @Override public void update(AnActionEvent e) { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudySerializationUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudySerializationUtils.java index 076d3ef73b76..279fad1248fc 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudySerializationUtils.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudySerializationUtils.java @@ -219,8 +219,7 @@ public class StudySerializationUtils { for (Element placeholder : getChildList(taskFileElement, ANSWER_PLACEHOLDERS)) { Element valueElement = new Element(SUBTASK_INFO); addChildMap(placeholder, SUBTASK_INFOS, Collections.singletonMap(String.valueOf(0), valueElement)); - for (String childName : ContainerUtil - .list(HINT, ADDITIONAL_HINTS, POSSIBLE_ANSWER, SELECTED, STATUS, TASK_TEXT)) { + for (String childName : ContainerUtil.list(HINT, ADDITIONAL_HINTS, POSSIBLE_ANSWER, SELECTED, STATUS, TASK_TEXT)) { Element child = getChildWithName(placeholder, childName); valueElement.addContent(child.clone()); } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java index 9505f6d63eab..9dcf390cd048 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java @@ -798,4 +798,15 @@ public class StudyUtils { toolWindowManager.registerToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW, true, ToolWindowAnchor.RIGHT, project, true); } } + + @Nullable public static AnswerPlaceholder getAnswerPlaceholder(int offset, List placeholders) { + for (AnswerPlaceholder placeholder : placeholders) { + int placeholderStart = placeholder.getOffset(); + int placeholderEnd = placeholderStart + placeholder.getRealLength(); + if (placeholderStart <= offset && offset <= placeholderEnd) { + return placeholder; + } + } + return null; + } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java index cf054406f5d8..6e11bc7a7b24 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java @@ -193,8 +193,7 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun int visibleSubtaskIndex = myTask.getActiveSubtaskIndex() + 1; ApplicationManager.getApplication().invokeLater(() -> { int subtaskSize = myTask.getLastSubtaskIndex() + 1; - String resultMessage = !hasMoreSubtasks ? message : "Subtask " + visibleSubtaskIndex + "/" + - subtaskSize + " solved"; + String resultMessage = !hasMoreSubtasks ? message : "Subtask " + visibleSubtaskIndex + "/" + subtaskSize + " solved"; StudyCheckUtils.showTestResultPopUp(resultMessage, MessageType.INFO.getPopupBackground(), myProject); if (hasMoreSubtasks) { int nextSubtaskIndex = myTask.getActiveSubtaskIndex() + 1; diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java index 92cc5aff9283..520e5d87d2cf 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java @@ -114,16 +114,19 @@ public class StudyCheckUtils { public static void runSmartTestProcess(@NotNull final VirtualFile taskDir, @NotNull final StudyTestRunner testRunner, - final String taskFileName, + @NotNull final String taskFileName, @NotNull final TaskFile taskFile, @NotNull final Project project) { - final TaskFile answerTaskFile = new TaskFile(); - answerTaskFile.name = taskFileName; final VirtualFile virtualFile = taskDir.findChild(taskFileName); if (virtualFile == null) { return; } - final VirtualFile answerFile = getCopyWithAnswers(taskDir, virtualFile, taskFile, answerTaskFile); + Pair pair = getCopyWithAnswers(taskDir, virtualFile, taskFile); + if (pair == null) { + return; + } + VirtualFile answerFile = pair.getFirst(); + TaskFile answerTaskFile = pair.getSecond(); for (final AnswerPlaceholder answerPlaceholder : answerTaskFile.getActivePlaceholders()) { final Document document = FileDocumentManager.getInstance().getDocument(virtualFile); if (document == null) { @@ -136,36 +139,34 @@ public class StudyCheckUtils { } - private static VirtualFile getCopyWithAnswers(@NotNull final VirtualFile taskDir, + private static Pair getCopyWithAnswers(@NotNull final VirtualFile taskDir, @NotNull final VirtualFile file, - @NotNull final TaskFile source, - @NotNull TaskFile target) { - VirtualFile copy = null; + @NotNull final TaskFile source) { try { - - copy = file.copy(taskDir, taskDir, file.getNameWithoutExtension() + EduNames.ANSWERS_POSTFIX + "." + file.getExtension()); + VirtualFile answerFile = file.copy(taskDir, taskDir, file.getNameWithoutExtension() + EduNames.ANSWERS_POSTFIX + "." + file.getExtension()); final FileDocumentManager documentManager = FileDocumentManager.getInstance(); - final Document document = documentManager.getDocument(copy); + final Document document = documentManager.getDocument(answerFile); if (document != null) { - target = source.getTask().copy().getTaskFile(file.getName()); - if (target == null) { - return copy; + TaskFile answerTaskFile = source.getTask().copy().getTaskFile(file.getName()); + if (answerTaskFile == null) { + return null; } - EduDocumentListener listener = new EduDocumentListener(target); + EduDocumentListener listener = new EduDocumentListener(answerTaskFile); document.addDocumentListener(listener); - for (AnswerPlaceholder answerPlaceholder : target.getActivePlaceholders()) { + for (AnswerPlaceholder answerPlaceholder : answerTaskFile.getActivePlaceholders()) { final int start = answerPlaceholder.getOffset(); final int end = start + answerPlaceholder.getRealLength(); final String text = answerPlaceholder.getPossibleAnswer(); document.replaceString(start, end, text); } ApplicationManager.getApplication().runWriteAction(() -> documentManager.saveDocument(document)); + return Pair.create(answerFile, answerTaskFile); } } catch (IOException e) { LOG.error(e); } - return copy; + return null; } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java index 48ff8e48b24d..11b94b633933 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java @@ -50,8 +50,10 @@ public class StudySmartChecker { if (windowDocument != null) { final File resourceFile = StudyUtils.copyResourceFile(virtualFile.getName(), windowCopy.getName(), project, usersTaskFile.getTask()); - final TaskFile windowTaskFile = new TaskFile(); - TaskFile.copy(answerTaskFile, windowTaskFile); + TaskFile windowTaskFile = answerTaskFile.getTask().copy().getTaskFile(virtualFile.getName()); + if (windowTaskFile == null) { + return; + } EduDocumentListener listener = new EduDocumentListener(windowTaskFile); windowDocument.addDocumentListener(listener); int start = placeholder.getOffset(); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduAnswerPlaceholderPainter.java b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduAnswerPlaceholderPainter.java index 7d29f15b7e71..6d05809fcba0 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduAnswerPlaceholderPainter.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduAnswerPlaceholderPainter.java @@ -16,6 +16,7 @@ import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholderSubtaskInfo; import com.jetbrains.edu.learning.courseFormat.TaskFile; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.awt.*; import java.util.List; @@ -37,7 +38,8 @@ public class EduAnswerPlaceholderPainter { textAttributes.setEffectColor(color); if (placeholder.isActive()) { drawAnswerPlaceholder(editor, placeholder, textAttributes, PLACEHOLDERS_LAYER); - } else if (!placeholder.getUseLength()) { + } + else if (!placeholder.getUseLength()) { int offset = placeholder.getOffset(); drawAnswerPlaceholderFromPrevStep(editor, offset, offset + placeholder.getVisibleLength(placeholder.getActiveSubtaskIndex())); } @@ -45,7 +47,7 @@ public class EduAnswerPlaceholderPainter { public static void drawAnswerPlaceholder(@NotNull Editor editor, @NotNull AnswerPlaceholder placeholder, - TextAttributes textAttributes, + @Nullable TextAttributes textAttributes, int placeholdersLayer) { int startOffset = placeholder.getOffset(); if (startOffset == -1) { @@ -69,16 +71,15 @@ public class EduAnswerPlaceholderPainter { public static void drawAnswerPlaceholder(@NotNull Editor editor, int start, int end, - TextAttributes textAttributes, + @Nullable TextAttributes textAttributes, int placeholdersLayer) { final Project project = editor.getProject(); assert project != null; if (start == -1) { return; } - RangeHighlighter - highlighter = editor.getMarkupModel().addRangeHighlighter(start, end, placeholdersLayer, - textAttributes, HighlighterTargetArea.EXACT_RANGE); + RangeHighlighter highlighter = editor.getMarkupModel().addRangeHighlighter(start, end, placeholdersLayer, + textAttributes, HighlighterTargetArea.EXACT_RANGE); highlighter.setGreedyToLeft(true); highlighter.setGreedyToRight(true); } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/AnswerPlaceholder.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/AnswerPlaceholder.java index ab23d693e6a3..6cfb41eeb03b 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/AnswerPlaceholder.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/AnswerPlaceholder.java @@ -24,7 +24,8 @@ public class AnswerPlaceholder { @SerializedName("offset") @Expose private int myOffset = -1; - @Expose private int length = -1; + @SerializedName("length") + @Expose private int myLength = -1; private int myIndex = -1; private MyInitialState myInitialState; @@ -41,7 +42,7 @@ public class AnswerPlaceholder { public void initAnswerPlaceholder(final TaskFile file, boolean isRestarted) { setTaskFile(file); if (!isRestarted) { - setInitialState(new MyInitialState(myOffset, length)); + setInitialState(new MyInitialState(myOffset, myLength)); for (AnswerPlaceholderSubtaskInfo info : getSubtaskInfos().values()) { info.setStatus(file.getTask().getStatus()); } @@ -60,11 +61,11 @@ public class AnswerPlaceholder { * in actions {@link AnswerPlaceholder#getRealLength()} should be used */ public int getLength() { - return length; + return myLength; } public void setLength(int length) { - this.length = length; + myLength = length; } @Transient @@ -114,7 +115,7 @@ public class AnswerPlaceholder { */ public void reset() { myOffset = myInitialState.getOffset(); - length = myInitialState.getLength(); + myLength = myInitialState.getLength(); } @Transient @@ -237,13 +238,13 @@ public class AnswerPlaceholder { int minIndex = Collections.min(mySubtaskInfos.keySet()); AnswerPlaceholderSubtaskInfo minInfo = mySubtaskInfos.get(minIndex); if (minIndex == subtaskIndex) { - return getUseLength() ? length : minInfo.getPossibleAnswer().length(); + return getUseLength() ? myLength : minInfo.getPossibleAnswer().length(); } if (minIndex > subtaskIndex) { return minInfo.isNeedInsertText() ? 0 : minInfo.getPlaceholderText().length(); } int maxIndex = Collections.max(ContainerUtil.filter(mySubtaskInfos.keySet(), i -> i <= subtaskIndex)); - return getUseLength() ? length : mySubtaskInfos.get(maxIndex).getPossibleAnswer().length(); + return getUseLength() ? myLength : mySubtaskInfos.get(maxIndex).getPossibleAnswer().length(); } public void switchSubtask(@NotNull Project project, @NotNull Document document, int fromSubtask, int toSubtask) { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/AnswerPlaceholderSubtaskInfo.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/AnswerPlaceholderSubtaskInfo.java index 33b7cda8de1d..3c3c931d9d4e 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/AnswerPlaceholderSubtaskInfo.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/AnswerPlaceholderSubtaskInfo.java @@ -15,7 +15,7 @@ public class AnswerPlaceholderSubtaskInfo { @Expose private List myHints = new ArrayList<>(); @SerializedName("possible_answer") - @Expose private String possibleAnswer = ""; + @Expose private String myPossibleAnswer = ""; @SerializedName("placeholder_text") @Expose private String myPlaceholderText; @@ -60,11 +60,11 @@ public class AnswerPlaceholderSubtaskInfo { } public String getPossibleAnswer() { - return possibleAnswer; + return myPossibleAnswer; } public void setPossibleAnswer(String possibleAnswer) { - this.possibleAnswer = possibleAnswer; + myPossibleAnswer = possibleAnswer; } public String getPlaceholderText() { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/TaskFile.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/TaskFile.java index 4358ee62d62c..cd6ec7377689 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/TaskFile.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/TaskFile.java @@ -3,6 +3,7 @@ package com.jetbrains.edu.learning.courseFormat; import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; import com.intellij.util.xmlb.annotations.Transient; +import com.jetbrains.edu.learning.StudyUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -87,18 +88,7 @@ public class TaskFile { */ @Nullable public AnswerPlaceholder getAnswerPlaceholder(int offset) { - return getAnswerPlaceholder(offset, getActivePlaceholders()); - } - - @Nullable public AnswerPlaceholder getAnswerPlaceholder(int offset, List placeholders) { - for (AnswerPlaceholder placeholder : placeholders) { - int placeholderStart = placeholder.getOffset(); - int placeholderEnd = placeholderStart + placeholder.getRealLength(); - if (placeholderStart <= offset && offset <= placeholderEnd) { - return placeholder; - } - } - return null; + return StudyUtils.getAnswerPlaceholder(offset, getActivePlaceholders()); } public boolean isTrackLengths() { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/navigation/StudyNavigator.java b/python/educational-core/student/src/com/jetbrains/edu/learning/navigation/StudyNavigator.java index 748893afaacf..525719dd95de 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/navigation/StudyNavigator.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/navigation/StudyNavigator.java @@ -109,22 +109,22 @@ public class StudyNavigator { @Nullable public static VirtualFile getFileToActivate(@NotNull Project project, Map nextTaskFiles, VirtualFile taskDir) { - VirtualFile shouldBeActive = null; + VirtualFile fileToActivate = null; for (Map.Entry entry : nextTaskFiles.entrySet()) { String name = entry.getKey(); TaskFile taskFile = entry.getValue(); VirtualFile srcDir = taskDir.findChild(EduNames.SRC); VirtualFile vf = srcDir == null ? taskDir.findChild(name) : srcDir.findChild(name); if (vf != null) { - if (shouldBeActive != null) { + if (fileToActivate != null) { FileEditorManager.getInstance(project).openFile(vf, true); } - if (shouldBeActive == null && !taskFile.getActivePlaceholders().isEmpty()) { - shouldBeActive = vf; + if (fileToActivate == null && !taskFile.getActivePlaceholders().isEmpty()) { + fileToActivate = vf; } } } - return shouldBeActive != null ? shouldBeActive : getFirstTaskFile(taskDir, project); + return fileToActivate != null ? fileToActivate : getFirstTaskFile(taskDir, project); } @Nullable @@ -178,9 +178,10 @@ public class StudyNavigator { FileEditorManager.getInstance(project).openFile(virtualFile, true); } EduUsagesCollector.taskNavigation(); - VirtualFile shouldBeActive = getFileToActivate(project, nextTaskFiles, taskDir); - - updateProjectView(project, shouldBeActive); + VirtualFile fileToActivate = getFileToActivate(project, nextTaskFiles, taskDir); + if (fileToActivate != null) { + updateProjectView(project, fileToActivate); + } StudyUtils.selectFirstAnswerPlaceholder(StudyUtils.getSelectedStudyEditor(project), project); ToolWindow runToolWindow = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.RUN); @@ -189,38 +190,37 @@ public class StudyNavigator { } } - private static void updateProjectView(@NotNull Project project, VirtualFile shouldBeActive) { + private static void updateProjectView(@NotNull Project project, @NotNull VirtualFile fileToActivate) { JTree tree = ProjectView.getInstance(project).getCurrentProjectViewPane().getTree(); - if (shouldBeActive != null) { - ProjectView.getInstance(project).selectCB(shouldBeActive, shouldBeActive, false).doWhenDone(() -> { - List paths = TreeUtil.collectExpandedPaths(tree); - List toCollapse = new ArrayList<>(); - TreePath selectedPath = tree.getSelectionPath(); - for (TreePath treePath : paths) { - if (treePath.isDescendant(selectedPath)) { - continue; + ProjectView.getInstance(project).selectCB(fileToActivate, fileToActivate, false).doWhenDone(() -> { + List paths = TreeUtil.collectExpandedPaths(tree); + List toCollapse = new ArrayList<>(); + TreePath selectedPath = tree.getSelectionPath(); + for (TreePath treePath : paths) { + if (treePath.isDescendant(selectedPath)) { + continue; + } + if (toCollapse.isEmpty()) { + toCollapse.add(treePath); + continue; + } + for (int i = 0; i < toCollapse.size(); i++) { + TreePath path = toCollapse.get(i); + if (treePath.isDescendant(path)) { + toCollapse.set(i, treePath); } - if (toCollapse.isEmpty()) { - toCollapse.add(treePath); - continue; - } - for (int i = 0; i < toCollapse.size(); i++) { - TreePath path = toCollapse.get(i); - if (treePath.isDescendant(path)) { - toCollapse.set(i, treePath); - } else { - if (!path.isDescendant(treePath)) { - toCollapse.add(treePath); - } + else { + if (!path.isDescendant(treePath)) { + toCollapse.add(treePath); } } } - for (TreePath path : toCollapse) { - tree.collapsePath(path); - tree.fireTreeCollapsed(path); - } - }); - FileEditorManager.getInstance(project).openFile(shouldBeActive, true); - } + } + for (TreePath path : toCollapse) { + tree.collapsePath(path); + tree.fireTreeCollapsed(path); + } + }); + FileEditorManager.getInstance(project).openFile(fileToActivate, true); } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/projectView/LessonDirectoryNode.java b/python/educational-core/student/src/com/jetbrains/edu/learning/projectView/LessonDirectoryNode.java index 4810bab66e95..03b81f24c40d 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/projectView/LessonDirectoryNode.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/projectView/LessonDirectoryNode.java @@ -41,7 +41,6 @@ public class LessonDirectoryNode extends StudyDirectoryNode { JBColor color = isSolved ? JBColor.BLACK : LIGHT_GREEN; Icon icon = isSolved ? InteractiveLearningIcons.Lesson : InteractiveLearningIcons.LessonCompl; updatePresentation(data, myLesson.getName(), color, icon, null); - } @Override diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java index 2fe31bbbe410..13184eea6432 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicConnector.java @@ -119,13 +119,14 @@ public class EduStepicConnector { final URI url; try { url = new URIBuilder(EduStepicNames.COURSES).addParameter("is_idea_compatible", "true"). - addParameter("page", String.valueOf(pageNumber)).build(); + addParameter("page", String.valueOf(pageNumber)).build(); } catch (URISyntaxException e) { LOG.error(e.getMessage()); return false; } - final StepicWrappers.CoursesContainer coursesContainer = EduStepicClient.getFromStepic(url.toString(), StepicWrappers.CoursesContainer.class); + final StepicWrappers.CoursesContainer coursesContainer = + EduStepicClient.getFromStepic(url.toString(), StepicWrappers.CoursesContainer.class); addAvailableCourses(result, coursesContainer); return coursesContainer.meta.containsKey("has_next") && coursesContainer.meta.get("has_next") == Boolean.TRUE; } @@ -167,7 +168,8 @@ public class EduStepicConnector { try { Integer version = Integer.valueOf(versionString); return version <= CURRENT_VERSION; - } catch (NumberFormatException e) { + } + catch (NumberFormatException e) { LOG.info("Wrong version format", e); return false; } @@ -180,7 +182,7 @@ public class EduStepicConnector { course.setAdaptive(info.isAdaptive()); course.setId(info.getId()); course.setUpdateDate(getCourseUpdateDate(info.getId())); - + if (!course.isAdaptive()) { String courseType = info.getType(); course.setName(info.getName()); @@ -216,7 +218,8 @@ public class EduStepicConnector { public static List getLessons(int sectionId) throws IOException { final StepicWrappers.SectionContainer - sectionContainer = EduStepicClient.getFromStepic(EduStepicNames.SECTIONS + String.valueOf(sectionId), StepicWrappers.SectionContainer.class); + sectionContainer = + EduStepicClient.getFromStepic(EduStepicNames.SECTIONS + String.valueOf(sectionId), StepicWrappers.SectionContainer.class); List unitIds = sectionContainer.sections.get(0).units; final List lessons = new ArrayList<>(); for (Integer unitId : unitIds) { @@ -224,14 +227,16 @@ public class EduStepicConnector { unit = EduStepicClient.getFromStepic(EduStepicNames.UNITS + "/" + String.valueOf(unitId), StepicWrappers.UnitContainer.class); int lessonID = unit.units.get(0).lesson; StepicWrappers.LessonContainer - lessonContainer = EduStepicClient.getFromStepic(EduStepicNames.LESSONS + String.valueOf(lessonID), StepicWrappers.LessonContainer.class); + lessonContainer = + EduStepicClient.getFromStepic(EduStepicNames.LESSONS + String.valueOf(lessonID), StepicWrappers.LessonContainer.class); Lesson lesson = lessonContainer.lessons.get(0); lesson.taskList = new ArrayList<>(); for (Integer s : lesson.steps) { createTask(lesson, s); } - if (!lesson.taskList.isEmpty()) + if (!lesson.taskList.isEmpty()) { lessons.add(lesson); + } } return lessons; @@ -260,7 +265,8 @@ public class EduStepicConnector { } public static StepicWrappers.StepSource getStep(Integer step) throws IOException { - return EduStepicClient.getFromStepic(EduStepicNames.STEPS + "/" + String.valueOf(step), StepicWrappers.StepContainer.class).steps.get(0); + return EduStepicClient.getFromStepic(EduStepicNames.STEPS + "/" + String.valueOf(step), StepicWrappers.StepContainer.class).steps + .get(0); } public static void postAttempt(@NotNull final Task task, boolean passed, @NotNull final Project project) { @@ -282,7 +288,8 @@ public class EduStepicConnector { if (statusLine.getStatusCode() != HttpStatus.SC_CREATED) { LOG.warn("Failed to make attempt " + attemptResponseString); } - final StepicWrappers.AttemptWrapper.Attempt attempt = new Gson().fromJson(attemptResponseString, StepicWrappers.AttemptContainer.class).attempts.get(0); + final StepicWrappers.AttemptWrapper.Attempt attempt = + new Gson().fromJson(attemptResponseString, StepicWrappers.AttemptContainer.class).attempts.get(0); final Map taskFiles = task.getTaskFiles(); final ArrayList files = new ArrayList<>(); diff --git a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCLanguageManager.java b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCLanguageManager.java index 6748590975f1..fd731c7cbd59 100644 --- a/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCLanguageManager.java +++ b/python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCLanguageManager.java @@ -2,16 +2,15 @@ package com.jetbrains.edu.coursecreator; import com.intellij.ide.fileTemplates.FileTemplate; import com.intellij.ide.fileTemplates.FileTemplateManager; +import com.intellij.ide.fileTemplates.FileTemplateUtil; 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.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiManager; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Task; import org.jetbrains.annotations.NotNull; @@ -69,38 +68,23 @@ public class PyCCLanguageManager implements CCLanguageManager { if (taskDir == null) { return; } - - int prevSubtaskIndex = task.getLastSubtaskIndex(); - String name = prevSubtaskIndex == 0 ? EduNames.TESTS_FILE : getSubtaskTestsFileName(prevSubtaskIndex); - VirtualFile testsFile = taskDir.findChild(name); - if (testsFile == null) { - return; - } - Document document = FileDocumentManager.getInstance().getDocument(testsFile); - if (document == null) { - return; - } - CharSequence prevTestText = document.getCharsSequence(); - int nextSubtaskIndex = prevSubtaskIndex + 1; + int nextSubtaskIndex = task.getLastSubtaskIndex() + 1; String nextSubtaskTestsFileName = getSubtaskTestsFileName(nextSubtaskIndex); ApplicationManager.getApplication().runWriteAction(() -> { try { - VirtualFile nextSubtaskTestsFile = taskDir.createChildData(this, nextSubtaskTestsFileName); - StudyTaskManager.getInstance(project).addInvisibleFiles(nextSubtaskTestsFile.getPath()); - Document nextSubtaskDocument = FileDocumentManager.getInstance().getDocument(nextSubtaskTestsFile); - if (nextSubtaskDocument == null) { + PsiDirectory taskPsiDir = PsiManager.getInstance(project).findDirectory(taskDir); + FileTemplate testsTemplate = getTestsTemplate(project); + if (taskPsiDir == null || testsTemplate == null) { return; } - String header = "# This is test for subtask " + nextSubtaskIndex + ". We've already copied tests from previous subtask here.\n\n"; - DocumentUtil.writeInRunUndoTransparentAction(() -> { - nextSubtaskDocument.insertString(0, header); - nextSubtaskDocument.insertString(header.length(), prevTestText); - FileDocumentManager.getInstance().saveDocument(nextSubtaskDocument); - }); + FileTemplateUtil.createFromTemplate(testsTemplate, nextSubtaskTestsFileName, null, taskPsiDir); } catch (IOException e) { LOG.error(e); } + catch (Exception e) { + LOG.error(e); + } }); }