import com.jetbrains.edu.EduUtils;
import com.jetbrains.edu.courseFormat.*;
import com.jetbrains.edu.learning.editor.StudyEditor;
-import com.jetbrains.edu.learning.run.StudyExecutor;
-import com.jetbrains.edu.learning.run.StudyTestRunner;
+import com.jetbrains.edu.learning.checker.StudyExecutor;
+import com.jetbrains.edu.learning.checker.StudyTestRunner;
import com.jetbrains.edu.learning.ui.StudyProgressToolWindowFactory;
import com.jetbrains.edu.learning.ui.StudyToolWindowFactory;
import org.jetbrains.annotations.NotNull;
package com.jetbrains.edu.learning.actions;
-import com.intellij.execution.ExecutionException;
-import com.intellij.execution.process.CapturingProcessHandler;
-import com.intellij.execution.process.ProcessOutput;
-import com.intellij.ide.projectView.ProjectView;
-import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.KeyboardShortcut;
import com.intellij.openapi.actionSystem.Presentation;
-import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.command.CommandProcessor;
-import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.fileEditor.FileDocumentManager;
-import com.intellij.openapi.fileEditor.FileEditor;
-import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.keymap.KeymapUtil;
-import com.intellij.openapi.progress.ProgressIndicator;
-import com.intellij.openapi.progress.ProgressManager;
-import com.intellij.openapi.progress.TaskInfo;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
-import com.intellij.openapi.ui.popup.Balloon;
-import com.intellij.openapi.ui.popup.BalloonBuilder;
-import com.intellij.openapi.ui.popup.JBPopupFactory;
-import com.intellij.openapi.util.Pair;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.openapi.wm.IdeFocusManager;
-import com.intellij.openapi.wm.IdeFrame;
-import com.intellij.openapi.wm.WindowManager;
-import com.intellij.openapi.wm.ex.StatusBarEx;
-import com.intellij.openapi.wm.ex.WindowManagerEx;
-import com.jetbrains.edu.EduDocumentListener;
-import com.jetbrains.edu.EduUtils;
-import com.jetbrains.edu.courseFormat.AnswerPlaceholder;
-import com.jetbrains.edu.courseFormat.StudyStatus;
-import com.jetbrains.edu.courseFormat.Task;
-import com.jetbrains.edu.courseFormat.TaskFile;
-import com.jetbrains.edu.learning.StudyState;
-import com.jetbrains.edu.learning.StudyTaskManager;
+import com.intellij.openapi.util.Ref;
import com.jetbrains.edu.learning.StudyUtils;
-import com.jetbrains.edu.learning.editor.StudyEditor;
-import com.jetbrains.edu.learning.navigation.StudyNavigator;
-import com.jetbrains.edu.learning.run.StudySmartChecker;
-import com.jetbrains.edu.learning.run.StudyTestRunner;
-import com.jetbrains.edu.stepic.EduStepicConnector;
-import com.jetbrains.edu.stepic.StudySettings;
+import com.jetbrains.edu.learning.checker.StudyCheckUtils;
import icons.InteractiveLearningIcons;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import javax.swing.*;
-import java.awt.*;
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
+
public class StudyCheckAction extends DumbAwareAction {
- private static final Logger LOG = Logger.getInstance(StudyCheckAction.class.getName());
- private static final String ANSWERS_POSTFIX = "_answers";
public static final String ACTION_ID = "CheckAction";
public static final String SHORTCUT = "ctrl alt pressed ENTER";
- boolean checkInProgress = false;
+ protected Ref<Boolean> myCheckInProgress = new Ref<>(false);
public StudyCheckAction() {
super("Check Task (" + KeymapUtil.getShortcutText(new KeyboardShortcut(KeyStroke.getKeyStroke(SHORTCUT), null)) + ")", "Check current task", InteractiveLearningIcons.Resolve);
}
- private static void flushWindows(@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;
- }
- EduUtils.flushWindows(taskFile, virtualFile, true);
- }
- }
-
- private 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);
- }
- }
- }
-
-
- protected void check(@NotNull final Project project) {
- if (DumbService.isDumb(project)) {
- DumbService.getInstance(project).showDumbModeNotification("Check Action is not available while indexing is in progress");
- return;
- }
- ApplicationManager.getApplication().runWriteAction(new Runnable() {
- @Override
- public void run() {
- CommandProcessor.getInstance().runUndoTransparentAction(() -> {
- final StudyEditor selectedEditor = StudyUtils.getSelectedStudyEditor(project);
- if (selectedEditor == null) return;
- final StudyState studyState = new StudyState(selectedEditor);
- if (!studyState.isValid()) {
- LOG.error("StudyCheckAction was invoked outside study editor");
- return;
- }
- final IdeFrame frame = ((WindowManagerEx)WindowManager.getInstance()).findFrameFor(project);
- final StatusBarEx statusBar = frame == null ? null : (StatusBarEx)frame.getStatusBar();
- if (statusBar != null) {
- final List<Pair<TaskInfo, ProgressIndicator>> processes = statusBar.getBackgroundProcesses();
- if (!processes.isEmpty()) return;
- }
-
- final Task task = studyState.getTask();
- final VirtualFile taskDir = studyState.getTaskDir();
- flushWindows(task, taskDir);
- final StudyRunAction runAction = (StudyRunAction)ActionManager.getInstance().getAction(StudyRunAction.ACTION_ID);
- if (runAction == null) {
- return;
- }
- runAction.run(project);
- ApplicationManager.getApplication().invokeLater(
- () -> IdeFocusManager.getInstance(project).requestFocus(studyState.getEditor().getComponent(), true));
-
- final StudyTestRunner testRunner = StudyUtils.getTestRunner(task, taskDir);
- Process testProcess = null;
- String commandLine = "";
- try {
- final VirtualFile executablePath = getTaskVirtualFile(studyState, task, taskDir);
- if (executablePath != null) {
- commandLine = executablePath.getPath();
- testProcess = testRunner.createCheckProcess(project, commandLine);
- }
- }
- catch (ExecutionException e) {
- LOG.error(e);
- }
- if (testProcess == null) {
- return;
- }
- checkInProgress = true;
- ProgressManager.getInstance().run(getCheckTask(studyState, testRunner, testProcess, commandLine, project, selectedEditor));
- });
- }
-
- @Nullable
- private VirtualFile getTaskVirtualFile(@NotNull final StudyState studyState,
- @NotNull final Task task,
- @NotNull final VirtualFile taskDir) {
- VirtualFile taskVirtualFile = studyState.getVirtualFile();
- 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) {
- if (!taskFile.getAnswerPlaceholders().isEmpty()) {
- taskVirtualFile = virtualFile;
- }
- }
- }
- return taskVirtualFile;
- }
- });
- }
-
- @NotNull
- protected com.intellij.openapi.progress.Task.Backgroundable getCheckTask(final StudyState studyState,
- final StudyTestRunner testRunner,
- final Process testProcess,
- @NotNull final String commandLine, @NotNull final Project project,
- final StudyEditor selectedEditor) {
- final Task task = studyState.getTask();
- final VirtualFile taskDir = studyState.getTaskDir();
-
- final StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
- final StudyStatus statusBeforeCheck = taskManager.getStatus(task);
- return new com.intellij.openapi.progress.Task.Backgroundable(project, "Checking Task", true) {
- @Override
- public void onSuccess() {
- StudyUtils.updateToolWindows(project);
- drawAllPlaceholders(project, task, taskDir);
- ProjectView.getInstance(project).refresh();
- EduUtils.deleteWindowDescriptions(task, taskDir);
- checkInProgress = false;
- }
-
- @Override
- public void onCancel() {
- taskManager.setStatus(task, statusBeforeCheck);
- EduUtils.deleteWindowDescriptions(task, taskDir);
- checkInProgress = false;
- }
-
- @Override
- public void run(@NotNull ProgressIndicator indicator) {
- final Map<String, TaskFile> taskFiles = task.getTaskFiles();
- final CapturingProcessHandler handler = new CapturingProcessHandler(testProcess, null, commandLine);
- final ProcessOutput output = handler.runProcessWithProgressIndicator(indicator);
- if (indicator.isCanceled()) {
- ApplicationManager.getApplication().invokeLater(
- () -> showTestResultPopUp("Tests check cancelled.", MessageType.WARNING.getPopupBackground(), project));
- return;
- }
- final StudyTestRunner.TestsOutput testsOutput = testRunner.getTestsOutput(output);
- String stderr = output.getStderr();
- if (!stderr.isEmpty()) {
- LOG.info("#educational " + stderr);
- }
- final StudySettings studySettings = StudySettings.getInstance();
-
- final String login = studySettings.getLogin();
- final String password = StringUtil.isEmptyOrSpaces(login) ? "" : studySettings.getPassword();
- if (testsOutput.isSuccess()) {
- taskManager.setStatus(task, StudyStatus.Solved);
- EduStepicConnector.postAttempt(task, true, login, password);
- ApplicationManager.getApplication().invokeLater(
- () -> showTestResultPopUp(testsOutput.getMessage(), MessageType.INFO.getPopupBackground(), project));
- }
- else {
- ApplicationManager.getApplication().invokeLater(() -> {
- if (taskDir == null) return;
- EduStepicConnector.postAttempt(task, false, login, password);
- taskManager.setStatus(task, StudyStatus.Failed);
- for (Map.Entry<String, TaskFile> entry : taskFiles.entrySet()) {
- final String name = entry.getKey();
- final TaskFile taskFile = entry.getValue();
- if (taskFile.getAnswerPlaceholders().size() < 2) {
- taskManager.setStatus(taskFile, StudyStatus.Failed);
- continue;
- }
- CommandProcessor.getInstance().runUndoTransparentAction(() -> ApplicationManager.getApplication().runWriteAction(() -> {
- runSmartTestProcess(taskDir, testRunner, name, taskFile, project);
- }));
- }
- showTestResultPopUp(testsOutput.getMessage(), MessageType.ERROR.getPopupBackground(), project);
- navigateToFailedPlaceholder(studyState, task, taskDir, project);
- });
- }
- }
- };
- }
-
- private static void navigateToFailedPlaceholder(@NotNull final StudyState studyState,
- @NotNull final Task task,
- @NotNull final VirtualFile taskDir,
- @NotNull final Project project) {
- TaskFile selectedTaskFile = studyState.getTaskFile();
- Editor editor = studyState.getEditor();
- TaskFile taskFileToNavigate = selectedTaskFile;
- VirtualFile fileToNavigate = studyState.getVirtualFile();
- final StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
- if (!taskManager.hasFailedAnswerPlaceholders(selectedTaskFile)) {
- for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
- String name = entry.getKey();
- TaskFile taskFile = entry.getValue();
- if (taskManager.hasFailedAnswerPlaceholders(taskFile)) {
- taskFileToNavigate = taskFile;
- VirtualFile virtualFile = taskDir.findChild(name);
- if (virtualFile == null) {
- continue;
- }
- FileEditor fileEditor = FileEditorManager.getInstance(project).getSelectedEditor(virtualFile);
- if (fileEditor instanceof StudyEditor) {
- StudyEditor studyEditor = (StudyEditor)fileEditor;
- editor = studyEditor.getEditor();
- }
- fileToNavigate = virtualFile;
- break;
- }
- }
- }
- if (fileToNavigate != null) {
- FileEditorManager.getInstance(project).openFile(fileToNavigate, true);
- }
- final Editor editorToNavigate = editor;
- ApplicationManager.getApplication().invokeLater(
- () -> IdeFocusManager.getInstance(project).requestFocus(editorToNavigate.getContentComponent(), true));
-
- StudyNavigator.navigateToFirstFailedAnswerPlaceholder(editor, taskFileToNavigate);
- }
-
- protected void runSmartTestProcess(@NotNull final VirtualFile taskDir,
- @NotNull final StudyTestRunner testRunner,
- 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);
- for (final AnswerPlaceholder answerPlaceholder : answerTaskFile.getAnswerPlaceholders()) {
- final Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
- if (document == null) {
- continue;
- }
- if (!answerPlaceholder.isValid(document)) {
- continue;
- }
- StudySmartChecker.smartCheck(answerPlaceholder, project, answerFile, answerTaskFile, taskFile, testRunner,
- virtualFile, document);
- }
- StudyUtils.deleteFile(answerFile);
- }
-
- private VirtualFile getCopyWithAnswers(@NotNull final VirtualFile taskDir,
- @NotNull final VirtualFile file,
- @NotNull final TaskFile source,
- @NotNull final TaskFile target) {
- VirtualFile copy = null;
- try {
-
- copy = file.copy(this, taskDir, file.getNameWithoutExtension() + ANSWERS_POSTFIX + "." + file.getExtension());
- final FileDocumentManager documentManager = FileDocumentManager.getInstance();
- final Document document = documentManager.getDocument(copy);
- if (document != null) {
- TaskFile.copy(source, target);
- EduDocumentListener listener = new EduDocumentListener(target);
- document.addDocumentListener(listener);
- for (AnswerPlaceholder answerPlaceholder : target.getAnswerPlaceholders()) {
- if (!answerPlaceholder.isValid(document)) {
- continue;
- }
- final int start = answerPlaceholder.getRealStartOffset(document);
- final int end = start + answerPlaceholder.getLength();
- final String text = answerPlaceholder.getPossibleAnswer();
- document.replaceString(start, end, text);
- }
- ApplicationManager.getApplication().runWriteAction(() -> {
- documentManager.saveDocument(document);
- });
- }
- }
- catch (IOException e) {
- LOG.error(e);
- }
- return copy;
- }
-
- protected static void showTestResultPopUp(final String text, Color color, @NotNull final Project project) {
- BalloonBuilder balloonBuilder =
- JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(text, null, color, null);
- final Balloon balloon = balloonBuilder.createBalloon();
- StudyUtils.showCheckPopUp(project, balloon);
- }
+ protected void check(@NotNull final Project project) {}
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Project project = e.getProject();
- if (project != null) {
- check(project);
+ if (project == null) {
+ return;
+ }
+ if (DumbService.isDumb(project)) {
+ StudyCheckUtils.showTestResultPopUp("Checking is not available while indexing is in progress", MessageType.WARNING.getPopupBackground(), project);
+ return;
}
+ check(project);
}
@Override
final Presentation presentation = e.getPresentation();
StudyUtils.updateAction(e);
if (presentation.isEnabled()) {
- presentation.setEnabled(!checkInProgress);
+ presentation.setEnabled(!myCheckInProgress.get());
}
}
}
--- /dev/null
+package com.jetbrains.edu.learning.checker;
+
+import com.intellij.execution.process.CapturingProcessHandler;
+import com.intellij.execution.process.ProcessOutput;
+import com.intellij.ide.projectView.ProjectView;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.MessageType;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.jetbrains.edu.EduUtils;
+import com.jetbrains.edu.courseFormat.StudyStatus;
+import com.jetbrains.edu.courseFormat.Task;
+import com.jetbrains.edu.learning.StudyState;
+import com.jetbrains.edu.learning.StudyTaskManager;
+import com.jetbrains.edu.learning.StudyUtils;
+import com.jetbrains.edu.stepic.EduStepicConnector;
+import com.jetbrains.edu.stepic.StudySettings;
+import org.jetbrains.annotations.NotNull;
+
+public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroundable {
+
+ private static final Logger LOG = Logger.getInstance(StudyCheckTask.class);
+ private final Project myProject;
+ protected final StudyState myStudyState;
+ protected final Task myTask;
+ protected final VirtualFile myTaskDir;
+ protected final StudyTaskManager myTaskManger;
+ private final StudyStatus myStatusBeforeCheck;
+ private Ref<Boolean> myCheckInProcess;
+ private final Process myTestProcess;
+ private final String myCommandLine;
+
+ public StudyCheckTask(Project project, StudyState studyState, Ref<Boolean> checkInProcess, Process testProcess, String commandLine) {
+ super(project, "Checking Task");
+ myProject = project;
+ myStudyState = studyState;
+ myCheckInProcess = checkInProcess;
+ myTestProcess = testProcess;
+ myCommandLine = commandLine;
+ myTask = studyState.getTask();
+ myTaskDir = studyState.getTaskDir();
+ myTaskManger = StudyTaskManager.getInstance(myProject);
+ myStatusBeforeCheck = myTaskManger.getStatus(myTask);
+ }
+
+ @Override
+ public void onSuccess() {
+ StudyUtils.updateToolWindows(myProject);
+ StudyCheckUtils.drawAllPlaceholders(myProject, myTask, myTaskDir);
+ ProjectView.getInstance(myProject).refresh();
+ clearState();
+ }
+
+ protected void clearState() {
+ EduUtils.deleteWindowDescriptions(myTask, myTaskDir);
+ myCheckInProcess.set(false);
+ }
+
+ @Override
+ public void onCancel() {
+ myTaskManger.setStatus(myTask, myStatusBeforeCheck);
+ clearState();
+ }
+
+ @Override
+ public void run(@NotNull ProgressIndicator indicator) {
+ final CapturingProcessHandler handler = new CapturingProcessHandler(myTestProcess, null, myCommandLine);
+ final ProcessOutput output = handler.runProcessWithProgressIndicator(indicator);
+ if (indicator.isCanceled()) {
+ ApplicationManager.getApplication().invokeLater(
+ () -> StudyCheckUtils.showTestResultPopUp("Check cancelled", MessageType.WARNING.getPopupBackground(), myProject));
+ return;
+ }
+
+
+ final StudyTestsOutputParser.TestsOutput testsOutput = StudyTestsOutputParser.getTestsOutput(output);
+ String stderr = output.getStderr();
+ if (!stderr.isEmpty()) {
+ ApplicationManager.getApplication().invokeLater(() ->
+ StudyCheckUtils.showTestResultPopUp("Failed to launch checking",
+ MessageType.WARNING.getPopupBackground(),
+ myProject));
+ //log error output of tests
+ LOG.info("#educational " + stderr);
+ return;
+ }
+
+ postAttemptToStepic(testsOutput);
+
+
+ if (testsOutput.isSuccess()) {
+ onTaskSolved(testsOutput);
+ }
+ else {
+ onTaskFailed(testsOutput);
+ }
+ }
+
+ protected void onTaskFailed(StudyTestsOutputParser.TestsOutput testsOutput) {
+ myTaskManger.setStatus(myTask, StudyStatus.Failed);
+ ApplicationManager.getApplication().invokeLater(
+ () -> StudyCheckUtils.showTestResultPopUp(testsOutput.getMessage(), MessageType.ERROR.getPopupBackground(), myProject));
+ }
+
+ protected void onTaskSolved(StudyTestsOutputParser.TestsOutput testsOutput) {
+ myTaskManger.setStatus(myTask, StudyStatus.Solved);
+ ApplicationManager.getApplication().invokeLater(
+ () -> StudyCheckUtils.showTestResultPopUp(testsOutput.getMessage(), MessageType.INFO.getPopupBackground(), myProject));
+ }
+
+ protected void postAttemptToStepic(StudyTestsOutputParser.TestsOutput testsOutput) {
+ final StudySettings studySettings = StudySettings.getInstance();
+ final String login = studySettings.getLogin();
+ final String password = StringUtil.isEmptyOrSpaces(login) ? "" : studySettings.getPassword();
+ EduStepicConnector.postAttempt(myTask, testsOutput.isSuccess(), login, password);
+ }
+}
--- /dev/null
+package com.jetbrains.edu.learning.checker;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.TaskInfo;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.popup.Balloon;
+import com.intellij.openapi.ui.popup.BalloonBuilder;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.wm.IdeFocusManager;
+import com.intellij.openapi.wm.IdeFrame;
+import com.intellij.openapi.wm.WindowManager;
+import com.intellij.openapi.wm.ex.StatusBarEx;
+import com.intellij.openapi.wm.ex.WindowManagerEx;
+import com.jetbrains.edu.EduDocumentListener;
+import com.jetbrains.edu.EduUtils;
+import com.jetbrains.edu.courseFormat.AnswerPlaceholder;
+import com.jetbrains.edu.courseFormat.Task;
+import com.jetbrains.edu.courseFormat.TaskFile;
+import com.jetbrains.edu.learning.StudyState;
+import com.jetbrains.edu.learning.StudyTaskManager;
+import com.jetbrains.edu.learning.StudyUtils;
+import com.jetbrains.edu.learning.editor.StudyEditor;
+import com.jetbrains.edu.learning.navigation.StudyNavigator;
+import org.jetbrains.annotations.NotNull;
+
+import java.awt.*;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+public class StudyCheckUtils {
+ private static final String ANSWERS_POSTFIX = "_answers";
+ private static final Logger LOG = Logger.getInstance(StudyCheckUtils.class);
+
+ private StudyCheckUtils() {
+ }
+
+ 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);
+ }
+ }
+ }
+
+ public static void navigateToFailedPlaceholder(@NotNull final StudyState studyState,
+ @NotNull final Task task,
+ @NotNull final VirtualFile taskDir,
+ @NotNull final Project project) {
+ TaskFile selectedTaskFile = studyState.getTaskFile();
+ Editor editor = studyState.getEditor();
+ TaskFile taskFileToNavigate = selectedTaskFile;
+ VirtualFile fileToNavigate = studyState.getVirtualFile();
+ final StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
+ if (!taskManager.hasFailedAnswerPlaceholders(selectedTaskFile)) {
+ for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
+ String name = entry.getKey();
+ TaskFile taskFile = entry.getValue();
+ if (taskManager.hasFailedAnswerPlaceholders(taskFile)) {
+ taskFileToNavigate = taskFile;
+ VirtualFile virtualFile = taskDir.findChild(name);
+ if (virtualFile == null) {
+ continue;
+ }
+ FileEditor fileEditor = FileEditorManager.getInstance(project).getSelectedEditor(virtualFile);
+ if (fileEditor instanceof StudyEditor) {
+ StudyEditor studyEditor = (StudyEditor)fileEditor;
+ editor = studyEditor.getEditor();
+ }
+ fileToNavigate = virtualFile;
+ break;
+ }
+ }
+ }
+ if (fileToNavigate != null) {
+ FileEditorManager.getInstance(project).openFile(fileToNavigate, true);
+ }
+ final Editor editorToNavigate = editor;
+ ApplicationManager.getApplication().invokeLater(
+ () -> IdeFocusManager.getInstance(project).requestFocus(editorToNavigate.getContentComponent(), true));
+
+ StudyNavigator.navigateToFirstFailedAnswerPlaceholder(editor, taskFileToNavigate);
+ }
+
+
+ public static void showTestResultPopUp(final String text, Color color, @NotNull final Project project) {
+ BalloonBuilder balloonBuilder =
+ JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(text, null, color, null);
+ final Balloon balloon = balloonBuilder.createBalloon();
+ StudyUtils.showCheckPopUp(project, balloon);
+ }
+
+
+ public static void runSmartTestProcess(@NotNull final VirtualFile taskDir,
+ @NotNull final StudyTestRunner testRunner,
+ 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);
+ for (final AnswerPlaceholder answerPlaceholder : answerTaskFile.getAnswerPlaceholders()) {
+ final Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
+ if (document == null) {
+ continue;
+ }
+ if (!answerPlaceholder.isValid(document)) {
+ continue;
+ }
+ StudySmartChecker.smartCheck(answerPlaceholder, project, answerFile, answerTaskFile, taskFile, testRunner,
+ virtualFile, document);
+ }
+ StudyUtils.deleteFile(answerFile);
+ }
+
+
+
+ private static VirtualFile getCopyWithAnswers(@NotNull final VirtualFile taskDir,
+ @NotNull final VirtualFile file,
+ @NotNull final TaskFile source,
+ @NotNull final TaskFile target) {
+ VirtualFile copy = null;
+ try {
+
+ copy = file.copy(taskDir, taskDir, file.getNameWithoutExtension() + ANSWERS_POSTFIX + "." + file.getExtension());
+ final FileDocumentManager documentManager = FileDocumentManager.getInstance();
+ final Document document = documentManager.getDocument(copy);
+ if (document != null) {
+ TaskFile.copy(source, target);
+ EduDocumentListener listener = new EduDocumentListener(target);
+ document.addDocumentListener(listener);
+ for (AnswerPlaceholder answerPlaceholder : target.getAnswerPlaceholders()) {
+ if (!answerPlaceholder.isValid(document)) {
+ continue;
+ }
+ final int start = answerPlaceholder.getRealStartOffset(document);
+ final int end = start + answerPlaceholder.getLength();
+ final String text = answerPlaceholder.getPossibleAnswer();
+ document.replaceString(start, end, text);
+ }
+ ApplicationManager.getApplication().runWriteAction(() -> {
+ documentManager.saveDocument(document);
+ });
+ }
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ return copy;
+ }
+
+
+ public static boolean hasBackgroundProcesses(@NotNull Project project) {
+ final IdeFrame frame = ((WindowManagerEx)WindowManager.getInstance()).findFrameFor(project);
+ final StatusBarEx statusBar = frame == null ? null : (StatusBarEx)frame.getStatusBar();
+ if (statusBar != null) {
+ final List<Pair<TaskInfo, ProgressIndicator>> processes = statusBar.getBackgroundProcesses();
+ if (!processes.isEmpty()) return true;
+ }
+ return false;
+ }
+
+
+ public static void flushWindows(@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;
+ }
+ EduUtils.flushWindows(taskFile, virtualFile, true);
+ }
+ }
+}
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.jetbrains.edu.learning.run;
+package com.jetbrains.edu.learning.checker;
import com.intellij.execution.RunContentExecutor;
import com.intellij.execution.configurations.GeneralCommandLine;
-package com.jetbrains.edu.learning.run;
+package com.jetbrains.edu.learning.checker;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.process.CapturingProcessHandler;
int userEnd = userStart + userAnswerPlaceholder.getLength();
String text = usersDocument.getText(new TextRange(userStart, userEnd));
windowDocument.replaceString(start, end, text);
- ApplicationManager.getApplication().runWriteAction(new Runnable() {
- @Override
- public void run() {
- documentManager.saveDocument(windowDocument);
- }
+ ApplicationManager.getApplication().runWriteAction(() -> {
+ documentManager.saveDocument(windowDocument);
});
VirtualFile fileWindows = EduUtils.flushWindows(windowTaskFile, windowCopy, true);
Process smartTestProcess = testRunner.createCheckProcess(project, windowCopy.getPath());
final CapturingProcessHandler handler = new CapturingProcessHandler(smartTestProcess, null, windowCopy.getPath());
final ProcessOutput output = handler.runProcess();
- boolean res = testRunner.getTestsOutput(output).isSuccess();
+ boolean res = StudyTestsOutputParser.getTestsOutput(output).isSuccess();
StudyTaskManager.getInstance(project).setStatus(userAnswerPlaceholder, res ? StudyStatus.Solved : StudyStatus.Failed);
StudyUtils.deleteFile(windowCopy);
if (fileWindows != null) {
}
}
}
- catch (ExecutionException e) {
- LOG.error(e);
- }
- catch (IOException e) {
+ catch (ExecutionException | IOException e) {
LOG.error(e);
}
}
--- /dev/null
+package com.jetbrains.edu.learning.checker;
+
+import com.intellij.execution.ExecutionException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.jetbrains.edu.courseFormat.Task;
+import org.jetbrains.annotations.NotNull;
+
+public abstract class StudyTestRunner {protected final Task myTask;
+ protected final VirtualFile myTaskDir;
+
+ public StudyTestRunner(@NotNull final Task task, @NotNull final VirtualFile taskDir) {
+ myTask = task;
+ myTaskDir = taskDir;
+ }
+
+ public abstract Process createCheckProcess(@NotNull final Project project, @NotNull final String executablePath) throws ExecutionException;
+
+}
-package com.jetbrains.edu.learning.run;
+package com.jetbrains.edu.learning.checker;
-import com.intellij.execution.ExecutionException;
import com.intellij.execution.process.ProcessOutput;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.jetbrains.edu.courseFormat.Task;
import org.jetbrains.annotations.NotNull;
-public abstract class StudyTestRunner {
+public class StudyTestsOutputParser {
private static final String ourStudyPrefix = "#educational_plugin";
public static final String TEST_OK = "test OK";
private static final String TEST_FAILED = "FAILED + ";
private static final String CONGRATS_MESSAGE = "CONGRATS_MESSAGE ";
- protected final Task myTask;
- protected final VirtualFile myTaskDir;
+ public static final String CONGRATULATIONS = "Congratulations!";
- public StudyTestRunner(@NotNull final Task task, @NotNull final VirtualFile taskDir) {
- myTask = task;
- myTaskDir = taskDir;
- }
+ public static class TestsOutput {
+ private final boolean isSuccess;
+ private final String myMessage;
+
+ public TestsOutput(boolean isSuccess, @NotNull final String message) {
+ this.isSuccess = isSuccess;
+ myMessage = message;
+ }
- public abstract Process createCheckProcess(@NotNull final Project project, @NotNull final String executablePath) throws ExecutionException;
+ public boolean isSuccess() {
+ return isSuccess;
+ }
+
+ public String getMessage() {
+ return myMessage;
+ }
+ }
@NotNull
- public TestsOutput getTestsOutput(@NotNull final ProcessOutput processOutput) {
- String congratulations = "Congratulations!";
+ public static TestsOutput getTestsOutput(@NotNull final ProcessOutput processOutput) {
+ String congratulations = CONGRATULATIONS;
for (String line : processOutput.getStdoutLines()) {
if (line.startsWith(ourStudyPrefix)) {
if (line.contains(TEST_OK)) {
return new TestsOutput(true, congratulations);
}
-
- public static class TestsOutput {
- private final boolean isSuccess;
- private final String myMessage;
-
- public TestsOutput(boolean isSuccess, @NotNull final String message) {
- this.isSuccess = isSuccess;
- myMessage = message;
- }
-
- public boolean isSuccess() {
- return isSuccess;
- }
-
- public String getMessage() {
- return myMessage;
- }
- }
-
}
--- /dev/null
+package com.jetbrains.edu.learning;
+
+import com.intellij.execution.ExecutionException;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.command.CommandProcessor;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.MessageType;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.wm.IdeFocusManager;
+import com.jetbrains.edu.courseFormat.StudyStatus;
+import com.jetbrains.edu.courseFormat.Task;
+import com.jetbrains.edu.courseFormat.TaskFile;
+import com.jetbrains.edu.learning.actions.StudyCheckAction;
+import com.jetbrains.edu.learning.actions.StudyRunAction;
+import com.jetbrains.edu.learning.checker.StudyCheckTask;
+import com.jetbrains.edu.learning.checker.StudyCheckUtils;
+import com.jetbrains.edu.learning.checker.StudyTestsOutputParser;
+import com.jetbrains.edu.learning.editor.StudyEditor;
+import com.jetbrains.edu.learning.checker.StudyTestRunner;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Map;
+
+public class PyStudyCheckAction extends StudyCheckAction {
+ private static final Logger LOG = Logger.getInstance(PyStudyCheckAction.class);
+
+ @Override
+ protected void check(@NotNull Project project) {
+ ApplicationManager.getApplication().runWriteAction(() -> {
+ CommandProcessor.getInstance().runUndoTransparentAction(() -> {
+ final StudyEditor selectedEditor = StudyUtils.getSelectedStudyEditor(project);
+ if (selectedEditor == null) return;
+ final StudyState studyState = new StudyState(selectedEditor);
+ if (!studyState.isValid()) {
+ LOG.info("StudyCheckAction was invoked outside study editor");
+ return;
+ }
+ if (StudyCheckUtils.hasBackgroundProcesses(project)) return;
+
+
+ if (!runTask(project)) return;
+
+ final Task task = studyState.getTask();
+ final VirtualFile taskDir = studyState.getTaskDir();
+ StudyCheckUtils.flushWindows(task, taskDir);
+
+
+ ApplicationManager.getApplication().invokeLater(
+ () -> IdeFocusManager.getInstance(project).requestFocus(studyState.getEditor().getComponent(), true));
+
+ final StudyTestRunner testRunner = StudyUtils.getTestRunner(task, taskDir);
+ Process testProcess = null;
+ String commandLine = "";
+ try {
+ final VirtualFile executablePath = getTaskVirtualFile(studyState, task, taskDir);
+ if (executablePath != null) {
+ commandLine = executablePath.getPath();
+ testProcess = testRunner.createCheckProcess(project, commandLine);
+ }
+ }
+ catch (ExecutionException e) {
+ LOG.error(e);
+ }
+ if (testProcess == null) {
+ return;
+ }
+ myCheckInProgress.set(true);
+ StudyCheckTask checkTask = getCheckTask(project, studyState, testRunner, testProcess, commandLine);
+ ProgressManager.getInstance().run(checkTask);
+ });
+ });
+ }
+
+ private static boolean runTask(@NotNull Project project) {
+ final StudyRunAction runAction = (StudyRunAction)ActionManager.getInstance().getAction(StudyRunAction.ACTION_ID);
+ if (runAction == null) {
+ return false;
+ }
+ runAction.run(project);
+ return true;
+ }
+
+ @NotNull
+ private StudyCheckTask getCheckTask(@NotNull final Project project,
+ final StudyState studyState,
+ final StudyTestRunner testRunner,
+ final Process testProcess,
+ final String commandLine) {
+ return new StudyCheckTask(project, studyState, myCheckInProgress, testProcess, commandLine) {
+ @Override
+ protected void onTaskFailed(StudyTestsOutputParser.TestsOutput testsOutput) {
+ ApplicationManager.getApplication().invokeLater(() -> {
+ if (myTaskDir == null) return;
+ myTaskManger.setStatus(myTask, StudyStatus.Failed);
+ for (Map.Entry<String, TaskFile> entry : myTask.getTaskFiles().entrySet()) {
+ final String name = entry.getKey();
+ final TaskFile taskFile = entry.getValue();
+ if (taskFile.getAnswerPlaceholders().size() < 2) {
+ myTaskManger.setStatus(taskFile, StudyStatus.Failed);
+ continue;
+ }
+ CommandProcessor.getInstance().runUndoTransparentAction(() -> ApplicationManager.getApplication().runWriteAction(() -> {
+ StudyCheckUtils.runSmartTestProcess(myTaskDir, testRunner, name, taskFile, project);
+ }));
+ }
+ StudyCheckUtils.showTestResultPopUp(testsOutput.getMessage(), MessageType.ERROR.getPopupBackground(), project);
+ StudyCheckUtils.navigateToFailedPlaceholder(myStudyState, myTask, myTaskDir, project);
+ });
+ }
+ };
+ }
+
+
+ @Nullable
+ private static VirtualFile getTaskVirtualFile(@NotNull final StudyState studyState,
+ @NotNull final Task task,
+ @NotNull final VirtualFile taskDir) {
+ VirtualFile taskVirtualFile = studyState.getVirtualFile();
+ 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) {
+ if (!taskFile.getAnswerPlaceholders().isEmpty()) {
+ taskVirtualFile = virtualFile;
+ }
+ }
+ }
+ return taskVirtualFile;
+ }
+}
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.edu.courseFormat.Task;
+import com.jetbrains.edu.learning.checker.StudyExecutor;
+import com.jetbrains.edu.learning.checker.StudyTestRunner;
import com.jetbrains.edu.learning.courseFormat.UserTest;
-import com.jetbrains.edu.learning.run.StudyExecutor;
-import com.jetbrains.edu.learning.run.StudyTestRunner;
import com.jetbrains.python.run.PythonTracebackFilter;
import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
import javax.swing.event.HyperlinkEvent;
-import javax.swing.event.HyperlinkListener;
import java.io.File;
import java.util.List;
final BalloonBuilder balloonBuilder = JBPopupFactory.getInstance().
createHtmlTextBalloonBuilder(text, null,
MessageType.WARNING.getPopupBackground(),
- new HyperlinkListener() {
- @Override
- public void hyperlinkUpdate(HyperlinkEvent event) {
- if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
- ApplicationManager.getApplication()
- .invokeLater(new Runnable() {
- @Override
- public void run() {
- ShowSettingsUtil.getInstance().showSettingsDialog(project, "Project Interpreter");
- }
- });
- }
+ event -> {
+ if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
+ ApplicationManager.getApplication()
+ .invokeLater(
+ () -> ShowSettingsUtil.getInstance().showSettingsDialog(project, "Project Interpreter"));
}
});
balloonBuilder.setHideOnLinkClick(true);
import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.edu.courseFormat.Course;
import com.jetbrains.edu.courseFormat.Task;
-import com.jetbrains.edu.learning.run.StudyTestRunner;
+import com.jetbrains.edu.learning.checker.StudyTestRunner;
import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
@Override
public DefaultActionGroup getActionGroup(Project project) {
final DefaultActionGroup group = new DefaultActionGroup();
- group.add(new StudyCheckAction());
+ group.add(new PyStudyCheckAction());
group.add(new StudyPreviousStudyTaskAction());
group.add(new StudyNextStudyTaskAction());
group.add(new StudyRefreshTaskFileAction());