From 9cfe7e89d42905889aca7dd93d97bca3fe6b1e05 Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Thu, 14 Aug 2014 18:01:31 +0200 Subject: [PATCH 01/11] Open project view by default. --- .../edu/PyCharmEduInitialConfigurator.java | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java b/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java index ecf3d79a6424..b72fb0d094bc 100644 --- a/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java +++ b/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java @@ -35,14 +35,14 @@ import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.keymap.Keymap; import com.intellij.openapi.keymap.ex.KeymapManagerEx; import com.intellij.openapi.keymap.impl.KeymapImpl; +import com.intellij.openapi.project.DumbAwareRunnable; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.project.ProjectManagerAdapter; import com.intellij.openapi.project.ex.ProjectManagerEx; +import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.vfs.VfsUtil; -import com.intellij.openapi.wm.ToolWindowEP; -import com.intellij.openapi.wm.ToolWindowId; -import com.intellij.openapi.wm.WindowManager; +import com.intellij.openapi.wm.*; import com.intellij.platform.DirectoryProjectConfigurator; import com.intellij.platform.PlatformProjectViewOpener; import com.intellij.psi.codeStyle.CodeStyleSettings; @@ -147,6 +147,29 @@ public class PyCharmEduInitialConfigurator { } patchProjectAreaExtensions(project); + + StartupManager.getInstance(project).runWhenProjectIsInitialized(new DumbAwareRunnable() { + @Override + public void run() { + if (project.isDisposed()) return; + + ToolWindowManager.getInstance(project).invokeLater(new Runnable() { + int count = 0; + + public void run() { + if (project.isDisposed()) return; + if (count++ < 3) { // we need to call this after ToolWindowManagerImpl.registerToolWindowsFromBeans + ToolWindowManager.getInstance(project).invokeLater(this); + return; + } + ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow("Project"); + if (toolWindow.getType() != ToolWindowType.SLIDING) { + toolWindow.activate(null); + } + } + }); + } + }); } }); } From b6cd07956a98e57bfbae13887aedc9ac32eb7291 Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Tue, 19 Aug 2014 16:01:14 +0200 Subject: [PATCH 02/11] Turn new settings order on in Edu. --- .../jetbrains/python/edu/PyCharmEduInitialConfigurator.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java b/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java index a217b6fa88cd..be7bd83cb1ce 100644 --- a/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java +++ b/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java @@ -41,6 +41,7 @@ import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.project.ProjectManagerAdapter; import com.intellij.openapi.project.ex.ProjectManagerEx; import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.wm.*; import com.intellij.platform.DirectoryProjectConfigurator; @@ -93,6 +94,8 @@ public class PyCharmEduInitialConfigurator { uiSettings.SHOW_MAIN_TOOLBAR = false; codeInsightSettings.REFORMAT_ON_PASTE = CodeInsightSettings.NO_REFORMAT; + Registry.get("ide.file.settings.order.new").setValue(true); + GeneralSettings.getInstance().setShowTipsOnStartup(false); EditorSettingsExternalizable.getInstance().setVirtualSpace(false); From 4272d582248702500a045060140fe2d62ba82c8c Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Tue, 19 Aug 2014 17:07:54 +0200 Subject: [PATCH 03/11] Fixed edu help file name. --- python/edu/build/pycharm_edu_build.gant | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/edu/build/pycharm_edu_build.gant b/python/edu/build/pycharm_edu_build.gant index 2ef0bed607a7..8790d0a771f2 100644 --- a/python/edu/build/pycharm_edu_build.gant +++ b/python/edu/build/pycharm_edu_build.gant @@ -363,7 +363,7 @@ private layoutWin(Map args, String target) { winScripts(target, ch, "pycharm.bat", args) winVMOptions(target, null, "pycharm.exe") - ant.copy(file: "$home/python/help/pycharmhelp.jar", todir: "$target/help", failonerror: false) + ant.copy(file: "$home/python/help/pycharm-eduhelp.jar", todir: "$target/help", failonerror: false) } private layoutUnix(Map args, String target) { From 8f010994a5a1859705f364da1888606a47a25ea0 Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Tue, 19 Aug 2014 17:12:27 +0200 Subject: [PATCH 04/11] Fix help file name 2. --- python/edu/build/pycharm_edu_build.gant | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/python/edu/build/pycharm_edu_build.gant b/python/edu/build/pycharm_edu_build.gant index 2ef0bed607a7..3422707bd5fb 100644 --- a/python/edu/build/pycharm_edu_build.gant +++ b/python/edu/build/pycharm_edu_build.gant @@ -26,6 +26,9 @@ setProperty("buildName", "PE-$buildNumber") setProperty("ch", "$home/community") setProperty("pythonCommunityHome", "$ch/python") setProperty("pythonEduHome", "$ch/python/edu") +requireProperty("jdk_bundled_mac", "1.7") +def jdk_bundled_version = p("jdk_bundled_mac") == "1.8" ? "jdk8_mac_redist.tar" : "jdk_mac_redist.tar" +ant.copy(file: "${home}/build/jdk/${jdk_bundled_version}", tofile: "${home}/build/jdk/jdk_mac_redist_for_${buildNumber}.tar") // load ApplicationInfo.xml properties ant.xmlproperty(file: "$pythonEduHome/resources/idea/PyCharmEduApplicationInfo.xml", collapseAttributes: "true") @@ -363,7 +366,7 @@ private layoutWin(Map args, String target) { winScripts(target, ch, "pycharm.bat", args) winVMOptions(target, null, "pycharm.exe") - ant.copy(file: "$home/python/help/pycharmhelp.jar", todir: "$target/help", failonerror: false) + ant.copy(file: "$home/python/help/pycharm-eduhelp.jar", todir: "$target/help", failonerror: false) } private layoutUnix(Map args, String target) { @@ -380,7 +383,7 @@ private layoutUnix(Map args, String target) { unixScripts(target, ch, "pycharm.sh", args) unixVMOptions(target, "pycharm") - ant.copy(file: "$home/python/help/pycharmhelp.jar", todir: "$target/help", failonerror: false) + ant.copy(file: "$home/python/help/pycharm-eduhelp.jar", todir: "$target/help", failonerror: false) } private layoutMac(Map _args, String target) { From a7f9b7867463b774d2fa24aff2026bd678131ea7 Mon Sep 17 00:00:00 2001 From: Liana Bakradze Date: Tue, 19 Aug 2014 19:21:32 +0400 Subject: [PATCH 05/11] multi-file smart tests fixed --- .../resources/courses/introduction_course.zip | Bin 93360 -> 93427 bytes .../com/jetbrains/python/edu/StudyState.java | 56 +++ .../jetbrains/python/edu/StudyTestRunner.java | 76 ++++ .../com/jetbrains/python/edu/StudyUtils.java | 42 +- .../python/edu/actions/StudyCheckAction.java | 373 +++++++----------- .../jetbrains/python/edu/course/TaskFile.java | 32 +- .../python/edu/course/TaskWindow.java | 67 +++- .../python/edu/editor/StudyEditor.java | 13 +- 8 files changed, 417 insertions(+), 242 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 diff --git a/python/edu/learn-python/resources/courses/introduction_course.zip b/python/edu/learn-python/resources/courses/introduction_course.zip index f3b24f24b1e2450592ca4b2e41b43f4e52daf7a5..6133ede1c7288a59344876956fd1f0d060d8243b 100644 GIT binary patch delta 21136 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 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..90afbc21830f 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; @@ -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 50454e67b607..8a8c68f6faf7 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,94 +12,80 @@ 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.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.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.ArrayList; -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; - } - - 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 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; } - 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); - } - finally { - StudyUtils.closeSilently(testOutputReader); - } - return TEST_OK; + StudyUtils.flushWindows(taskFile, virtualFile); } } + 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); + } + } + } + + 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; + } + FileEditor fileEditor = FileEditorManager.getInstance(project).getSelectedEditor(virtualFile); + if (fileEditor instanceof StudyEditor) { + StudyEditor studyEditor = (StudyEditor)fileEditor; + taskFile.drawAllWindows(studyEditor.getEditor()); + } + } + } + + public void check(@NotNull final Project project) { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override @@ -108,188 +93,126 @@ 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); + 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); + } + 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!", MessageType.INFO.getPopupBackground(), project); - return; - } - currentTask.setStatus(StudyStatus.Failed, oldStatus); - 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); - } - } - StudyUtils.updateStudyToolWindow(project); - createTestResultPopUp(failedMessage, MessageType.ERROR.getPopupBackground(), 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); - } - catch (ExecutionException e) { - LOG.error(e); - } + FileEditorManager.getInstance(project).openFile(fileToNavigate, true); + IdeFocusManager.getInstance(project).requestFocus(editor.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; + } + 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) { @@ -316,8 +239,6 @@ public class StudyCheckAction extends DumbAwareAction { catch (IOException e) { LOG.error(e); } - - return copy; } 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..0db1a6271dee 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 @@ -217,12 +217,30 @@ 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) { + 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 46e3c641fc8a..14b8340cbd3f 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 @@ -51,6 +51,7 @@ 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; @@ -66,6 +67,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); @@ -90,16 +95,16 @@ 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); } From ac6a644071c522c7ca34b4c74c45013cec3e5458 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Tue, 19 Aug 2014 18:22:42 +0400 Subject: [PATCH 06/11] cleanup --- .../psi/impl/search/SimpleAccessorReferenceSearcher.java | 1 - 1 file changed, 1 deletion(-) diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/search/SimpleAccessorReferenceSearcher.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/SimpleAccessorReferenceSearcher.java index 16870688e738..e047db8b992a 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/search/SimpleAccessorReferenceSearcher.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/search/SimpleAccessorReferenceSearcher.java @@ -54,7 +54,6 @@ public class SimpleAccessorReferenceSearcher extends QueryExecutorBase Date: Tue, 19 Aug 2014 18:22:59 +0400 Subject: [PATCH 07/11] toString() --- .../core-api/src/com/intellij/usageView/UsageInfo.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/platform/core-api/src/com/intellij/usageView/UsageInfo.java b/platform/core-api/src/com/intellij/usageView/UsageInfo.java index 2d3cecc3628b..e67958a21faf 100644 --- a/platform/core-api/src/com/intellij/usageView/UsageInfo.java +++ b/platform/core-api/src/com/intellij/usageView/UsageInfo.java @@ -246,6 +246,15 @@ public class UsageInfo { return result; } + @Override + public String toString() { + PsiReference reference = getReference(); + if (reference == null) { + return super.toString(); + } + return reference.getCanonicalText() + " (" + reference.getClass() + ")"; + } + @Nullable public PsiFile getFile() { return mySmartPointer.getContainingFile(); From 2fd9c401627c4414c09d6fe36d5c232bf240220d Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Tue, 19 Aug 2014 17:37:38 +0200 Subject: [PATCH 08/11] show changed values --- .../evaluate/XDebuggerEditorLinePainter.java | 85 +++++++++++++++++-- 1 file changed, 78 insertions(+), 7 deletions(-) diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/XDebuggerEditorLinePainter.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/XDebuggerEditorLinePainter.java index a3aa8ec43fc5..a85b6d124932 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/XDebuggerEditorLinePainter.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/XDebuggerEditorLinePainter.java @@ -18,8 +18,10 @@ package com.intellij.xdebugger.impl.evaluate; import com.intellij.openapi.editor.EditorLinePainter; import com.intellij.openapi.editor.LineExtensionInfo; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.JBColor; import com.intellij.ui.SimpleColoredText; @@ -30,15 +32,13 @@ import com.intellij.xdebugger.impl.ui.tree.nodes.XValueTextRendererImpl; import org.jetbrains.annotations.NotNull; import java.awt.*; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * @author Konstantin Bulenkov */ public class XDebuggerEditorLinePainter extends EditorLinePainter { + public static final Key> CACHE = Key.create("debug.frame"); @Override public Collection getLineExtensions(@NotNull Project project, @NotNull VirtualFile file, int lineNumber) { if (!Registry.is("ide.debugger.inline")) { @@ -46,6 +46,11 @@ public class XDebuggerEditorLinePainter extends EditorLinePainter { } Map, Set> map = project.getUserData(XVariablesView.DEBUG_VARIABLES); + Map oldValues = project.getUserData(CACHE); + if (oldValues == null) { + oldValues = new HashMap(); + project.putUserData(CACHE, oldValues); + } if (map != null) { Set values = map.get(Pair.create(file, lineNumber)); if (values != null && !values.isEmpty()) { @@ -61,9 +66,29 @@ public class XDebuggerEditorLinePainter extends EditorLinePainter { presentation.renderValue(renderer); } final Color color = new JBColor(new Color(61, 128, 101), new Color(61, 128, 101)); - result.add(new LineExtensionInfo(" " + value.getName() + ": ", color, null, null, Font.PLAIN)); - for (String s : text.getTexts()) { - result.add(new LineExtensionInfo(s, color, null, null, Font.PLAIN)); + final String name = value.getName(); + result.add(new LineExtensionInfo(" " + name + ": ", color, null, null, Font.PLAIN)); + + Variable var = new Variable(name, value.hashCode(), lineNumber); + VariableValue variableValue = oldValues.get(var); + if (variableValue == null) { + variableValue = new VariableValue(text.toString(), null, value.hashCode()); + oldValues.put(var, variableValue); + } + if (variableValue.valueNodeHashCode != value.hashCode()) { + variableValue.old = variableValue.actual; + variableValue.actual = text.toString(); + variableValue.valueNodeHashCode = value.hashCode(); + } + + if (!variableValue.isChanged()) { + for (String s : text.getTexts()) { + result.add(new LineExtensionInfo(s, color, null, null, Font.PLAIN)); + } + } else { + for (String s : text.getTexts()) { + result.add(new LineExtensionInfo(s, new JBColor(new Color(202, 128, 33), new Color(116, 114, 4)), null, null, Font.BOLD)); + } } } return result; @@ -72,4 +97,50 @@ public class XDebuggerEditorLinePainter extends EditorLinePainter { return null; } + + static class Variable { + private int lineNumber; + private String name; + + public Variable(String name, int nodeHashCode, int lineNumber) { + this.lineNumber = lineNumber; + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Variable variable = (Variable)o; + + if (lineNumber != variable.lineNumber) return false; + if (!name.equals(variable.name)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = lineNumber; + result = 31 * result + name.hashCode(); + return result; + } + } + + static class VariableValue { + private String actual; + private String old; + private int valueNodeHashCode; + + public VariableValue(String actual, String old, int valueNodeHashCode) { + this.actual = actual; + this.old = old; + this.valueNodeHashCode = valueNodeHashCode; + } + + public boolean isChanged() { + return old != null && !StringUtil.equals(actual, old); + } + } } From 3c66e8bdf779f9a3d23f9b3dc70c680c6e0741cf Mon Sep 17 00:00:00 2001 From: Liana Bakradze Date: Tue, 19 Aug 2014 19:40:12 +0400 Subject: [PATCH 09/11] EDU-128 run tool window hidden when navigating between tasks --- .../python/edu/actions/StudyTaskNavigationAction.java | 3 +++ 1 file changed, 3 insertions(+) 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..ea696eb15e91 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 @@ -11,6 +11,8 @@ 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.ToolWindowId; +import com.intellij.openapi.wm.ToolWindowManager; import com.jetbrains.python.edu.StudyTaskManager; import com.jetbrains.python.edu.course.Lesson; import com.jetbrains.python.edu.course.Task; @@ -82,6 +84,7 @@ abstract public class StudyTaskNavigationAction extends DumbAwareAction { if (shouldBeActive != null) { FileEditorManager.getInstance(project).openFile(shouldBeActive, true); } + ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.RUN).hide(null); } protected abstract JButton getButton(StudyEditor selectedStudyEditor); From a96914963d06a291f9213304f62e92afe8136e39 Mon Sep 17 00:00:00 2001 From: Nadya Zabrodina Date: Tue, 19 Aug 2014 19:47:23 +0400 Subject: [PATCH 10/11] fix already disposed --- .../hg4idea/src/org/zmlx/hg4idea/provider/HgChangeProvider.java | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgChangeProvider.java b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgChangeProvider.java index daaf6ec33fc5..02e49354f3ab 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgChangeProvider.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgChangeProvider.java @@ -68,6 +68,7 @@ public class HgChangeProvider implements ChangeProvider { public void getChanges(VcsDirtyScope dirtyScope, ChangelistBuilder builder, ProgressIndicator progress, ChangeListManagerGate addGate) throws VcsException { + if (myProject.isDisposed()) return; final Collection changes = new HashSet(); changes.addAll(process(builder, dirtyScope.getRecursivelyDirtyDirectories())); changes.addAll(process(builder, dirtyScope.getDirtyFiles())); From 42131dafc56ac6afa5c333aad8b1ef1a7400dbf1 Mon Sep 17 00:00:00 2001 From: Nadya Zabrodina Date: Tue, 19 Aug 2014 19:53:28 +0400 Subject: [PATCH 11/11] focus problem when start editing fixed --- .../intellij/dvcs/push/ui/RepositoryWithBranchPanel.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryWithBranchPanel.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryWithBranchPanel.java index c1afcdd52839..131c8e2a19a8 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryWithBranchPanel.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryWithBranchPanel.java @@ -54,6 +54,7 @@ public class RepositoryWithBranchPanel extends NonOpaquePanel implements TreeCel super(); setLayout(new BorderLayout()); myRepositoryCheckbox = new JBCheckBox(); + myRepositoryCheckbox.setFocusable(false); myRepositoryCheckbox.setOpaque(false); myRepositoryCheckbox.addActionListener(new ActionListener() { @Override @@ -77,15 +78,17 @@ public class RepositoryWithBranchPanel extends NonOpaquePanel implements TreeCel protected void updateBorder(@NotNull final EditorEx editor) { } }; - myDestBranchTextField.setBorder(UIUtil.getTableFocusCellHighlightBorder());//getTextFieldBorder()); + myDestBranchTextField.setBorder(UIUtil.getTableFocusCellHighlightBorder()); myDestBranchTextField.setOneLineMode(true); myDestBranchTextField.setOpaque(true); - myDestBranchTextField.addFocusListener(new FocusAdapter() { + FocusAdapter focusListener = new FocusAdapter() { @Override public void focusGained(FocusEvent e) { myDestBranchTextField.selectAll(); } - }); + }; + myDestBranchTextField.addFocusListener(focusListener); + addFocusListener(focusListener); myTextRenderer = new ColoredTreeCellRenderer() { public void customizeCellRenderer(@NotNull JTree tree,