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> 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); + } + } } 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())); diff --git a/python/edu/build/pycharm_edu_build.gant b/python/edu/build/pycharm_edu_build.gant index 6e46eea83ad9..3422707bd5fb 100644 --- a/python/edu/build/pycharm_edu_build.gant +++ b/python/edu/build/pycharm_edu_build.gant @@ -366,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/pycharm-eduhelp.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) { @@ -383,7 +383,7 @@ private layoutUnix(Map args, String target) { unixScripts(target, ch, "pycharm.sh", args) unixVMOptions(target, "pycharm") - ant.copy(file: "$home/python/help/pycharm-eduhelp.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) { diff --git a/python/edu/learn-python/resources/courses/introduction_course.zip b/python/edu/learn-python/resources/courses/introduction_course.zip index a36351a01399..a2f7febb1a3b 100644 Binary files a/python/edu/learn-python/resources/courses/introduction_course.zip and b/python/edu/learn-python/resources/courses/introduction_course.zip differ 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..0ffcc1f9dfa9 --- /dev/null +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyState.java @@ -0,0 +1,56 @@ +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(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 StudyEditor getStudyEditor() { + return myStudyEditor; + } + + 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/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..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/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); 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 e126575d9a12..c48395543c8e 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 @@ -52,6 +52,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; @@ -67,6 +68,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); @@ -91,16 +96,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); } diff --git a/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java b/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java index 1af55e00b260..be7bd83cb1ce 100644 --- a/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java +++ b/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java @@ -35,14 +35,15 @@ 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.util.registry.Registry; 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; @@ -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); @@ -147,6 +150,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); + } + } + }); + } + }); } }); }