From c3d3a90f6b4ead083d63e28e6b9fcea93d675678 Mon Sep 17 00:00:00 2001 From: Tor Norbye Date: Thu, 4 Sep 2014 13:24:04 -0700 Subject: Snapshot idea/138.1980 from git://git.jetbrains.org/idea/community.git Change-Id: Ib567c9c152d770212a7a3db20fbf591c210920bd --- .../python/edu/StudyDirectoryProjectGenerator.java | 4 +- .../python/edu/StudyDocumentListener.java | 3 + .../python/edu/StudyInstructionPainter.java | 5 - .../src/com/jetbrains/python/edu/StudyState.java | 52 +++ .../com/jetbrains/python/edu/StudyTaskManager.java | 24 +- .../com/jetbrains/python/edu/StudyTestRunner.java | 76 +++++ .../src/com/jetbrains/python/edu/StudyUtils.java | 44 ++- .../python/edu/actions/StudyCheckAction.java | 378 +++++++++------------ .../python/edu/actions/StudyEditInputAction.java | 4 +- .../python/edu/actions/StudyNewProject.java | 34 ++ .../edu/actions/StudyNextStudyTaskAction.java | 5 +- .../python/edu/actions/StudyNextWindowAction.java | 4 +- .../edu/actions/StudyPreviousStudyTaskAction.java | 5 +- .../python/edu/actions/StudyRefreshTaskAction.java | 122 ------- .../edu/actions/StudyRefreshTaskFileAction.java | 129 +++++++ .../python/edu/actions/StudyShowHintAction.java | 103 +++--- .../edu/actions/StudyTaskNavigationAction.java | 49 +-- .../com/jetbrains/python/edu/course/TaskFile.java | 48 ++- .../jetbrains/python/edu/course/TaskWindow.java | 67 +++- .../jetbrains/python/edu/editor/StudyEditor.java | 96 +++++- .../edu/highlighting/StudyVisitorFilter.java | 18 + .../python/edu/projectView/StudyDirectoryNode.java | 11 +- .../python/edu/ui/StudyNewProjectPanel.form | 10 +- .../python/edu/ui/StudyNewProjectPanel.java | 7 +- 24 files changed, 812 insertions(+), 486 deletions(-) create mode 100644 python/edu/learn-python/src/com/jetbrains/python/edu/StudyState.java create mode 100644 python/edu/learn-python/src/com/jetbrains/python/edu/StudyTestRunner.java create mode 100644 python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNewProject.java delete mode 100644 python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskAction.java create mode 100644 python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskFileAction.java create mode 100644 python/edu/learn-python/src/com/jetbrains/python/edu/highlighting/StudyVisitorFilter.java (limited to 'python/edu/learn-python/src') diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDirectoryProjectGenerator.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDirectoryProjectGenerator.java index d4831d93e367..59bd8bc2ea7c 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDirectoryProjectGenerator.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDirectoryProjectGenerator.java @@ -53,7 +53,7 @@ public class StudyDirectoryProjectGenerator extends PythonProjectGenerator imple @NotNull @Override public String getName() { - return "Study project"; + return "Learn Python"; } @@ -137,7 +137,7 @@ public class StudyDirectoryProjectGenerator extends PythonProjectGenerator imple @Nullable @Override public Icon getLogo() { - return StudyIcons.Playground; + return StudyIcons.EducationalProjectType; } diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDocumentListener.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDocumentListener.java index 9fdcf704a29b..6ce1d0991dff 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDocumentListener.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDocumentListener.java @@ -52,6 +52,9 @@ public class StudyDocumentListener extends DocumentAdapter { if (myTaskWindow != null) { int newLength = myTaskWindow.getLength() + change; myTaskWindow.setLength(newLength <= 0 ? 0 : newLength); + if (e.getNewFragment().equals("\n")) { + myTaskWindow.setLength(myTaskWindow.getLength() + 1); + } } int newEnd = offset + event.getNewLength(); int newLine = document.getLineNumber(newEnd); diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyInstructionPainter.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyInstructionPainter.java index 4f34bfb92fab..96a44b2ee66a 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyInstructionPainter.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyInstructionPainter.java @@ -8,7 +8,6 @@ import com.intellij.ui.JBColor; import com.intellij.util.PairFunction; import com.intellij.util.ui.GraphicsUtil; import com.intellij.util.ui.UIUtil; -import com.jetbrains.python.edu.ui.StudyCondition; import java.awt.*; @@ -19,10 +18,6 @@ import java.awt.*; public class StudyInstructionPainter extends EditorEmptyTextPainter { @Override public void paintEmptyText(final EditorsSplitters splitters, Graphics g) { - if (!StudyCondition.VALUE) { - super.paintEmptyText(splitters, g); - return; - } boolean isDarkBackground = UIUtil.isUnderDarcula(); UIUtil.applyRenderingHints(g); GraphicsUtil.setupAntialiasing(g, true, false); diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyState.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyState.java new file mode 100644 index 000000000000..96dc3d906472 --- /dev/null +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyState.java @@ -0,0 +1,52 @@ +package com.jetbrains.python.edu; + +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.python.edu.course.Task; +import com.jetbrains.python.edu.course.TaskFile; +import com.jetbrains.python.edu.editor.StudyEditor; + +public class StudyState { + private final StudyEditor myStudyEditor; + private final Editor myEditor; + private final TaskFile myTaskFile; + private final VirtualFile myVirtualFile; + private final Task myTask; + private final VirtualFile myTaskDir; + + public StudyState(final StudyEditor studyEditor) { + myStudyEditor = studyEditor; + myEditor = studyEditor != null ? studyEditor.getEditor() : null; + myTaskFile = studyEditor != null ? studyEditor.getTaskFile() : null; + myVirtualFile = myEditor != null ? FileDocumentManager.getInstance().getFile(myEditor.getDocument()) : null; + myTaskDir = myVirtualFile != null ? myVirtualFile.getParent() : null; + myTask = myTaskFile != null ? myTaskFile.getTask() : null; + } + + public Editor getEditor() { + return myEditor; + } + + public TaskFile getTaskFile() { + return myTaskFile; + } + + public VirtualFile getVirtualFile() { + return myVirtualFile; + } + + public Task getTask() { + return myTask; + } + + public VirtualFile getTaskDir() { + return myTaskDir; + } + + public boolean isValid() { + return myStudyEditor != null && myEditor != null && + myTaskFile != null && myVirtualFile != null && + myTask != null && myTaskDir != null; + } +} diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyTaskManager.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyTaskManager.java index 213c1f7601f0..3013fbcb05d3 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyTaskManager.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyTaskManager.java @@ -109,21 +109,29 @@ public class StudyTaskManager implements ProjectComponent, PersistentStateCompon StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new Runnable() { @Override public void run() { - ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PROJECT_VIEW).show(null); - FileEditor[] editors = FileEditorManager.getInstance(myProject).getSelectedEditors(); - if (editors.length > 0) { - JComponent focusedComponent = editors[0].getPreferredFocusedComponent(); - if (focusedComponent != null) { - IdeFocusManager.getInstance(myProject).requestFocus(focusedComponent, true); + ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PROJECT_VIEW).show(new Runnable() { + @Override + public void run() { + FileEditor[] editors = FileEditorManager.getInstance(myProject).getSelectedEditors(); + if (editors.length > 0) { + final JComponent focusedComponent = editors[0].getPreferredFocusedComponent(); + if (focusedComponent != null) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + IdeFocusManager.getInstance(myProject).requestFocus(focusedComponent, true); + } + }); + } + } } - } + }); } }); UISettings.getInstance().HIDE_TOOL_STRIPES = false; UISettings.getInstance().fireUISettingsChanged(); ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject); String toolWindowId = StudyToolWindowFactory.STUDY_TOOL_WINDOW; - //TODO:decide smth with tool window position try { Method method = toolWindowManager.getClass().getDeclaredMethod("registerToolWindow", String.class, JComponent.class, diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyTestRunner.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyTestRunner.java new file mode 100644 index 000000000000..b0cd5ba89fed --- /dev/null +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyTestRunner.java @@ -0,0 +1,76 @@ +package com.jetbrains.python.edu; + +import com.intellij.execution.ExecutionException; +import com.intellij.execution.configurations.GeneralCommandLine; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.python.edu.course.Course; +import com.jetbrains.python.edu.course.Task; +import com.jetbrains.python.sdk.PythonSdkType; + +import java.io.*; +import java.util.Map; + +public class StudyTestRunner { + public static final String TEST_OK = "#study_plugin test OK"; + private static final String TEST_FAILED = "#study_plugin FAILED + "; + private static final String PYTHONPATH = "PYTHONPATH"; + private static final Logger LOG = Logger.getInstance(StudyTestRunner.class); + private final Task myTask; + private final VirtualFile myTaskDir; + + public StudyTestRunner(Task task, VirtualFile taskDir) { + myTask = task; + myTaskDir = taskDir; + } + + public Process launchTests(Project project, String executablePath) throws ExecutionException { + Sdk sdk = PythonSdkType.findPythonSdk(ModuleManager.getInstance(project).getModules()[0]); + File testRunner = new File(myTaskDir.getPath(), myTask.getTestFile()); + GeneralCommandLine commandLine = new GeneralCommandLine(); + commandLine.setWorkDirectory(myTaskDir.getPath()); + final Map env = commandLine.getEnvironment(); + final VirtualFile courseDir = project.getBaseDir(); + if (courseDir != null) { + env.put(PYTHONPATH, courseDir.getPath()); + } + if (sdk != null) { + String pythonPath = sdk.getHomePath(); + if (pythonPath != null) { + commandLine.setExePath(pythonPath); + commandLine.addParameter(testRunner.getPath()); + final Course course = StudyTaskManager.getInstance(project).getCourse(); + assert course != null; + commandLine.addParameter(new File(course.getResourcePath()).getParent()); + commandLine.addParameter(FileUtil.toSystemDependentName(executablePath)); + return commandLine.createProcess(); + } + } + return null; + } + + + public String getPassedTests(Process p) { + InputStream testOutput = p.getInputStream(); + BufferedReader testOutputReader = new BufferedReader(new InputStreamReader(testOutput)); + String line; + try { + while ((line = testOutputReader.readLine()) != null) { + if (line.contains(TEST_FAILED)) { + return line.substring(TEST_FAILED.length(), line.length()); + } + } + } + catch (IOException e) { + LOG.error(e); + } + finally { + StudyUtils.closeSilently(testOutputReader); + } + return TEST_OK; + } +} diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyUtils.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyUtils.java index d3ac1dadf98e..5d9bb139db09 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyUtils.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyUtils.java @@ -3,6 +3,7 @@ package com.jetbrains.python.edu; import com.intellij.ide.SaveAndSyncHandlerImpl; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; @@ -10,12 +11,12 @@ import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.util.ui.UIUtil; -import com.jetbrains.python.edu.course.TaskFile; -import com.jetbrains.python.edu.course.TaskWindow; +import com.jetbrains.python.edu.course.*; import com.jetbrains.python.edu.editor.StudyEditor; import com.jetbrains.python.edu.ui.StudyToolWindowFactory; import org.jetbrains.annotations.NotNull; @@ -70,7 +71,7 @@ public class StudyUtils { return wrapHTML ? UIUtil.toHtml(taskText.toString()) : taskText.toString(); } catch (IOException e) { - LOG.error("Failed to get file text from file " + fileName, e); + LOG.info("Failed to get file text from file " + fileName, e); } finally { closeSilently(reader); @@ -119,14 +120,18 @@ public class StudyUtils { } @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") - public static VirtualFile flushWindows(Document document, TaskFile taskFile, VirtualFile file) { + public static VirtualFile flushWindows(TaskFile taskFile, VirtualFile file) { VirtualFile taskDir = file.getParent(); VirtualFile fileWindows = null; + final Document document = FileDocumentManager.getInstance().getDocument(file); + if (document == null) { + LOG.debug("Couldn't flush windows"); + return null; + } if (taskDir != null) { String name = file.getNameWithoutExtension() + "_windows"; PrintWriter printWriter = null; try { - fileWindows = taskDir.createChildData(taskFile, name); printWriter = new PrintWriter(new FileOutputStream(fileWindows.getPath())); for (TaskWindow taskWindow : taskFile.getTaskWindows()) { @@ -137,6 +142,12 @@ public class StudyUtils { String windowDescription = document.getText(new TextRange(start, start + taskWindow.getLength())); printWriter.println("#study_plugin_window = " + windowDescription); } + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + FileDocumentManager.getInstance().saveDocument(document); + } + }); } catch (IOException e) { LOG.error(e); @@ -148,4 +159,27 @@ public class StudyUtils { } return fileWindows; } + + public static void deleteFile(VirtualFile file) { + try { + file.delete(StudyUtils.class); + } + catch (IOException e) { + LOG.error(e); + } + } + + public static File copyResourceFile(String sourceName, String copyName, Project project, Task task) + throws IOException { + StudyTaskManager taskManager = StudyTaskManager.getInstance(project); + Course course = taskManager.getCourse(); + int taskNum = task.getIndex() + 1; + int lessonNum = task.getLesson().getIndex() + 1; + assert course != null; + String pathToResource = + FileUtil.join(new File(course.getResourcePath()).getParent(), Lesson.LESSON_DIR + lessonNum, Task.TASK_DIR + taskNum); + File resourceFile = new File(pathToResource, copyName); + FileUtil.copy(new File(pathToResource, sourceName), resourceFile); + return resourceFile; + } } diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyCheckAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyCheckAction.java index f8e10c9c4521..5d02f7149aab 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyCheckAction.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyCheckAction.java @@ -1,7 +1,6 @@ package com.jetbrains.python.edu.actions; import com.intellij.execution.ExecutionException; -import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.ide.projectView.ProjectView; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -13,93 +12,81 @@ 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.module.ModuleManager; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; -import com.intellij.openapi.projectRoots.Sdk; +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.TextRange; -import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.ui.JBColor; +import com.intellij.openapi.wm.IdeFocusManager; import com.jetbrains.python.edu.StudyDocumentListener; -import com.jetbrains.python.edu.StudyTaskManager; +import com.jetbrains.python.edu.StudyState; +import com.jetbrains.python.edu.StudyTestRunner; import com.jetbrains.python.edu.StudyUtils; -import com.jetbrains.python.edu.course.*; +import com.jetbrains.python.edu.course.StudyStatus; +import com.jetbrains.python.edu.course.Task; +import com.jetbrains.python.edu.course.TaskFile; +import com.jetbrains.python.edu.course.TaskWindow; import com.jetbrains.python.edu.editor.StudyEditor; -import com.jetbrains.python.sdk.PythonSdkType; import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; -import java.io.*; -import java.util.*; -import java.util.List; +import java.io.IOException; +import java.util.Map; public class StudyCheckAction extends DumbAwareAction { private static final Logger LOG = Logger.getInstance(StudyCheckAction.class.getName()); - public static final String PYTHONPATH = "PYTHONPATH"; + private static final String ANSWERS_POSTFIX = "_answers.py"; - static class StudyTestRunner { - public static final String TEST_OK = "#study_plugin test OK"; - private static final String TEST_FAILED = "#study_plugin FAILED + "; - private final Task myTask; - private final VirtualFile myTaskDir; - StudyTestRunner(Task task, VirtualFile taskDir) { - myTask = task; - myTaskDir = taskDir; + private static void flushWindows(@NotNull final Task task, @NotNull final VirtualFile taskDir) { + for (Map.Entry entry : task.getTaskFiles().entrySet()) { + String name = entry.getKey(); + TaskFile taskFile = entry.getValue(); + VirtualFile virtualFile = taskDir.findChild(name); + if (virtualFile == null) { + continue; + } + StudyUtils.flushWindows(taskFile, virtualFile); } + } - Process launchTests(Project project, String executablePath) throws ExecutionException { - Sdk sdk = PythonSdkType.findPythonSdk(ModuleManager.getInstance(project).getModules()[0]); - File testRunner = new File(myTaskDir.getPath(), myTask.getTestFile()); - GeneralCommandLine commandLine = new GeneralCommandLine(); - commandLine.setWorkDirectory(myTaskDir.getPath()); - final Map env = commandLine.getEnvironment(); - final VirtualFile courseDir = project.getBaseDir(); - if (courseDir != null) - env.put(PYTHONPATH, courseDir.getPath()); - if (sdk != null) { - String pythonPath = sdk.getHomePath(); - if (pythonPath != null) { - commandLine.setExePath(pythonPath); - commandLine.addParameter(testRunner.getPath()); - final Course course = StudyTaskManager.getInstance(project).getCourse(); - assert course != null; - commandLine.addParameter(new File(course.getResourcePath()).getParent()); - commandLine.addParameter(FileUtil.toSystemDependentName(executablePath)); - return commandLine.createProcess(); - } + private static void deleteWindowDescriptions(@NotNull final Task task, @NotNull final VirtualFile taskDir) { + for (Map.Entry entry : task.getTaskFiles().entrySet()) { + String name = entry.getKey(); + VirtualFile virtualFile = taskDir.findChild(name); + if (virtualFile == null) { + continue; + } + String windowsFileName = virtualFile.getNameWithoutExtension() + "_windows"; + VirtualFile windowsFile = taskDir.findChild(windowsFileName); + if (windowsFile != null) { + StudyUtils.deleteFile(windowsFile); } - return null; } + } - - String getPassedTests(Process p) { - InputStream testOutput = p.getInputStream(); - BufferedReader testOutputReader = new BufferedReader(new InputStreamReader(testOutput)); - String line; - try { - while ((line = testOutputReader.readLine()) != null) { - if (line.contains(TEST_FAILED)) { - return line.substring(TEST_FAILED.length(), line.length()); - } - } - } - catch (IOException e) { - LOG.error(e); + private static void drawAllTaskWindows(@NotNull final Project project, @NotNull final Task task, @NotNull final VirtualFile taskDir) { + for (Map.Entry entry : task.getTaskFiles().entrySet()) { + String name = entry.getKey(); + TaskFile taskFile = entry.getValue(); + VirtualFile virtualFile = taskDir.findChild(name); + if (virtualFile == null) { + continue; } - finally { - StudyUtils.closeSilently(testOutputReader); + FileEditor fileEditor = FileEditorManager.getInstance(project).getSelectedEditor(virtualFile); + if (fileEditor instanceof StudyEditor) { + StudyEditor studyEditor = (StudyEditor)fileEditor; + taskFile.drawAllWindows(studyEditor.getEditor()); } - return TEST_OK; } } + public void check(@NotNull final Project project) { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override @@ -107,188 +94,138 @@ public class StudyCheckAction extends DumbAwareAction { CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() { @Override public void run() { - final Editor selectedEditor = StudyEditor.getSelectedEditor(project); - if (selectedEditor != null) { - final FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance(); - final VirtualFile openedFile = fileDocumentManager.getFile(selectedEditor.getDocument()); - if (openedFile != null) { - StudyTaskManager taskManager = StudyTaskManager.getInstance(project); - final TaskFile selectedTaskFile = taskManager.getTaskFile(openedFile); - List filesToDelete = new ArrayList(); - if (selectedTaskFile != null) { - final VirtualFile taskDir = openedFile.getParent(); - Task currentTask = selectedTaskFile.getTask(); - StudyStatus oldStatus = currentTask.getStatus(); - Map taskFiles = selectedTaskFile.getTask().getTaskFiles(); + final StudyEditor selectedEditor = StudyEditor.getSelectedStudyEditor(project); + final StudyState studyState = new StudyState(selectedEditor); + if (!studyState.isValid()) { + LOG.error("StudyCheckAction was invokes outside study editor"); + return; + } + Task task = studyState.getTask(); + StudyStatus oldStatus = task.getStatus(); + Map taskFiles = task.getTaskFiles(); + VirtualFile taskDir = studyState.getTaskDir(); + flushWindows(task, taskDir); + StudyRunAction runAction = (StudyRunAction)ActionManager.getInstance().getAction(StudyRunAction.ACTION_ID); + if (runAction != null && taskFiles.size() == 1) { + runAction.run(project); + } + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + IdeFocusManager.getInstance(project).requestFocus(studyState.getEditor().getComponent(), true); + } + }); + final StudyTestRunner testRunner = new StudyTestRunner(task, taskDir); + Process testProcess = null; + try { + testProcess = testRunner.launchTests(project, studyState.getVirtualFile().getPath()); + } + catch (ExecutionException e) { + LOG.error(e); + } + if (testProcess == null) { + return; + } + String failedMessage = testRunner.getPassedTests(testProcess); + if (failedMessage.equals(StudyTestRunner.TEST_OK)) { + task.setStatus(StudyStatus.Solved, oldStatus); + createTestResultPopUp("Congratulations!", MessageType.INFO.getPopupBackground(), project); + } + else { + task.setStatus(StudyStatus.Failed, oldStatus); for (Map.Entry entry : taskFiles.entrySet()) { String name = entry.getKey(); TaskFile taskFile = entry.getValue(); - VirtualFile virtualFile = taskDir.findChild(name); - if (virtualFile == null) { + if (taskFile.getTaskWindows().size() < 2) { + taskFile.setStatus(StudyStatus.Failed, StudyStatus.Unchecked); continue; } - VirtualFile windowFile = StudyUtils.flushWindows(FileDocumentManager.getInstance().getDocument(virtualFile), taskFile, virtualFile); - filesToDelete.add(windowFile); - FileDocumentManager.getInstance().saveAllDocuments(); - } - - StudyRunAction runAction = (StudyRunAction)ActionManager.getInstance().getAction(StudyRunAction.ACTION_ID); - if (runAction != null && currentTask.getTaskFiles().size() == 1) { - runAction.run(project); - } - final StudyTestRunner testRunner = new StudyTestRunner(currentTask, taskDir); - Process testProcess = null; - try { - testProcess = testRunner.launchTests(project, openedFile.getPath()); - } - catch (ExecutionException e) { - LOG.error(e); - } - if (testProcess != null) { - String failedMessage = testRunner.getPassedTests(testProcess); - if (failedMessage.equals(StudyTestRunner.TEST_OK)) { - currentTask.setStatus(StudyStatus.Solved, oldStatus); - StudyUtils.updateStudyToolWindow(project); - selectedTaskFile.drawAllWindows(selectedEditor); - ProjectView.getInstance(project).refresh(); - for (VirtualFile file:filesToDelete) { - try { - file.delete(this); - } - catch (IOException e) { - LOG.error(e); - } - } - createTestResultPopUp("Congratulations!", JBColor.GREEN, project); - return; - } - for (Map.Entry entry : taskFiles.entrySet()) { - String name = entry.getKey(); - TaskFile taskFile = entry.getValue(); - TaskFile answerTaskFile = new TaskFile(); - VirtualFile virtualFile = taskDir.findChild(name); - if (virtualFile == null) { - continue; - } - VirtualFile answerFile = getCopyWithAnswers(taskDir, virtualFile, taskFile, answerTaskFile); - for (TaskWindow taskWindow : answerTaskFile.getTaskWindows()) { - Document document = FileDocumentManager.getInstance().getDocument(virtualFile); - if (document == null) { - continue; - } - if (!taskWindow.isValid(document)) { - continue; - } - check(project, taskWindow, answerFile, answerTaskFile, taskFile, document, testRunner, virtualFile); - } - FileEditor fileEditor = FileEditorManager.getInstance(project).getSelectedEditor(virtualFile); - Editor editor = null; - if (fileEditor instanceof StudyEditor) { - StudyEditor studyEditor = (StudyEditor) fileEditor; - editor = studyEditor.getEditor(); - } - - if (editor != null) { - taskFile.drawAllWindows(editor); - StudyUtils.synchronize(); - } - try { - answerFile.delete(this); - } - catch (IOException e) { - LOG.error(e); - } - } - for (VirtualFile file:filesToDelete) { - try { - file.delete(this); - } - catch (IOException e) { - LOG.error(e); - } - } - currentTask.setStatus(StudyStatus.Failed, oldStatus); - StudyUtils.updateStudyToolWindow(project); - createTestResultPopUp(failedMessage, JBColor.RED, project); + runSmartTestProcess(taskDir, testRunner, name, taskFile, project); } + createTestResultPopUp(failedMessage, MessageType.ERROR.getPopupBackground(), project); + navigateToFailedTaskWindow(studyState, task, taskDir, project); } + StudyUtils.updateStudyToolWindow(project); + drawAllTaskWindows(project, task, taskDir); + ProjectView.getInstance(project).refresh(); + deleteWindowDescriptions(task, taskDir); } - } - - } - }); + }); } }); } - private void check(Project project, - TaskWindow taskWindow, - VirtualFile answerFile, - TaskFile answerTaskFile, - TaskFile usersTaskFile, - Document usersDocument, - StudyTestRunner testRunner, - VirtualFile openedFile) { - - try { - VirtualFile windowCopy = answerFile.copy(this, answerFile.getParent(), answerFile.getNameWithoutExtension() + "_window" + taskWindow.getIndex() + ".py"); - final FileDocumentManager documentManager = FileDocumentManager.getInstance(); - final Document windowDocument = documentManager.getDocument(windowCopy); - if (windowDocument != null) { - StudyTaskManager taskManager = StudyTaskManager.getInstance(project); - Course course = taskManager.getCourse(); - Task task = usersTaskFile.getTask(); - int taskNum = task.getIndex() + 1; - int lessonNum = task.getLesson().getIndex() + 1; - assert course != null; - String pathToResource = FileUtil.join(new File(course.getResourcePath()).getParent(), Lesson.LESSON_DIR + lessonNum, Task.TASK_DIR + taskNum); - File resourceFile = new File(pathToResource, windowCopy.getName()); - FileUtil.copy(new File(pathToResource, openedFile.getName()), resourceFile); - TaskFile windowTaskFile = new TaskFile(); - TaskFile.copy(answerTaskFile, windowTaskFile); - StudyDocumentListener listener = new StudyDocumentListener(windowTaskFile); - windowDocument.addDocumentListener(listener); - int start = taskWindow.getRealStartOffset(windowDocument); - int end = start + taskWindow.getLength(); - TaskWindow userTaskWindow = usersTaskFile.getTaskWindows().get(taskWindow.getIndex()); - int userStart = userTaskWindow.getRealStartOffset(usersDocument); - int userEnd = userStart + userTaskWindow.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); + private static void navigateToFailedTaskWindow(@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(); + if (!selectedTaskFile.hasFailedTaskWindows()) { + for (Map.Entry entry : task.getTaskFiles().entrySet()) { + String name = entry.getKey(); + TaskFile taskFile = entry.getValue(); + if (taskFile.hasFailedTaskWindows()) { + taskFileToNavigate = taskFile; + VirtualFile virtualFile = taskDir.findChild(name); + if (virtualFile == null) { + continue; } - }); - VirtualFile fileWindows = StudyUtils.flushWindows(windowDocument, windowTaskFile, windowCopy); - Process smartTestProcess = testRunner.launchTests(project, windowCopy.getPath()); - boolean res = testRunner.getPassedTests(smartTestProcess).equals(StudyTestRunner.TEST_OK); - userTaskWindow.setStatus(res ? StudyStatus.Solved : StudyStatus.Failed, StudyStatus.Unchecked); - windowCopy.delete(this); - fileWindows.delete(this); - if (!resourceFile.delete()) { - LOG.error("failed to delete", resourceFile.getPath()); + FileEditor fileEditor = FileEditorManager.getInstance(project).getSelectedEditor(virtualFile); + if (fileEditor instanceof StudyEditor) { + StudyEditor studyEditor = (StudyEditor)fileEditor; + editor = studyEditor.getEditor(); + } + fileToNavigate = virtualFile; + break; } } } - catch (IOException e) { - LOG.error(e); + FileEditorManager.getInstance(project).openFile(fileToNavigate, true); + final Editor editorToNavigate = editor; + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + IdeFocusManager.getInstance(project).requestFocus(editorToNavigate.getContentComponent(), true); + } + }); + taskFileToNavigate.navigateToFirstFailedTaskWindow(editor); + } + + private void runSmartTestProcess(@NotNull final VirtualFile taskDir, + @NotNull final StudyTestRunner testRunner, + final String taskFileName, + @NotNull final TaskFile taskFile, + @NotNull final Project project) { + TaskFile answerTaskFile = new TaskFile(); + VirtualFile virtualFile = taskDir.findChild(taskFileName); + if (virtualFile == null) { + return; } - catch (ExecutionException e) { - LOG.error(e); + VirtualFile answerFile = getCopyWithAnswers(taskDir, virtualFile, taskFile, answerTaskFile); + for (TaskWindow taskWindow : answerTaskFile.getTaskWindows()) { + Document document = FileDocumentManager.getInstance().getDocument(virtualFile); + if (document == null) { + continue; + } + if (!taskWindow.isValid(document)) { + continue; + } + taskWindow.smartCheck(project, answerFile, answerTaskFile, taskFile, testRunner, virtualFile, document); } + StudyUtils.deleteFile(answerFile); } - - private VirtualFile getCopyWithAnswers(final VirtualFile taskDir, - final VirtualFile file, - final TaskFile source, - TaskFile target) { + 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.py"); + copy = file.copy(this, taskDir, file.getNameWithoutExtension() + ANSWERS_POSTFIX); final FileDocumentManager documentManager = FileDocumentManager.getInstance(); final Document document = documentManager.getDocument(copy); if (document != null) { @@ -315,19 +252,18 @@ public class StudyCheckAction extends DumbAwareAction { catch (IOException e) { LOG.error(e); } - - return copy; } private static void createTestResultPopUp(final String text, Color color, @NotNull final Project project) { BalloonBuilder balloonBuilder = JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(text, null, color, null); - Balloon balloon = balloonBuilder.createBalloon(); + final Balloon balloon = balloonBuilder.createBalloon(); StudyEditor studyEditor = StudyEditor.getSelectedStudyEditor(project); assert studyEditor != null; JButton checkButton = studyEditor.getCheckButton(); balloon.showInCenterOf(checkButton); + Disposer.register(project, balloon); } @Override diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyEditInputAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyEditInputAction.java index 5b9a6fef23ac..72660fc9c2c6 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyEditInputAction.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyEditInputAction.java @@ -19,6 +19,7 @@ import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.ui.tabs.TabInfo; import com.intellij.ui.tabs.TabsListener; import com.intellij.ui.tabs.impl.JBEditorTabs; +import com.intellij.util.PlatformIcons; import com.jetbrains.python.edu.StudyTaskManager; import com.jetbrains.python.edu.StudyUtils; import com.jetbrains.python.edu.course.Task; @@ -26,7 +27,6 @@ import com.jetbrains.python.edu.course.TaskFile; import com.jetbrains.python.edu.course.UserTest; import com.jetbrains.python.edu.editor.StudyEditor; import com.jetbrains.python.edu.ui.StudyTestContentPanel; -import icons.StudyIcons; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -92,7 +92,7 @@ public class StudyEditInputAction extends DumbAwareAction { i++; } TabInfo plusTab = new TabInfo(new JPanel()); - plusTab.setIcon(StudyIcons.Add); + plusTab.setIcon(PlatformIcons.ADD_ICON); tabbedPane.addTabSilently(plusTab, tabbedPane.getTabCount()); final JBPopup hint = JBPopupFactory.getInstance().createComponentPopupBuilder(tabbedPane.getComponent(), tabbedPane.getComponent()) diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNewProject.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNewProject.java new file mode 100644 index 000000000000..0b75c4bc01c4 --- /dev/null +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNewProject.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jetbrains.python.edu.actions; + +import com.jetbrains.python.edu.StudyDirectoryProjectGenerator; +import com.jetbrains.python.newProject.actions.GenerateProjectCallback; +import com.jetbrains.python.newProject.actions.ProjectSpecificAction; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class StudyNewProject extends ProjectSpecificAction { + + public StudyNewProject(@NotNull final String name, @Nullable final Runnable runnable) { + super(new GenerateProjectCallback(runnable), new StudyDirectoryProjectGenerator(), name, true); + } + + public StudyNewProject() { + this("Learn Python", null); + } + +} diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextStudyTaskAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextStudyTaskAction.java index 81818a95c044..3c971c3fe15e 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextStudyTaskAction.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextStudyTaskAction.java @@ -2,13 +2,14 @@ package com.jetbrains.python.edu.actions; import com.jetbrains.python.edu.editor.StudyEditor; import com.jetbrains.python.edu.course.Task; +import org.jetbrains.annotations.NotNull; import javax.swing.*; public class StudyNextStudyTaskAction extends StudyTaskNavigationAction { @Override - protected JButton getButton(StudyEditor selectedStudyEditor) { + protected JButton getButton(@NotNull final StudyEditor selectedStudyEditor) { return selectedStudyEditor.getNextTaskButton(); } @@ -18,7 +19,7 @@ public class StudyNextStudyTaskAction extends StudyTaskNavigationAction { } @Override - protected Task getTargetTask(Task sourceTask) { + protected Task getTargetTask(@NotNull final Task sourceTask) { return sourceTask.next(); } } \ No newline at end of file diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextWindowAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextWindowAction.java index 595aeeff42e3..fcf9ef40c7d4 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextWindowAction.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNextWindowAction.java @@ -1,8 +1,8 @@ package com.jetbrains.python.edu.actions; +import com.intellij.icons.AllIcons; import com.jetbrains.python.edu.StudyUtils; import com.jetbrains.python.edu.course.TaskWindow; -import icons.StudyIcons; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -16,7 +16,7 @@ public class StudyNextWindowAction extends StudyWindowNavigationAction { public static final String SHORTCUT2 = "ctrl pressed ENTER"; public StudyNextWindowAction() { - super("NextWindowAction", "Select next window", StudyIcons.Next); + super("NextWindowAction", "Select next window", AllIcons.Actions.Forward); } @Override diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyPreviousStudyTaskAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyPreviousStudyTaskAction.java index bc26c28cfabd..f6da6a067894 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyPreviousStudyTaskAction.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyPreviousStudyTaskAction.java @@ -3,13 +3,14 @@ package com.jetbrains.python.edu.actions; import com.jetbrains.python.edu.editor.StudyEditor; import com.jetbrains.python.edu.course.Task; +import org.jetbrains.annotations.NotNull; import javax.swing.*; public class StudyPreviousStudyTaskAction extends StudyTaskNavigationAction { @Override - protected JButton getButton(StudyEditor selectedStudyEditor) { + protected JButton getButton(@NotNull final StudyEditor selectedStudyEditor) { return selectedStudyEditor.getPrevTaskButton(); } @@ -19,7 +20,7 @@ public class StudyPreviousStudyTaskAction extends StudyTaskNavigationAction { } @Override - protected Task getTargetTask(Task sourceTask) { + protected Task getTargetTask(@NotNull final Task sourceTask) { return sourceTask.prev(); } } \ No newline at end of file diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskAction.java deleted file mode 100644 index f8abb0b63365..000000000000 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskAction.java +++ /dev/null @@ -1,122 +0,0 @@ -package com.jetbrains.python.edu.actions; - -import com.intellij.ide.projectView.ProjectView; -import com.intellij.openapi.actionSystem.AnActionEvent; -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.project.DumbAwareAction; -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.vfs.VirtualFile; -import com.intellij.openapi.wm.IdeFocusManager; -import com.jetbrains.python.edu.StudyDocumentListener; -import com.jetbrains.python.edu.StudyTaskManager; -import com.jetbrains.python.edu.StudyUtils; -import com.jetbrains.python.edu.course.*; -import com.jetbrains.python.edu.editor.StudyEditor; - -import java.io.*; - -public class StudyRefreshTaskAction extends DumbAwareAction { - private static final Logger LOG = Logger.getInstance(StudyRefreshTaskAction.class.getName()); - - public void refresh(final Project project) { - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") - @Override - public void run() { - final Editor editor = StudyEditor.getSelectedEditor(project); - assert editor != null; - final Document document = editor.getDocument(); - StudyDocumentListener listener = StudyEditor.getListener(document); - if (listener != null) { - document.removeDocumentListener(listener); - } - final int lineCount = document.getLineCount(); - if (lineCount != 0) { - CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() { - @Override - public void run() { - document.deleteString(0, document.getLineEndOffset(lineCount - 1)); - } - }); - } - StudyTaskManager taskManager = StudyTaskManager.getInstance(project); - Course course = taskManager.getCourse(); - assert course != null; - File resourceFile = new File(course.getResourcePath()); - File resourceRoot = resourceFile.getParentFile(); - FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance(); - VirtualFile openedFile = fileDocumentManager.getFile(document); - assert openedFile != null; - final TaskFile selectedTaskFile = taskManager.getTaskFile(openedFile); - assert selectedTaskFile != null; - Task currentTask = selectedTaskFile.getTask(); - String lessonDir = Lesson.LESSON_DIR + String.valueOf(currentTask.getLesson().getIndex() + 1); - String taskDir = Task.TASK_DIR + String.valueOf(currentTask.getIndex() + 1); - File pattern = new File(new File(new File(resourceRoot, lessonDir), taskDir), openedFile.getName()); - BufferedReader reader = null; - try { - reader = new BufferedReader(new InputStreamReader(new FileInputStream(pattern))); - String line; - StringBuilder patternText = new StringBuilder(); - while ((line = reader.readLine()) != null) { - patternText.append(line); - patternText.append("\n"); - } - int patternLength = patternText.length(); - if (patternText.charAt(patternLength - 1) == '\n') { - patternText.delete(patternLength - 1, patternLength); - } - document.setText(patternText); - StudyStatus oldStatus = currentTask.getStatus(); - LessonInfo lessonInfo = currentTask.getLesson().getLessonInfo(); - lessonInfo.update(oldStatus, -1); - lessonInfo.update(StudyStatus.Unchecked, +1); - StudyUtils.updateStudyToolWindow(project); - for (TaskWindow taskWindow : selectedTaskFile.getTaskWindows()) { - taskWindow.reset(); - } - ProjectView.getInstance(project).refresh(); - if (listener != null) { - document.addDocumentListener(listener); - } - selectedTaskFile.drawAllWindows(editor); - IdeFocusManager.getInstance(project).requestFocus(editor.getContentComponent(), true); - selectedTaskFile.navigateToFirstTaskWindow(editor); - BalloonBuilder balloonBuilder = - JBPopupFactory.getInstance().createHtmlTextBalloonBuilder("You can now start again", MessageType.INFO, null); - Balloon balloon = balloonBuilder.createBalloon(); - StudyEditor selectedStudyEditor = StudyEditor.getSelectedStudyEditor(project); - assert selectedStudyEditor != null; - balloon.showInCenterOf(selectedStudyEditor.getRefreshButton()); - } - catch (FileNotFoundException e1) { - LOG.error(e1); - } - catch (IOException e1) { - LOG.error(e1); - } - finally { - StudyUtils.closeSilently(reader); - } - } - }); - } - }); - } - - public void actionPerformed(AnActionEvent e) { - refresh(e.getProject()); - } -} diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskFileAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskFileAction.java new file mode 100644 index 000000000000..a9448ddea0e3 --- /dev/null +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskFileAction.java @@ -0,0 +1,129 @@ +package com.jetbrains.python.edu.actions; + +import com.intellij.ide.projectView.ProjectView; +import com.intellij.openapi.actionSystem.AnActionEvent; +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.project.DumbAwareAction; +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.Disposer; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.IdeFocusManager; +import com.jetbrains.python.edu.StudyDocumentListener; +import com.jetbrains.python.edu.StudyTaskManager; +import com.jetbrains.python.edu.StudyUtils; +import com.jetbrains.python.edu.course.*; +import com.jetbrains.python.edu.editor.StudyEditor; + +import java.io.*; + +public class StudyRefreshTaskFileAction extends DumbAwareAction { + private static final Logger LOG = Logger.getInstance(StudyRefreshTaskFileAction.class.getName()); + + public void refresh(final Project project) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") + @Override + public void run() { + final Editor editor = StudyEditor.getSelectedEditor(project); + assert editor != null; + final Document document = editor.getDocument(); + StudyDocumentListener listener = StudyEditor.getListener(document); + if (listener != null) { + document.removeDocumentListener(listener); + } + final int lineCount = document.getLineCount(); + if (lineCount != 0) { + CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() { + @Override + public void run() { + document.deleteString(0, document.getLineEndOffset(lineCount - 1)); + } + }); + } + StudyTaskManager taskManager = StudyTaskManager.getInstance(project); + Course course = taskManager.getCourse(); + assert course != null; + File resourceFile = new File(course.getResourcePath()); + File resourceRoot = resourceFile.getParentFile(); + FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance(); + VirtualFile openedFile = fileDocumentManager.getFile(document); + assert openedFile != null; + final TaskFile selectedTaskFile = taskManager.getTaskFile(openedFile); + assert selectedTaskFile != null; + Task currentTask = selectedTaskFile.getTask(); + String lessonDir = Lesson.LESSON_DIR + String.valueOf(currentTask.getLesson().getIndex() + 1); + String taskDir = Task.TASK_DIR + String.valueOf(currentTask.getIndex() + 1); + File pattern = new File(new File(new File(resourceRoot, lessonDir), taskDir), openedFile.getName()); + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader(new FileInputStream(pattern))); + String line; + StringBuilder patternText = new StringBuilder(); + while ((line = reader.readLine()) != null) { + patternText.append(line); + patternText.append("\n"); + } + int patternLength = patternText.length(); + if (patternText.charAt(patternLength - 1) == '\n') { + patternText.delete(patternLength - 1, patternLength); + } + document.setText(patternText); + StudyStatus oldStatus = currentTask.getStatus(); + LessonInfo lessonInfo = currentTask.getLesson().getLessonInfo(); + lessonInfo.update(oldStatus, -1); + lessonInfo.update(StudyStatus.Unchecked, +1); + StudyUtils.updateStudyToolWindow(project); + for (TaskWindow taskWindow : selectedTaskFile.getTaskWindows()) { + taskWindow.reset(); + } + ProjectView.getInstance(project).refresh(); + if (listener != null) { + document.addDocumentListener(listener); + } + selectedTaskFile.drawAllWindows(editor); + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + IdeFocusManager.getInstance(project).requestFocus(editor.getContentComponent(), true); + } + }); + selectedTaskFile.navigateToFirstTaskWindow(editor); + BalloonBuilder balloonBuilder = + JBPopupFactory.getInstance().createHtmlTextBalloonBuilder("You can now start again", MessageType.INFO, null); + final Balloon balloon = balloonBuilder.createBalloon(); + StudyEditor selectedStudyEditor = StudyEditor.getSelectedStudyEditor(project); + assert selectedStudyEditor != null; + balloon.showInCenterOf(selectedStudyEditor.getRefreshButton()); + Disposer.register(project, balloon); + } + catch (FileNotFoundException e1) { + LOG.error(e1); + } + catch (IOException e1) { + LOG.error(e1); + } + finally { + StudyUtils.closeSilently(reader); + } + } + }); + } + }); + } + + public void actionPerformed(AnActionEvent e) { + refresh(e.getProject()); + } +} diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyShowHintAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyShowHintAction.java index 1efa90889449..2952486274cf 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyShowHintAction.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyShowHintAction.java @@ -5,19 +5,18 @@ import com.intellij.codeInsight.documentation.DocumentationManager; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.LogicalPosition; -import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.util.Disposer; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; +import com.jetbrains.python.edu.StudyState; import com.jetbrains.python.edu.StudyTaskManager; import com.jetbrains.python.edu.StudyUtils; import com.jetbrains.python.edu.course.Course; -import com.jetbrains.python.edu.course.TaskFile; import com.jetbrains.python.edu.course.TaskWindow; import com.jetbrains.python.edu.editor.StudyEditor; import icons.StudyIcons; @@ -33,58 +32,56 @@ public class StudyShowHintAction extends DumbAwareAction { } public void actionPerformed(AnActionEvent e) { - Project project = e.getProject(); - if (project != null) { + final Project project = e.getProject(); + if (project == null) { + return; + } + Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null) { + return; + } + StudyState studyState = new StudyState(StudyEditor.getSelectedStudyEditor(project)); + if (!studyState.isValid()) { + return; + } + PsiFile file = PsiManager.getInstance(project).findFile(studyState.getVirtualFile()); + final Editor editor = studyState.getEditor(); + LogicalPosition pos = editor.getCaretModel().getLogicalPosition(); + TaskWindow taskWindow = studyState.getTaskFile().getTaskWindow(editor.getDocument(), pos); + if (file == null || taskWindow == null) { + return; + } + String hint = taskWindow.getHint(); + if (hint == null) { + return; + } + File resourceFile = new File(course.getResourcePath()); + File resourceRoot = resourceFile.getParentFile(); + if (resourceRoot == null || !resourceRoot.exists()) { + return; + } + File hintsDir = new File(resourceRoot, Course.HINTS_DIR); + if (hintsDir.exists()) { + String hintText = StudyUtils.getFileText(hintsDir.getAbsolutePath(), hint, true); + int offset = editor.getDocument().getLineStartOffset(pos.line) + pos.column; + PsiElement element = file.findElementAt(offset); + if (hintText == null || element == null) { + return; + } + DocumentationManager documentationManager = DocumentationManager.getInstance(project); DocumentationComponent component = new DocumentationComponent(documentationManager); - Editor selectedEditor = StudyEditor.getSelectedEditor(project); - FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance(); - assert selectedEditor != null; - VirtualFile openedFile = fileDocumentManager.getFile(selectedEditor.getDocument()); - if (openedFile != null) { - StudyTaskManager taskManager = StudyTaskManager.getInstance(e.getProject()); - TaskFile taskFile = taskManager.getTaskFile(openedFile); - if (taskFile != null) { - PsiFile file = PsiManager.getInstance(project).findFile(openedFile); - if (file != null) { - LogicalPosition pos = selectedEditor.getCaretModel().getLogicalPosition(); - TaskWindow taskWindow = taskFile.getTaskWindow(selectedEditor.getDocument(), pos); - if (taskWindow != null) { - String hint = taskWindow.getHint(); - if (hint == null) { - return; - } - Course course = taskManager.getCourse(); - if (course != null) { - File resourceFile = new File(course.getResourcePath()); - File resourceRoot = resourceFile.getParentFile(); - if (resourceRoot != null && resourceRoot.exists()) { - File hintsDir = new File(resourceRoot, Course.HINTS_DIR); - if (hintsDir.exists()) { - String hintText = StudyUtils.getFileText(hintsDir.getAbsolutePath(), hint, true); - if (hintText != null) { - int offset = selectedEditor.getDocument().getLineStartOffset(pos.line) + pos.column; - PsiElement element = file.findElementAt(offset); - if (element != null) { - component.setData(element, hintText, true, null); - final JBPopup popup = - JBPopupFactory.getInstance().createComponentPopupBuilder(component, component) - .setDimensionServiceKey(project, DocumentationManager.JAVADOC_LOCATION_AND_SIZE, false) - .setResizable(true) - .setMovable(true) - .setRequestFocus(true) - .createPopup(); - component.setHint(popup); - popup.showInBestPositionFor(selectedEditor); - } - } - } - } - } - } - } - } - } + component.setData(element, hintText, true, null); + final JBPopup popup = + JBPopupFactory.getInstance().createComponentPopupBuilder(component, component) + .setDimensionServiceKey(project, DocumentationManager.JAVADOC_LOCATION_AND_SIZE, false) + .setResizable(true) + .setMovable(true) + .setRequestFocus(true) + .createPopup(); + component.setHint(popup); + popup.showInBestPositionFor(editor); + Disposer.dispose(component); } } diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyTaskNavigationAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyTaskNavigationAction.java index b781e7da8849..46c0981cb964 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyTaskNavigationAction.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyTaskNavigationAction.java @@ -1,8 +1,6 @@ package com.jetbrains.python.edu.actions; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; @@ -11,37 +9,34 @@ 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.vfs.VirtualFile; -import com.jetbrains.python.edu.StudyTaskManager; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowId; +import com.intellij.openapi.wm.ToolWindowManager; +import com.jetbrains.python.edu.StudyState; import com.jetbrains.python.edu.course.Lesson; import com.jetbrains.python.edu.course.Task; import com.jetbrains.python.edu.course.TaskFile; import com.jetbrains.python.edu.editor.StudyEditor; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.util.Map; -/** - * author: liana - * data: 7/21/14. - */ + abstract public class StudyTaskNavigationAction extends DumbAwareAction { - public void navigateTask(Project project) { - Editor selectedEditor = StudyEditor.getSelectedEditor(project); - FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance(); - assert selectedEditor != null; - VirtualFile openedFile = fileDocumentManager.getFile(selectedEditor.getDocument()); - StudyTaskManager taskManager = StudyTaskManager.getInstance(project); - assert openedFile != null; - TaskFile selectedTaskFile = taskManager.getTaskFile(openedFile); - assert selectedTaskFile != null; - Task currentTask = selectedTaskFile.getTask(); - Task nextTask = getTargetTask(currentTask); + public void navigateTask(@NotNull final Project project) { + StudyEditor studyEditor = StudyEditor.getSelectedStudyEditor(project); + StudyState studyState = new StudyState(studyEditor); + if (!studyState.isValid()) { + return; + } + Task nextTask = getTargetTask(studyState.getTask()); if (nextTask == null) { BalloonBuilder balloonBuilder = JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(getNavigationFinishedMessage(), MessageType.INFO, null); Balloon balloon = balloonBuilder.createBalloon(); - StudyEditor selectedStudyEditor = StudyEditor.getSelectedStudyEditor(project); - balloon.showInCenterOf(getButton(selectedStudyEditor)); + assert studyEditor != null; + balloon.showInCenterOf(getButton(studyEditor)); return; } for (VirtualFile file : FileEditorManager.getInstance(project).getOpenFiles()) { @@ -82,16 +77,24 @@ abstract public class StudyTaskNavigationAction extends DumbAwareAction { if (shouldBeActive != null) { FileEditorManager.getInstance(project).openFile(shouldBeActive, true); } + ToolWindow runToolWindow = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.RUN); + if (runToolWindow != null) { + runToolWindow.hide(null); + } } - protected abstract JButton getButton(StudyEditor selectedStudyEditor); + protected abstract JButton getButton(@NotNull final StudyEditor selectedStudyEditor); @Override public void actionPerformed(AnActionEvent e) { - navigateTask(e.getProject()); + Project project = e.getProject(); + if (project == null) { + return; + } + navigateTask(project); } protected abstract String getNavigationFinishedMessage(); - protected abstract Task getTargetTask(Task sourceTask); + protected abstract Task getTargetTask(@NotNull final Task sourceTask); } diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskFile.java b/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskFile.java index 4f17fc0d27f3..c46c4f56f937 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskFile.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskFile.java @@ -21,7 +21,7 @@ import java.util.List; * which is visible to student in project view */ -public class TaskFile implements Stateful{ +public class TaskFile implements Stateful { public List taskWindows = new ArrayList(); private Task myTask; @Transient @@ -173,7 +173,18 @@ public class TaskFile implements Stateful{ for (TaskWindow w : taskWindows) { if ((w.getLine() == line) && (w.getStart() >= oldEndOffsetInLine)) { int distance = w.getStart() - oldEndOffsetInLine; - if (lineChange != 0 || newEndOffsetInLine <= w.getStart()) { + boolean coveredByPrevTW = false; + int prevIndex = w.getIndex() - 1; + if (StudyUtils.indexIsValid(prevIndex, taskWindows)) { + TaskWindow prevTW = taskWindows.get(prevIndex); + if (prevTW.getLine() == line) { + int endOffset = prevTW.getStart() + prevTW.getLength(); + if (endOffset >= newEndOffsetInLine) { + coveredByPrevTW = true; + } + } + } + if (lineChange != 0 || newEndOffsetInLine <= w.getStart() || coveredByPrevTW) { w.setStart(distance + newEndOffsetInLine); w.setLine(line + lineChange); } @@ -217,12 +228,33 @@ public class TaskFile implements Stateful{ public void navigateToFirstTaskWindow(@NotNull final Editor editor) { if (!taskWindows.isEmpty()) { TaskWindow firstTaskWindow = StudyUtils.getFirst(taskWindows); - mySelectedTaskWindow = firstTaskWindow; - LogicalPosition taskWindowStart = new LogicalPosition(firstTaskWindow.getLine(), firstTaskWindow.getStart()); - editor.getCaretModel().moveToLogicalPosition(taskWindowStart); - int startOffset = firstTaskWindow.getRealStartOffset(editor.getDocument()); - int endOffset = startOffset + firstTaskWindow.getLength(); - editor.getSelectionModel().setSelection(startOffset, endOffset); + navigateToTaskWindow(editor, firstTaskWindow); + } + } + + private void navigateToTaskWindow(@NotNull final Editor editor, @NotNull final TaskWindow firstTaskWindow) { + if (!firstTaskWindow.isValid(editor.getDocument())) { + return; } + mySelectedTaskWindow = firstTaskWindow; + LogicalPosition taskWindowStart = new LogicalPosition(firstTaskWindow.getLine(), firstTaskWindow.getStart()); + editor.getCaretModel().moveToLogicalPosition(taskWindowStart); + int startOffset = firstTaskWindow.getRealStartOffset(editor.getDocument()); + int endOffset = startOffset + firstTaskWindow.getLength(); + editor.getSelectionModel().setSelection(startOffset, endOffset); + } + + public void navigateToFirstFailedTaskWindow(@NotNull final Editor editor) { + for (TaskWindow taskWindow : taskWindows) { + if (taskWindow.getStatus() != StudyStatus.Failed) { + continue; + } + navigateToTaskWindow(editor, taskWindow); + break; + } + } + + public boolean hasFailedTaskWindows() { + return taskWindows.size() > 0 && getStatus() == StudyStatus.Failed; } } diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskWindow.java b/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskWindow.java index 4fb112cc1f9b..dc4a75a800ce 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskWindow.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskWindow.java @@ -1,5 +1,8 @@ package com.jetbrains.python.edu.course; +import com.intellij.execution.ExecutionException; +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.editor.colors.EditorColors; @@ -8,16 +11,27 @@ import com.intellij.openapi.editor.markup.HighlighterLayer; import com.intellij.openapi.editor.markup.HighlighterTargetArea; import com.intellij.openapi.editor.markup.RangeHighlighter; import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.JBColor; +import com.jetbrains.python.edu.StudyDocumentListener; +import com.jetbrains.python.edu.StudyTestRunner; +import com.jetbrains.python.edu.StudyUtils; import org.jetbrains.annotations.NotNull; +import java.io.File; +import java.io.IOException; + /** * Implementation of windows which user should type in */ public class TaskWindow implements Comparable, Stateful { - + private static final String WINDOW_POSTFIX = "_window.py"; + private static final Logger LOG = Logger.getInstance(TaskWindow.class); public int line = 0; public int start = 0; public String hint = ""; @@ -174,4 +188,55 @@ public class TaskWindow implements Comparable, Stateful { public int getIndex() { return myIndex; } + + public void smartCheck(@NotNull final Project project, + @NotNull final VirtualFile answerFile, + @NotNull final TaskFile answerTaskFile, + @NotNull final TaskFile usersTaskFile, + @NotNull final StudyTestRunner testRunner, + @NotNull final VirtualFile virtualFile, + @NotNull final Document usersDocument) { + + try { + VirtualFile windowCopy = + answerFile.copy(this, answerFile.getParent(), answerFile.getNameWithoutExtension() + WINDOW_POSTFIX); + final FileDocumentManager documentManager = FileDocumentManager.getInstance(); + final Document windowDocument = documentManager.getDocument(windowCopy); + if (windowDocument != null) { + File resourceFile = StudyUtils.copyResourceFile(virtualFile.getName(), windowCopy.getName(), project, usersTaskFile.getTask()); + TaskFile windowTaskFile = new TaskFile(); + TaskFile.copy(answerTaskFile, windowTaskFile); + StudyDocumentListener listener = new StudyDocumentListener(windowTaskFile); + windowDocument.addDocumentListener(listener); + int start = getRealStartOffset(windowDocument); + int end = start + getLength(); + TaskWindow userTaskWindow = usersTaskFile.getTaskWindows().get(getIndex()); + int userStart = userTaskWindow.getRealStartOffset(usersDocument); + int userEnd = userStart + userTaskWindow.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); + } + }); + VirtualFile fileWindows = StudyUtils.flushWindows(windowTaskFile, windowCopy); + Process smartTestProcess = testRunner.launchTests(project, windowCopy.getPath()); + boolean res = testRunner.getPassedTests(smartTestProcess).equals(StudyTestRunner.TEST_OK); + userTaskWindow.setStatus(res ? StudyStatus.Solved : StudyStatus.Failed, StudyStatus.Unchecked); + StudyUtils.deleteFile(windowCopy); + StudyUtils.deleteFile(fileWindows); + if (!resourceFile.delete()) { + LOG.error("failed to delete", resourceFile.getPath()); + } + } + } + catch (ExecutionException e) { + LOG.error(e); + } + catch (IOException e) { + LOG.error(e); + } + } } \ No newline at end of file diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/editor/StudyEditor.java b/python/edu/learn-python/src/com/jetbrains/python/edu/editor/StudyEditor.java index 69c5acc5f127..6b27c4a406db 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/editor/StudyEditor.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/editor/StudyEditor.java @@ -1,8 +1,10 @@ package com.jetbrains.python.edu.editor; import com.intellij.codeHighlighting.BackgroundEditorHighlighter; +import com.intellij.icons.AllIcons; import com.intellij.ide.structureView.StructureViewBuilder; import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorFactory; @@ -17,9 +19,15 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowId; +import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.pom.Navigatable; +import com.intellij.ui.BrowserHyperlinkListener; import com.intellij.ui.HideableTitledPanel; import com.intellij.ui.JBColor; +import com.intellij.util.ui.EmptyClipboardOwner; import com.intellij.util.ui.UIUtil; import com.jetbrains.python.edu.StudyDocumentListener; import com.jetbrains.python.edu.StudyTaskManager; @@ -36,8 +44,8 @@ import javax.swing.text.MutableAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.StyledDocument; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; +import java.awt.datatransfer.StringSelection; +import java.awt.event.*; import java.beans.PropertyChangeListener; import java.util.HashMap; import java.util.Map; @@ -50,12 +58,13 @@ public class StudyEditor implements TextEditor { private static final String TASK_TEXT_HEADER = "Task Text"; private final FileEditor myDefaultEditor; private final JComponent myComponent; + private final TaskFile myTaskFile; private JButton myCheckButton; private JButton myNextTaskButton; private JButton myPrevTaskButton; private JButton myRefreshButton; private static final Map myDocumentListeners = new HashMap(); - private Project myProject; + private final Project myProject; public JButton getCheckButton() { return myCheckButton; @@ -65,6 +74,10 @@ public class StudyEditor implements TextEditor { return myPrevTaskButton; } + public TaskFile getTaskFile() { + return myTaskFile; + } + private static JButton addButton(@NotNull final JComponent parentComponent, String toolTipText, Icon icon) { JButton newButton = new JButton(); newButton.setToolTipText(toolTipText); @@ -89,26 +102,72 @@ public class StudyEditor implements TextEditor { myComponent = myDefaultEditor.getComponent(); JPanel studyPanel = new JPanel(); studyPanel.setLayout(new BoxLayout(studyPanel, BoxLayout.Y_AXIS)); - TaskFile taskFile = StudyTaskManager.getInstance(myProject).getTaskFile(file); - if (taskFile != null) { - Task currentTask = taskFile.getTask(); + myTaskFile = StudyTaskManager.getInstance(myProject).getTaskFile(file); + if (myTaskFile != null) { + Task currentTask = myTaskFile.getTask(); String taskText = currentTask.getResourceText(project, currentTask.getText(), false); initializeTaskText(studyPanel, taskText); JPanel studyButtonPanel = new JPanel(new GridLayout(1, 2)); JPanel taskActionsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); studyButtonPanel.add(taskActionsPanel); studyButtonPanel.add(new JPanel()); - initializeButtons(taskActionsPanel, taskFile); + initializeButtons(taskActionsPanel, myTaskFile); studyPanel.add(studyButtonPanel); myComponent.add(studyPanel, BorderLayout.NORTH); } } - private static void initializeTaskText(JPanel studyPanel, @Nullable String taskText) { + class CopyListener extends MouseAdapter { + final JTextPane myTextPane; + + public CopyListener(JTextPane textPane) { + myTextPane = textPane; + } + + @Override + public void mouseReleased(MouseEvent e) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + ToolWindow projectView = ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PROJECT_VIEW); + if (projectView == null) { + return; + } + final Component focusComponent = projectView.getComponent(); + IdeFocusManager.getInstance(myProject).requestFocus(focusComponent, true); + final String text = myTextPane.getSelectedText(); + if (text == null) { + return; + } + KeyAdapter keyAdapter = new KeyAdapter() { + @Override + public void keyPressed(KeyEvent ev) { + if (ev.getKeyCode() == KeyEvent.VK_C + && ev.getModifiers() == InputEvent.CTRL_MASK) { + StringSelection selection = new StringSelection(text); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, EmptyClipboardOwner.INSTANCE); + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + IdeFocusManager.getInstance(myProject).requestFocus(myDefaultEditor.getComponent(), true); + } + }); + } + } + }; + focusComponent.addKeyListener(keyAdapter); + } + }); + } + } + + private void initializeTaskText(JPanel studyPanel, @Nullable String taskText) { JTextPane taskTextPane = new JTextPane(); + taskTextPane.addMouseListener(new CopyListener(taskTextPane)); taskTextPane.setContentType("text/html"); taskTextPane.setEditable(false); taskTextPane.setText(taskText); + taskTextPane.addHyperlinkListener(new BrowserHyperlinkListener()); EditorColorsScheme editorColorsScheme = EditorColorsManager.getInstance().getGlobalScheme(); int fontSize = editorColorsScheme.getEditorFontSize(); String fontName = editorColorsScheme.getEditorFontName(); @@ -134,10 +193,10 @@ public class StudyEditor implements TextEditor { private void initializeButtons(@NotNull final JPanel taskActionsPanel, @NotNull final TaskFile taskFile) { myCheckButton = addButton(taskActionsPanel, "Check task", StudyIcons.Resolve); myPrevTaskButton = addButton(taskActionsPanel, "Prev Task", StudyIcons.Prev); - myNextTaskButton = addButton(taskActionsPanel, "Next Task", StudyIcons.Next); - myRefreshButton = addButton(taskActionsPanel, "Start task again", StudyIcons.Refresh24); + myNextTaskButton = addButton(taskActionsPanel, "Next Task", AllIcons.Actions.Forward); + myRefreshButton = addButton(taskActionsPanel, "Start task again", AllIcons.Actions.Refresh); if (!taskFile.getTask().getUserTests().isEmpty()) { - JButton runButton = addButton(taskActionsPanel, "Run", StudyIcons.Run); + JButton runButton = addButton(taskActionsPanel, "Run", AllIcons.General.Run); runButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -149,7 +208,8 @@ public class StudyEditor implements TextEditor { watchInputButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - StudyEditInputAction studyEditInputAction = (StudyEditInputAction)ActionManager.getInstance().getAction("WatchInputAction"); + StudyEditInputAction studyEditInputAction = + (StudyEditInputAction)ActionManager.getInstance().getAction("WatchInputAction"); studyEditInputAction.showInput(myProject); } }); @@ -165,7 +225,8 @@ public class StudyEditor implements TextEditor { myNextTaskButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - StudyNextStudyTaskAction studyNextTaskAction = (StudyNextStudyTaskAction)ActionManager.getInstance().getAction("NextTaskAction"); + StudyNextStudyTaskAction studyNextTaskAction = + (StudyNextStudyTaskAction)ActionManager.getInstance().getAction("NextTaskAction"); studyNextTaskAction.navigateTask(myProject); } }); @@ -180,7 +241,8 @@ public class StudyEditor implements TextEditor { myRefreshButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - StudyRefreshTaskAction studyRefreshTaskAction = (StudyRefreshTaskAction)ActionManager.getInstance().getAction("RefreshTaskAction"); + StudyRefreshTaskFileAction studyRefreshTaskAction = + (StudyRefreshTaskFileAction)ActionManager.getInstance().getAction("RefreshTaskAction"); studyRefreshTaskAction.refresh(myProject); } }); @@ -300,7 +362,8 @@ public class StudyEditor implements TextEditor { if (fileEditor instanceof StudyEditor) { return (StudyEditor)fileEditor; } - } catch (Exception e) { + } + catch (Exception e) { return null; } return null; @@ -325,8 +388,9 @@ public class StudyEditor implements TextEditor { @NotNull @Override public Editor getEditor() { - if (myDefaultEditor instanceof TextEditor) + if (myDefaultEditor instanceof TextEditor) { return ((TextEditor)myDefaultEditor).getEditor(); + } return EditorFactory.getInstance().createViewer(new DocumentImpl(""), myProject); } diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/highlighting/StudyVisitorFilter.java b/python/edu/learn-python/src/com/jetbrains/python/edu/highlighting/StudyVisitorFilter.java new file mode 100644 index 000000000000..dc7495adb16e --- /dev/null +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/highlighting/StudyVisitorFilter.java @@ -0,0 +1,18 @@ +package com.jetbrains.python.edu.highlighting; + +import com.intellij.psi.PsiFile; +import com.jetbrains.python.edu.StudyTaskManager; +import com.jetbrains.python.inspections.PythonVisitorFilter; +import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection; +import org.jetbrains.annotations.NotNull; + +public class StudyVisitorFilter implements PythonVisitorFilter { + @Override + public boolean isSupported(@NotNull final Class visitorClass, @NotNull final PsiFile file) { + if (StudyTaskManager.getInstance(file.getProject()).getCourse() == null) return true; + if (visitorClass == PyUnresolvedReferencesInspection.class) { + return false; + } + return true; + } +} diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/projectView/StudyDirectoryNode.java b/python/edu/learn-python/src/com/jetbrains/python/edu/projectView/StudyDirectoryNode.java index abf648c5c82a..2f80dba12695 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/projectView/StudyDirectoryNode.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/projectView/StudyDirectoryNode.java @@ -32,7 +32,7 @@ public class StudyDirectoryNode extends PsiDirectoryNode { @Override protected void updateImpl(PresentationData data) { - data.setIcon(StudyIcons.Unchecked); + data.setIcon(StudyIcons.Task); String valueName = myValue.getName(); StudyTaskManager studyTaskManager = StudyTaskManager.getInstance(myProject); Course course = studyTaskManager.getCourse(); @@ -41,7 +41,7 @@ public class StudyDirectoryNode extends PsiDirectoryNode { } if (valueName.equals(myProject.getName())) { data.clearText(); - data.addText(course.getName(), new SimpleTextAttributes(SimpleTextAttributes.STYLE_BOLD, JBColor.BLUE)); + data.addText(course.getName(), new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, JBColor.BLACK)); data.addText(" (" + valueName + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES); return; } @@ -91,15 +91,16 @@ public class StudyDirectoryNode extends PsiDirectoryNode { StudyStatus taskStatus = stateful.getStatus(); switch (taskStatus) { case Unchecked: { - updatePresentation(data, additionalName, JBColor.blue, StudyIcons.Unchecked); + updatePresentation(data, additionalName, JBColor.BLACK, stateful instanceof Lesson ? StudyIcons.Lesson : StudyIcons.Task); break; } case Solved: { - updatePresentation(data, additionalName, new JBColor(new Color(0, 134, 0), new Color(98, 150, 85)), StudyIcons.Checked); + updatePresentation(data, additionalName, new JBColor(new Color(0, 134, 0), new Color(98, 150, 85)), + stateful instanceof Lesson ? StudyIcons.LessonCompl : StudyIcons.TaskCompl); break; } case Failed: { - updatePresentation(data, additionalName, JBColor.RED, StudyIcons.Failed); + updatePresentation(data, additionalName, JBColor.RED, stateful instanceof Lesson ? StudyIcons.Lesson : StudyIcons.TaskProbl); } } } diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.form b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.form index 133c38d4e8f8..8dd6506d710c 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.form +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.form @@ -39,11 +39,9 @@ - + - - - + @@ -67,9 +65,7 @@ - - - + diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.java b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.java index 0f1ec08a8856..6edad63586f0 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyNewProjectPanel.java @@ -2,6 +2,7 @@ package com.jetbrains.python.edu.ui; import com.intellij.facet.ui.FacetValidatorsManager; import com.intellij.facet.ui.ValidationResult; +import com.intellij.icons.AllIcons; import com.intellij.openapi.fileChooser.FileChooser; import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.vfs.VirtualFile; @@ -9,7 +10,6 @@ import com.intellij.util.Consumer; import com.jetbrains.python.edu.StudyDirectoryProjectGenerator; import com.jetbrains.python.edu.StudyUtils; import com.jetbrains.python.edu.course.CourseInfo; -import icons.StudyIcons; import javax.swing.*; import java.awt.event.ActionEvent; @@ -32,6 +32,7 @@ public class StudyNewProjectPanel{ private JPanel myContentPanel; private JLabel myAuthorLabel; private JLabel myDescriptionLabel; + private JLabel myLabel; private final StudyDirectoryProjectGenerator myGenerator; private static final String CONNECTION_ERROR = "Failed to download courses.
Check your Internet connection."; private static final String INVALID_COURSE = "Selected course is invalid"; @@ -56,7 +57,9 @@ public class StudyNewProjectPanel{ } initListeners(); myRefreshButton.setVisible(true); - myRefreshButton.setIcon(StudyIcons.Refresh); + myRefreshButton.setIcon(AllIcons.Actions.Refresh); + + myLabel.setPreferredSize(new JLabel("Project name").getPreferredSize()); } private void initListeners() { -- cgit v1.2.3