summaryrefslogtreecommitdiff
path: root/python/edu
diff options
context:
space:
mode:
authorTor Norbye <tnorbye@google.com>2014-09-18 13:38:58 -0700
committerTor Norbye <tnorbye@google.com>2014-09-18 13:38:58 -0700
commitb5fb31ef6a38f19404859755dbd2e345215b97bf (patch)
treee8787c45e494dfcc558faf0f75956f8785c39b94 /python/edu
parente222a9e1e66670a56e926a6b0f3e10231eeeb1fb (diff)
parente782c57d74000722f9db4c9426317410520670c6 (diff)
downloadidea-b5fb31ef6a38f19404859755dbd2e345215b97bf.tar.gz
Merge remote-tracking branch 'aosp/upstream-master' into merge
Conflicts: .idea/libraries/asm_tools.xml .idea/libraries/bouncy_castle.xml .idea/libraries/builder_model.xml .idea/libraries/commons_compress.xml .idea/libraries/easymock_tools.xml .idea/libraries/freemarker_2_3_20.xml .idea/libraries/guava_tools.xml .idea/libraries/kxml2.xml .idea/libraries/lombok_ast.xml .idea/libraries/mockito.xml .idea/modules.xml .idea/vcs.xml build/scripts/layouts.gant updater/src/com/intellij/updater/Runner.java Change-Id: I8e1c173e00cd76c855b8a98543b0a0edfdd99d12
Diffstat (limited to 'python/edu')
-rw-r--r--python/edu/build/pycharm_edu_build.gant8
-rw-r--r--python/edu/build/upload_pythonInfo.xml2
-rw-r--r--python/edu/course-creator/resources/META-INF/plugin.xml14
-rw-r--r--python/edu/course-creator/resources/fileTemplates/internal/task.answer.ft1
-rw-r--r--python/edu/course-creator/resources/fileTemplates/internal/task.py.ft1
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/AnswerFileType.java40
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/AnswerFileTypeFactory.java28
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCEditorFactoryListener.java13
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectComponent.java141
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectService.java52
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCRefactoringElementListenerProvider.java96
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCRunTests.java284
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/RunTestsLineMarker.java109
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/StudyDocumentListener.java5
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java5
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCRename.java97
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCRenameLesson.java48
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCRenameTask.java57
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCShowPreview.java104
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateCourseArchive.java188
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java14
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFile.java6
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Course.java14
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Lesson.java31
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Task.java20
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java27
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java11
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/projectView/CCDirectoryNode.java2
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/projectView/CCTreeStructureProvider.java18
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.form2
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java6
-rw-r--r--python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java5
-rw-r--r--python/edu/learn-python/resources/META-INF/plugin.xml7
-rw-r--r--python/edu/learn-python/resources/courses/introduction_course.zipbin93557 -> 95274 bytes
-rw-r--r--python/edu/learn-python/src/com/jetbrains/python/edu/StudyDirectoryProjectGenerator.java2
-rw-r--r--python/edu/learn-python/src/com/jetbrains/python/edu/StudyTestRunner.java4
-rw-r--r--python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyIntroductionCourseAction.java75
-rw-r--r--python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNewProject.java34
-rw-r--r--python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskFileAction.java211
-rw-r--r--python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyReloadCourseAction.java134
-rw-r--r--python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyTaskNavigationAction.java7
-rw-r--r--python/edu/learn-python/src/com/jetbrains/python/edu/projectView/StudyDirectoryNode.java55
-rw-r--r--python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyToolWindowFactory.java11
-rw-r--r--python/edu/resources/idea/PyCharmEduApplicationInfo.xml2
44 files changed, 1723 insertions, 268 deletions
diff --git a/python/edu/build/pycharm_edu_build.gant b/python/edu/build/pycharm_edu_build.gant
index 28d829e68b34..356df8c7d4c7 100644
--- a/python/edu/build/pycharm_edu_build.gant
+++ b/python/edu/build/pycharm_edu_build.gant
@@ -158,7 +158,6 @@ public layoutEducational(String classesPath, Set usedJars) {
usedJars = collectUsedJars(modules(), approvedJars(), ["/ant/"], null)
}
- def appInfo = appInfoFile()
def paths = new Paths(home)
buildSearchableOptions("${projectBuilder.moduleOutput(findModule("platform-resources"))}/search", [], {
projectBuilder.moduleRuntimeClasspath(findModule("main_pycharm_edu"), false).each {
@@ -166,8 +165,9 @@ public layoutEducational(String classesPath, Set usedJars) {
}
}, "-Didea.platform.prefix=PyCharmEdu -Didea.no.jre.check=true")
+ def appInfo = appInfoFile()
if (!dryRun) {
- wireBuildDate("PE-${buildNumber}", appInfo)
+ wireBuildDate(buildNumber, appInfo)
}
Map args = [
@@ -223,7 +223,7 @@ private layoutPlugins(layouts) {
}
private String appInfoFile() {
- return "$pythonEduHome/resources/idea/PyCharmEduApplicationInfo.xml"
+ return "$home/out/pycharmEDU/classes/production/python-educational/idea/PyCharmEduApplicationInfo.xml"
}
private layoutFull(Map args, String target, Set usedJars) {
@@ -405,7 +405,7 @@ private layoutMac(Map _args, String target) {
args.icns = "$pythonCommunityHome/resources/PyCharmCore.icns"
args.bundleIdentifier = "com.jetbrains.pycharm"
args.platform_prefix = "PyCharmEdu"
- args.help_id = "PY"
+ args.help_id = "PE"
args."idea.properties.path" = "${paths.distAll}/bin/idea.properties"
args."idea.properties" = ["idea.no.jre.check": true, "ide.mac.useNativeClipboard": "false"];
layoutMacApp(target, ch, args)
diff --git a/python/edu/build/upload_pythonInfo.xml b/python/edu/build/upload_pythonInfo.xml
index f8d9477d1a3f..0633ad82f8c0 100644
--- a/python/edu/build/upload_pythonInfo.xml
+++ b/python/edu/build/upload_pythonInfo.xml
@@ -20,7 +20,7 @@
</fileset>
<fileset dir="${home}/community/lib">
<include name="commons-net-3.1.jar"/>
- <include name="jsch-0.1.50.jar"/>
+ <include name="jsch-0.1.51.jar"/>
</fileset>
</classpath>
diff --git a/python/edu/course-creator/resources/META-INF/plugin.xml b/python/edu/course-creator/resources/META-INF/plugin.xml
index 6a9a9ea90276..0e7f75c90051 100644
--- a/python/edu/course-creator/resources/META-INF/plugin.xml
+++ b/python/edu/course-creator/resources/META-INF/plugin.xml
@@ -26,6 +26,10 @@
<codeInsight.lineMarkerProvider language="Python"
implementationClass="org.jetbrains.plugins.coursecreator.highlighting.CCTaskLineMarkerProvider"/>
<treeStructureProvider implementation="org.jetbrains.plugins.coursecreator.projectView.CCTreeStructureProvider"/>
+ <codeInsight.lineMarkerProvider language="Python" implementationClass="org.jetbrains.plugins.coursecreator.RunTestsLineMarker"/>
+ <fileTypeFactory implementation="org.jetbrains.plugins.coursecreator.AnswerFileTypeFactory" />
+ <refactoring.elementListenerProvider implementation="org.jetbrains.plugins.coursecreator.CCRefactoringElementListenerProvider"/>
+ <errorHandler implementation="com.intellij.diagnostic.ITNReporter"/>
</extensions>
<application-components>
@@ -52,9 +56,19 @@
<action id="AddTaskWindow" class="org.jetbrains.plugins.coursecreator.actions.AddTaskWindow">
<add-to-group group-id="EditorPopupMenu" anchor="before" relative-to-action="CopyReference"/>
</action>
+ <action id="ShowPreview" class="org.jetbrains.plugins.coursecreator.actions.CCShowPreview">
+ <add-to-group group-id="ProjectViewPopupMenu" anchor="first"/>
+ </action>
+ <action id="RenameLesson" class="org.jetbrains.plugins.coursecreator.actions.CCRenameLesson">
+ <add-to-group group-id="ProjectViewPopupMenu" anchor="before" relative-to-action="CutCopyPasteGroup"/>
+ </action>
+ <action id="RenameTask" class="org.jetbrains.plugins.coursecreator.actions.CCRenameTask">
+ <add-to-group group-id="ProjectViewPopupMenu" anchor="before" relative-to-action="CutCopyPasteGroup"/>
+ </action>
<action id="PackCourse" class="org.jetbrains.plugins.coursecreator.actions.CreateCourseArchive">
<add-to-group group-id="MainToolBar" anchor="last" />
</action>
+ <action id="CCRunTests" class="org.jetbrains.plugins.coursecreator.CCRunTests" text="Run tests" description="Run tests"/>
</actions>
</idea-plugin> \ No newline at end of file
diff --git a/python/edu/course-creator/resources/fileTemplates/internal/task.answer.ft b/python/edu/course-creator/resources/fileTemplates/internal/task.answer.ft
new file mode 100644
index 000000000000..f0a4bcecdd24
--- /dev/null
+++ b/python/edu/course-creator/resources/fileTemplates/internal/task.answer.ft
@@ -0,0 +1 @@
+# TODO: type solution here
diff --git a/python/edu/course-creator/resources/fileTemplates/internal/task.py.ft b/python/edu/course-creator/resources/fileTemplates/internal/task.py.ft
deleted file mode 100644
index 0256e108786c..000000000000
--- a/python/edu/course-creator/resources/fileTemplates/internal/task.py.ft
+++ /dev/null
@@ -1 +0,0 @@
-# TODO: type solution here \ No newline at end of file
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/AnswerFileType.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/AnswerFileType.java
new file mode 100644
index 000000000000..3b8189aadffc
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/AnswerFileType.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.plugins.coursecreator;
+
+import com.jetbrains.python.PythonFileType;
+import org.jetbrains.annotations.NotNull;
+
+public class AnswerFileType extends PythonFileType {
+
+ @NotNull
+ @Override
+ public String getDefaultExtension() {
+ return "answer";
+ }
+
+ @NotNull
+ @Override
+ public String getDescription() {
+ return "Answer file";
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ return "Answer";
+ }
+}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/AnswerFileTypeFactory.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/AnswerFileTypeFactory.java
new file mode 100644
index 000000000000..e4adcbc5b386
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/AnswerFileTypeFactory.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.plugins.coursecreator;
+
+
+import com.intellij.openapi.fileTypes.FileTypeConsumer;
+import com.intellij.openapi.fileTypes.FileTypeFactory;
+import org.jetbrains.annotations.NotNull;
+
+public class AnswerFileTypeFactory extends FileTypeFactory {
+ @Override
+ public void createFileTypes(@NotNull FileTypeConsumer fileTypeConsumer) {
+ fileTypeConsumer.consume(AnswerFileType.INSTANCE, "answer");
+ }
+}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCEditorFactoryListener.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCEditorFactoryListener.java
index 1eb8690aad22..acb0d43d0d12 100644
--- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCEditorFactoryListener.java
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCEditorFactoryListener.java
@@ -35,6 +35,9 @@ public class CCEditorFactoryListener implements EditorFactoryListener {
final Lesson lesson = course.getLesson(lessonDir.getName());
final Task task = lesson.getTask(taskDir.getName());
final TaskFile taskFile = task.getTaskFile(virtualFile.getName());
+ if (taskFile == null) {
+ return;
+ }
TaskFileModificationListener listener = new TaskFileModificationListener(taskFile);
CCProjectService.addDocumentListener(editor.getDocument(), listener);
editor.getDocument().addDocumentListener(listener);
@@ -54,13 +57,10 @@ public class CCEditorFactoryListener implements EditorFactoryListener {
editor.getSelectionModel().removeSelection();
}
- private class TaskFileModificationListener extends StudyDocumentListener {
-
- private final TaskFile myTaskFile;
+ private static class TaskFileModificationListener extends StudyDocumentListener {
public TaskFileModificationListener(TaskFile taskFile) {
super(taskFile);
- myTaskFile = taskFile;
}
@Override
@@ -71,10 +71,5 @@ public class CCEditorFactoryListener implements EditorFactoryListener {
taskWindow.setReplacementLength(taskWindow.getLength() + 1);
}
}
-
- @Override
- protected boolean needModify() {
- return myTaskFile.isTrackChanges();
- }
}
}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectComponent.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectComponent.java
index 34de943d2ad0..8b524dbffd4b 100644
--- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectComponent.java
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectComponent.java
@@ -1,22 +1,33 @@
package org.jetbrains.plugins.coursecreator;
+import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ProjectComponent;
+import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.event.EditorFactoryEvent;
import com.intellij.openapi.editor.impl.EditorFactoryImpl;
-import com.intellij.openapi.fileEditor.FileEditor;
-import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.fileEditor.*;
import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileAdapter;
+import com.intellij.openapi.vfs.VirtualFileEvent;
+import com.intellij.openapi.vfs.VirtualFileManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.coursecreator.format.Course;
+import org.jetbrains.plugins.coursecreator.format.Lesson;
+import org.jetbrains.plugins.coursecreator.format.Task;
+import org.jetbrains.plugins.coursecreator.format.TaskFile;
+
+import java.io.IOException;
public class CCProjectComponent implements ProjectComponent {
+ private static final Logger LOG = Logger.getInstance(CCProjectComponent.class.getName());
private final Project myProject;
+ private FileDeletedListener myListener;
public CCProjectComponent(Project project) {
myProject = project;
@@ -37,15 +48,46 @@ public class CCProjectComponent implements ProjectComponent {
StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new Runnable() {
@Override
public void run() {
- Course course = CCProjectService.getInstance(myProject).getCourse();
+ final Course course = CCProjectService.getInstance(myProject).getCourse();
if (course != null) {
- EditorFactory.getInstance().addEditorFactoryListener(new CCEditorFactoryListener(), myProject);
+ myProject.getMessageBus().connect(myProject).subscribe(
+ FileEditorManagerListener.FILE_EDITOR_MANAGER, new FileEditorManagerAdapter() {
+ @Override
+ public void selectionChanged(@NotNull FileEditorManagerEvent event) {
+ final VirtualFile oldFile = event.getOldFile();
+ if (oldFile == null) {
+ return;
+ }
+ if (CCProjectService.getInstance(myProject).isTaskFile(oldFile)) {
+ FileEditorManager.getInstance(myProject).closeFile(oldFile);
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ oldFile.delete(myProject);
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ }
+ });
+ }
+ }
+ });
+ myListener = new FileDeletedListener();
+ VirtualFileManager.getInstance().addVirtualFileListener(myListener);
+ final CCEditorFactoryListener editorFactoryListener = new CCEditorFactoryListener();
+ EditorFactory.getInstance().addEditorFactoryListener(editorFactoryListener, myProject);
VirtualFile[] files = FileEditorManager.getInstance(myProject).getOpenFiles();
for (VirtualFile file : files) {
+ if (CCProjectService.getInstance(myProject).isTaskFile(file)) {
+ FileEditorManager.getInstance(myProject).closeFile(file);
+ continue;
+ }
FileEditor fileEditor = FileEditorManager.getInstance(myProject).getSelectedEditor(file);
if (fileEditor instanceof PsiAwareTextEditorImpl) {
Editor editor = ((PsiAwareTextEditorImpl)fileEditor).getEditor();
- new CCEditorFactoryListener().editorCreated(new EditorFactoryEvent(new EditorFactoryImpl(ProjectManager.getInstance()), editor ));
+ editorFactoryListener.editorCreated(new EditorFactoryEvent(new EditorFactoryImpl(ProjectManager.getInstance()), editor));
}
}
}
@@ -54,5 +96,94 @@ public class CCProjectComponent implements ProjectComponent {
}
public void projectClosed() {
+ if (myListener != null) {
+ VirtualFileManager.getInstance().removeVirtualFileListener(myListener);
+ }
+ }
+
+ private class FileDeletedListener extends VirtualFileAdapter {
+
+ @Override
+ public void fileDeleted(@NotNull VirtualFileEvent event) {
+ if (myProject.isDisposed() || !myProject.isOpen()) {
+ return;
+ }
+ Course course = CCProjectService.getInstance(myProject).getCourse();
+ if (course == null) {
+ return;
+ }
+ VirtualFile removedFile = event.getFile();
+ if (removedFile.getName().contains(".answer")) {
+ deleteTaskFile(course, removedFile);
+ }
+ if (removedFile.getName().contains("task")) {
+ deleteTask(course, removedFile);
+ }
+ if (removedFile.getName().contains("lesson")) {
+ deleteLesson(course, removedFile);
+ }
+ }
+
+ private void deleteLesson(Course course, VirtualFile file) {
+ VirtualFile courseDir = file.getParent();
+ if (!courseDir.getName().equals(myProject.getName())) {
+ return;
+ }
+ Lesson lesson = course.getLesson(file.getName());
+ if (lesson != null) {
+ course.getLessons().remove(lesson);
+ course.getLessonsMap().remove(file.getName());
+ }
+ }
+
+ private void deleteTask(Course course, VirtualFile removedFile) {
+ VirtualFile lessonDir = removedFile.getParent();
+ if (lessonDir == null || !lessonDir.getName().contains("lesson")) {
+ return;
+ }
+ VirtualFile courseDir = lessonDir.getParent();
+ if (!courseDir.getName().equals(myProject.getName())) {
+ return;
+ }
+ Lesson lesson = course.getLesson(lessonDir.getName());
+ if (lesson == null) {
+ return;
+ }
+ Task task = lesson.getTask(removedFile.getName());
+ if (task == null) {
+ return;
+ }
+ lesson.getTaskList().remove(task);
+ lesson.getTasksMap().remove(removedFile.getName());
+ }
+
+ private void deleteTaskFile(Course course, VirtualFile removedFile) {
+ VirtualFile taskDir = removedFile.getParent();
+ if (taskDir == null || !taskDir.getName().contains("task")) {
+ return;
+ }
+ VirtualFile lessonDir = taskDir.getParent();
+ if (lessonDir == null || !lessonDir.getName().contains("lesson")) {
+ return;
+ }
+ VirtualFile courseDir = lessonDir.getParent();
+ if (!courseDir.getName().equals(myProject.getName())) {
+ return;
+ }
+ Lesson lesson = course.getLesson(lessonDir.getName());
+ if (lesson == null) {
+ return;
+ }
+ Task task = lesson.getTask(taskDir.getName());
+ if (task == null) {
+ return;
+ }
+ TaskFile taskFile = task.getTaskFile(removedFile.getName());
+ if (taskFile == null) {
+ return;
+ }
+ String name = CCProjectService.getRealTaskFileName(removedFile.getName());
+ task.getTaskFiles().remove(name);
+ }
}
}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectService.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectService.java
index 1e38bab865cb..c06925e9a453 100644
--- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectService.java
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectService.java
@@ -32,6 +32,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.coursecreator.format.*;
import java.io.File;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -70,6 +71,9 @@ public class CCProjectService implements PersistentStateComponent<Element> {
@Override
public void loadState(Element el) {
myCourse = XmlSerializer.deserialize(el.getChild(COURSE_ELEMENT), Course.class);
+ if (myCourse != null) {
+ myCourse.init();
+ }
}
public static CCProjectService getInstance(@NotNull Project project) {
@@ -115,7 +119,7 @@ public class CCProjectService implements PersistentStateComponent<Element> {
}
List<TaskWindow> taskWindows = taskFile.getTaskWindows();
for (TaskWindow taskWindow : taskWindows) {
- taskWindow.drawHighlighter(editor);
+ taskWindow.drawHighlighter(editor, false);
}
}
@@ -131,8 +135,52 @@ public class CCProjectService implements PersistentStateComponent<Element> {
myDocumentListeners.remove(document);
}
- public static boolean indexIsValid(int index, List<TaskWindow> collection) {
+ public static boolean indexIsValid(int index, Collection collection) {
int size = collection.size();
return index >= 0 && index < size;
}
+
+ public boolean isTaskFile(VirtualFile file) {
+ if (myCourse == null || file == null) {
+ return false;
+ }
+ VirtualFile taskDir = file.getParent();
+ if (taskDir != null) {
+ String taskDirName = taskDir.getName();
+ if (taskDirName.contains("task")) {
+ VirtualFile lessonDir = taskDir.getParent();
+ if (lessonDir != null) {
+ String lessonDirName = lessonDir.getName();
+ int lessonIndex = getIndex(lessonDirName, "lesson");
+ List<Lesson> lessons = myCourse.getLessons();
+ if (!indexIsValid(lessonIndex, lessons)) {
+ return false;
+ }
+ Lesson lesson = lessons.get(lessonIndex);
+ int taskIndex = getIndex(taskDirName, "task");
+ List<Task> tasks = lesson.getTaskList();
+ if (!indexIsValid(taskIndex, tasks)) {
+ return false;
+ }
+ Task task = tasks.get(taskIndex);
+ return task.isTaskFile(file.getName());
+ }
+ }
+ }
+ return false;
+ }
+
+ public static int getIndex(@NotNull final String fullName, @NotNull final String logicalName) {
+ if (!fullName.contains(logicalName)) {
+ throw new IllegalArgumentException();
+ }
+ return Integer.parseInt(fullName.substring(logicalName.length())) - 1;
+ }
+ public static String getRealTaskFileName(String name) {
+ if (!name.contains(".answer")) {
+ return null;
+ }
+ int nameEnd = name.indexOf(".answer");
+ return name.substring(0, nameEnd) + ".py";
+ }
}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCRefactoringElementListenerProvider.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCRefactoringElementListenerProvider.java
new file mode 100644
index 000000000000..601e4fc7cb40
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCRefactoringElementListenerProvider.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.plugins.coursecreator;
+
+import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.refactoring.listeners.RefactoringElementAdapter;
+import com.intellij.refactoring.listeners.RefactoringElementListener;
+import com.intellij.refactoring.listeners.RefactoringElementListenerProvider;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.coursecreator.format.Course;
+import org.jetbrains.plugins.coursecreator.format.Lesson;
+import org.jetbrains.plugins.coursecreator.format.Task;
+import org.jetbrains.plugins.coursecreator.format.TaskFile;
+
+import java.util.Map;
+
+public class CCRefactoringElementListenerProvider implements RefactoringElementListenerProvider {
+ @Nullable
+ @Override
+ public RefactoringElementListener getListener(PsiElement element) {
+ return new CCRenameListener(element);
+ }
+
+
+ static class CCRenameListener extends RefactoringElementAdapter {
+
+ private String myElementName;
+
+ public CCRenameListener(PsiElement element) {
+ if (element instanceof PsiFile) {
+ PsiFile psiFile = (PsiFile)element;
+ myElementName = psiFile.getName();
+ }
+ }
+
+ @Override
+ protected void elementRenamedOrMoved(@NotNull PsiElement newElement) {
+ if (newElement instanceof PsiFile && myElementName != null) {
+ PsiFile psiFile = (PsiFile)newElement;
+ if (myElementName.contains(".answer")) {
+ //this is task file
+ renameTaskFile(psiFile, myElementName);
+ }
+ }
+ }
+
+ private static void renameTaskFile(PsiFile file, String oldName) {
+ PsiDirectory taskDir = file.getContainingDirectory();
+ Course course = CCProjectService.getInstance(file.getProject()).getCourse();
+ if (course == null) {
+ return;
+ }
+ if (taskDir == null || !taskDir.getName().contains("task")) {
+ return;
+ }
+ PsiDirectory lessonDir = taskDir.getParent();
+ if (lessonDir == null || !lessonDir.getName().contains("lesson")) {
+ return;
+ }
+ Lesson lesson = course.getLesson(lessonDir.getName());
+ if (lesson == null) {
+ return;
+ }
+ Task task = lesson.getTask(taskDir.getName());
+ if (task == null) {
+ return;
+ }
+ Map<String, TaskFile> taskFiles = task.getTaskFiles();
+ TaskFile taskFile = task.getTaskFile(oldName);
+ String realTaskFileName = CCProjectService.getRealTaskFileName(oldName);
+ taskFiles.remove(realTaskFileName);
+ taskFiles.put(CCProjectService.getRealTaskFileName(file.getName()), taskFile);
+ }
+
+ @Override
+ public void undoElementMovedOrRenamed(@NotNull PsiElement newElement, @NotNull String oldQualifiedName) {
+
+ }
+ }
+}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCRunTests.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCRunTests.java
new file mode 100644
index 000000000000..d078972e3f56
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCRunTests.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.plugins.coursecreator;
+
+import com.intellij.execution.*;
+import com.intellij.execution.actions.ConfigurationContext;
+import com.intellij.execution.configurations.ConfigurationFactory;
+import com.intellij.execution.executors.DefaultRunExecutor;
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.projectView.ProjectView;
+import com.intellij.openapi.actionSystem.AnAction;
+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;
+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.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.util.containers.HashMap;
+import com.jetbrains.python.run.PythonConfigurationType;
+import com.jetbrains.python.run.PythonRunConfiguration;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.coursecreator.actions.CreateCourseArchive;
+import org.jetbrains.plugins.coursecreator.format.*;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Map;
+
+public class CCRunTests extends AnAction {
+ private static final Logger LOG = Logger.getInstance(CCRunTests.class.getName());
+
+ public CCRunTests() {
+ getTemplatePresentation().setIcon(AllIcons.Actions.Lightning);
+ }
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ Presentation presentation = e.getPresentation();
+ final ConfigurationContext context = ConfigurationContext.getFromContext(e.getDataContext());
+ Location location = context.getLocation();
+ if (location == null) {
+ return;
+ }
+ PsiElement psiElement = location.getPsiElement();
+ PsiFile psiFile = psiElement.getContainingFile();
+ if (psiFile != null && psiFile.getName().contains(".answer")) {
+ presentation.setEnabled(true);
+ presentation.setText("Run tests from '" + psiFile.getName() + "'");
+ }
+ else {
+ presentation.setEnabled(false);
+ }
+ }
+
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ final ConfigurationContext context = ConfigurationContext.getFromContext(e.getDataContext());
+ run(context);
+ }
+
+ public static void run(final @NotNull ConfigurationContext context) {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ final Project project = context.getProject();
+ PsiElement location = context.getPsiLocation();
+ final Course course = CCProjectService.getInstance(project).getCourse();
+ if (course == null || location == null) {
+ return;
+ }
+ PsiFile psiFile = location.getContainingFile();
+ final VirtualFile virtualFile = psiFile.getVirtualFile();
+ final VirtualFile taskDir = virtualFile.getParent();
+ if (taskDir == null) {
+ return;
+ }
+ final Task task = getTask(course, taskDir);
+ if (task == null) {
+ return;
+ }
+ for (final Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
+ final String name = entry.getKey();
+ createTestEnvironment(taskDir, name, entry.getValue(), project);
+ VirtualFile testFile = taskDir.findChild("tests.py");
+ if (testFile == null) {
+ return;
+ }
+ executeTests(project, virtualFile, taskDir, testFile);
+ clearTestEnvironment(taskDir, project);
+ }
+ }
+ });
+ }
+
+ private static void createTestEnvironment(@NotNull final VirtualFile taskDir, final String fileName, @NotNull final TaskFile taskFile,
+ @NotNull final Project project) {
+ try {
+ String answerFileName = FileUtil.getNameWithoutExtension(fileName) + ".answer";
+ final VirtualFile answerFile = taskDir.findChild(answerFileName);
+ if (answerFile == null) {
+ LOG.debug("could not find answer file " + answerFileName);
+ return;
+ }
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ final FileDocumentManager documentManager = FileDocumentManager.getInstance();
+ documentManager.saveAllDocuments();
+ }
+ });
+ answerFile.copy(project, taskDir, fileName);
+ flushWindows(taskFile, answerFile);
+ createResourceFiles(answerFile, project);
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ }
+
+ private static void clearTestEnvironment(@NotNull final VirtualFile taskDir, @NotNull final Project project) {
+ try {
+ VirtualFile ideaDir = project.getBaseDir().findChild(".idea");
+ if (ideaDir == null) {
+ LOG.debug("idea directory doesn't exist");
+ return;
+ }
+ VirtualFile courseResourceDir = ideaDir.findChild("course");
+ if (courseResourceDir == null) {
+ return;
+ }
+ courseResourceDir.delete(project);
+ VirtualFile[] taskDirChildren = taskDir.getChildren();
+ for (VirtualFile file : taskDirChildren) {
+ if (file.getName().contains("_windows")) {
+ file.delete(project);
+ }
+ if (CCProjectService.getInstance(project).isTaskFile(file)) {
+ file.delete(project);
+ }
+ }
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ }
+
+ private static void executeTests(@NotNull final Project project,
+ @NotNull final VirtualFile virtualFile,
+ @NotNull final VirtualFile taskDir,
+ @NotNull final VirtualFile testFile) {
+ final ConfigurationFactory factory = PythonConfigurationType.getInstance().getConfigurationFactories()[0];
+ final RunnerAndConfigurationSettings settings =
+ RunManager.getInstance(project).createRunConfiguration("test", factory);
+
+ final PythonRunConfiguration configuration = (PythonRunConfiguration)settings.getConfiguration();
+ configuration.setScriptName(testFile.getPath());
+ VirtualFile userFile = taskDir.findChild(virtualFile.getNameWithoutExtension() + ".py");
+ if (userFile == null) {
+ return;
+ }
+ VirtualFile ideaDir = project.getBaseDir().findChild(".idea");
+ if (ideaDir == null) {
+ return;
+ }
+ VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
+ ProjectView.getInstance(project).refresh();
+ VirtualFile courseDir = ideaDir.findChild("course");
+ if (courseDir == null) {
+ return;
+ }
+ configuration.setScriptParameters(courseDir.getPath() + " " + userFile.getPath());
+ Executor executor = DefaultRunExecutor.getRunExecutorInstance();
+ ProgramRunnerUtil.executeConfiguration(project, settings, executor);
+ }
+
+ @Nullable
+ private static Task getTask(@NotNull final Course course, @NotNull final VirtualFile taskDir) {
+ if (!taskDir.getName().contains("task")) {
+ return null;
+ }
+ VirtualFile lessonDir = taskDir.getParent();
+ if (lessonDir == null || !lessonDir.getName().contains("lesson")) {
+ return null;
+ }
+ Lesson lesson = course.getLesson(lessonDir.getName());
+ if (lesson == null) {
+ return null;
+ }
+ return lesson.getTask(taskDir.getName());
+ }
+
+
+ //some tests could compare task files after user modifications with initial task files
+ private static void createResourceFiles(@NotNull final VirtualFile file, @NotNull final Project project) {
+ VirtualFile taskDir = file.getParent();
+ int index = CCProjectService.getIndex(taskDir.getName(), "task");
+ VirtualFile lessonDir = taskDir.getParent();
+ int lessonIndex = CCProjectService.getIndex(lessonDir.getName(), "lesson");
+ Course course = CCProjectService.getInstance(project).getCourse();
+ if (course == null) {
+ return;
+ }
+ VirtualFile ideaDir = project.getBaseDir().findChild(".idea");
+ assert ideaDir != null;
+ try {
+ VirtualFile taskResourceDir = ideaDir.createChildDirectory(project, "course").createChildDirectory(project, lessonDir.getName())
+ .createChildDirectory(project, taskDir.getName());
+ if (CCProjectService.indexIsValid(lessonIndex, course.getLessons())) {
+ Lesson lesson = course.getLessons().get(lessonIndex);
+ if (CCProjectService.indexIsValid(index, lesson.getTaskList())) {
+ Task task = lesson.getTaskList().get(index);
+ HashMap<TaskFile, TaskFile> taskFilesCopy = new HashMap<TaskFile, TaskFile>();
+ for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
+ CreateCourseArchive.createUserFile(project, taskFilesCopy, taskResourceDir, taskDir, entry);
+ CreateCourseArchive.resetTaskFiles(taskFilesCopy);
+ }
+ }
+ }
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ }
+
+ @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
+ 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()) {
+ int start = taskWindow.getRealStartOffset(document);
+ String windowDescription = document.getText(new TextRange(start, start + taskWindow.getReplacementLength()));
+ 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);
+ }
+ finally {
+ if (printWriter != null) {
+ printWriter.close();
+ }
+ }
+ }
+ return fileWindows;
+ }
+}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/RunTestsLineMarker.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/RunTestsLineMarker.java
new file mode 100644
index 000000000000..93e0a706f367
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/RunTestsLineMarker.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.plugins.coursecreator;
+
+import com.intellij.codeHighlighting.Pass;
+import com.intellij.codeInsight.daemon.GutterIconNavigationHandler;
+import com.intellij.codeInsight.daemon.LineMarkerInfo;
+import com.intellij.codeInsight.daemon.LineMarkerProvider;
+import com.intellij.execution.actions.ConfigurationContext;
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.DataManager;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.markup.GutterIconRenderer;
+import com.intellij.psi.PsiComment;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiWhiteSpace;
+import com.intellij.psi.util.PsiUtilBase;
+import com.intellij.util.Function;
+import com.jetbrains.python.psi.PyFile;
+import com.jetbrains.python.psi.PyImportStatement;
+import com.jetbrains.python.psi.PyStatement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.event.MouseEvent;
+import java.util.Collection;
+import java.util.List;
+
+public class RunTestsLineMarker implements LineMarkerProvider {
+ @Nullable
+ @Override
+ public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) {
+ return null;
+ }
+
+ @Override
+ public void collectSlowLineMarkers(@NotNull List<PsiElement> elements, @NotNull Collection<LineMarkerInfo> result) {
+ for (PsiElement element : elements) {
+ if (isFirstCodeLine(element)) {
+ PsiFile psiFile = element.getContainingFile();
+ if (psiFile == null || !psiFile.getName().contains(".answer")) {
+ continue;
+ }
+ result.add(new LineMarkerInfo<PsiElement>(
+ element, element.getTextRange(), AllIcons.Actions.Lightning, Pass.UPDATE_OVERRIDEN_MARKERS,
+ new Function<PsiElement, String>() {
+ @Override
+ public String fun(PsiElement e) {
+ return "Run tests from file '" + e.getContainingFile().getName() + "'";
+ }
+ },
+ new GutterIconNavigationHandler<PsiElement>() {
+ @Override
+ public void navigate(MouseEvent e, PsiElement elt) {
+ executeCurrentScript(elt);
+ }
+ },
+ GutterIconRenderer.Alignment.RIGHT));
+ }
+ }
+ }
+
+ private static void executeCurrentScript(PsiElement elt) {
+ Editor editor = PsiUtilBase.findEditor(elt);
+ assert editor != null;
+
+ final ConfigurationContext context =
+ ConfigurationContext.getFromContext(DataManager.getInstance().getDataContext(editor.getComponent()));
+ CCRunTests.run(context);
+ }
+
+ private static boolean isFirstCodeLine(PsiElement element) {
+ return element instanceof PyStatement &&
+ element.getParent() instanceof PyFile &&
+ !isNothing(element) &&
+ nothingBefore(element);
+ }
+
+ private static boolean nothingBefore(PsiElement element) {
+ element = element.getPrevSibling();
+ while (element != null) {
+ if (!isNothing(element)) {
+ return false;
+ }
+ element = element.getPrevSibling();
+ }
+
+ return true;
+ }
+
+ private static boolean isNothing(PsiElement element) {
+ return (element instanceof PsiComment) || (element instanceof PyImportStatement) || (element instanceof PsiWhiteSpace);
+ }
+}
+
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/StudyDocumentListener.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/StudyDocumentListener.java
index d803e0e8fd97..965bd31771a0 100644
--- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/StudyDocumentListener.java
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/StudyDocumentListener.java
@@ -44,9 +44,6 @@ public abstract class StudyDocumentListener extends DocumentAdapter {
@Override
public void documentChanged(DocumentEvent e) {
if (e instanceof DocumentEventImpl) {
- if (!needModify()) {
- return;
- }
DocumentEventImpl event = (DocumentEventImpl)e;
Document document = e.getDocument();
int offset = e.getOffset();
@@ -65,7 +62,5 @@ public abstract class StudyDocumentListener extends DocumentAdapter {
}
protected abstract void updateTaskWindowLength(CharSequence fragment, TaskWindow taskWindow, int change);
-
- protected abstract boolean needModify();
}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java
index ff88cea5fd42..0bc631dbd784 100644
--- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/AddTaskWindow.java
@@ -13,6 +13,7 @@ import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.coursecreator.CCProjectService;
import org.jetbrains.plugins.coursecreator.format.*;
import org.jetbrains.plugins.coursecreator.ui.CreateTaskWindowDialog;
@@ -59,12 +60,12 @@ public class AddTaskWindow extends DumbAwareAction {
}
int index = taskFile.getTaskWindows().size() + 1;
taskFile.addTaskWindow(taskWindow, index);
- taskWindow.drawHighlighter(editor);
+ taskWindow.drawHighlighter(editor, false);
DaemonCodeAnalyzerImpl.getInstance(project).restart(file);
}
@Override
- public void update(AnActionEvent event) {
+ public void update(@NotNull AnActionEvent event) {
final Presentation presentation = event.getPresentation();
final Project project = event.getData(CommonDataKeys.PROJECT);
if (project == null) {
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCRename.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCRename.java
new file mode 100644
index 000000000000..321a86a5ba35
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCRename.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.plugins.coursecreator.actions;
+
+import com.intellij.ide.IdeView;
+import com.intellij.ide.projectView.ProjectView;
+import com.intellij.ide.util.DirectoryChooserUtil;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.coursecreator.CCProjectService;
+import org.jetbrains.plugins.coursecreator.format.Course;
+
+import javax.swing.*;
+
+public abstract class CCRename extends DumbAwareAction {
+ public CCRename(String text, String description, Icon icon) {
+ super(text, description, icon);
+ }
+
+ @Override
+ public void update(@NotNull AnActionEvent event) {
+ final Presentation presentation = event.getPresentation();
+ final Project project = event.getData(CommonDataKeys.PROJECT);
+ if (project == null) {
+ presentation.setVisible(false);
+ presentation.setEnabled(false);
+ return;
+ }
+
+ final IdeView view = event.getData(LangDataKeys.IDE_VIEW);
+ if (view == null) {
+ presentation.setVisible(false);
+ presentation.setEnabled(false);
+ return;
+ }
+
+ final PsiDirectory[] directories = view.getDirectories();
+ if (directories.length == 0) {
+ presentation.setVisible(false);
+ presentation.setEnabled(false);
+ return;
+ }
+ final PsiFile file = CommonDataKeys.PSI_FILE.getData(event.getDataContext());
+ final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view);
+ if (file != null ||directory == null || !directory.getName().contains(getFolderName())) {
+ presentation.setEnabled(false);
+ presentation.setVisible(false);
+ return;
+ }
+ presentation.setVisible(true);
+ presentation.setEnabled(true);
+ }
+
+ public abstract String getFolderName();
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ final IdeView view = e.getData(LangDataKeys.IDE_VIEW);
+ final Project project = e.getData(CommonDataKeys.PROJECT);
+
+ if (view == null || project == null) {
+ return;
+ }
+ final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view);
+ if (directory == null || !directory.getName().contains(getFolderName())) {
+ return;
+ }
+ Course course = CCProjectService.getInstance(project).getCourse();
+ if (course == null) {
+ return;
+ }
+ if (!processRename(project, directory, course)) return;
+ ProjectView.getInstance(project).refresh();
+ }
+
+ public abstract boolean processRename(Project project, PsiDirectory directory, Course course);
+}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCRenameLesson.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCRenameLesson.java
new file mode 100644
index 000000000000..3f580454ed6e
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCRenameLesson.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.plugins.coursecreator.actions;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.psi.PsiDirectory;
+import org.jetbrains.plugins.coursecreator.format.Course;
+import org.jetbrains.plugins.coursecreator.format.Lesson;
+
+public class CCRenameLesson extends CCRename {
+
+ public CCRenameLesson() {
+ super("Rename Lesson", "Rename Lesson", null);
+ }
+
+ @Override
+ public String getFolderName() {
+ return "lesson";
+ }
+
+ @Override
+ public boolean processRename(Project project, PsiDirectory directory, Course course) {
+ Lesson lesson = course.getLesson(directory.getName());
+ if (lesson == null) {
+ return false;
+ }
+ String newName = Messages.showInputDialog(project, "Enter new name", "Rename " + getFolderName(), null);
+ if (newName == null) {
+ return false;
+ }
+ lesson.setName(newName);
+ return true;
+ }
+}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCRenameTask.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCRenameTask.java
new file mode 100644
index 000000000000..342621bf0d47
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCRenameTask.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.plugins.coursecreator.actions;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.psi.PsiDirectory;
+import org.jetbrains.plugins.coursecreator.format.Course;
+import org.jetbrains.plugins.coursecreator.format.Lesson;
+import org.jetbrains.plugins.coursecreator.format.Task;
+
+public class CCRenameTask extends CCRename {
+ public CCRenameTask() {
+ super("Rename Task", "Rename Task", null);
+ }
+
+ @Override
+ public String getFolderName() {
+ return "task";
+ }
+
+ @Override
+ public boolean processRename(Project project, PsiDirectory directory, Course course) {
+ PsiDirectory lessonDir = directory.getParent();
+ if (lessonDir == null || !lessonDir.getName().contains("lesson")) {
+ return false;
+ }
+ Lesson lesson = course.getLesson(lessonDir.getName());
+ if (lesson == null) {
+ return false;
+ }
+ Task task = lesson.getTask(directory.getName());
+ if (task == null) {
+ return false;
+ }
+ String newName = Messages.showInputDialog(project, "Enter new name", "Rename " + getFolderName(), null);
+ if (newName == null) {
+ return false;
+ }
+ task.setName(newName);
+ return true;
+
+ }
+}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCShowPreview.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCShowPreview.java
new file mode 100644
index 000000000000..bccaacaf1957
--- /dev/null
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCShowPreview.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.plugins.coursecreator.actions;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.PsiFile;
+import com.intellij.util.containers.hash.HashMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.coursecreator.CCProjectService;
+import org.jetbrains.plugins.coursecreator.format.*;
+
+import java.util.Map;
+
+public class CCShowPreview extends DumbAwareAction {
+ public CCShowPreview() {
+ super("Show preview","Show preview", null);
+ }
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ Presentation presentation = e.getPresentation();
+ presentation.setEnabled(false);
+ presentation.setVisible(false);
+ final PsiFile file = CommonDataKeys.PSI_FILE.getData(e.getDataContext());
+ if (file != null && file.getName().contains(".answer")) {
+ presentation.setEnabled(true);
+ presentation.setVisible(true);
+ }
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ final Project project = e.getProject();
+ if (project == null) {
+ return;
+ }
+ final PsiFile file = CommonDataKeys.PSI_FILE.getData(e.getDataContext());
+ if (file == null || !file.getName().contains(".answer")) {
+ return;
+ }
+ final PsiDirectory taskDir = file.getContainingDirectory();
+ if (taskDir == null) {
+ return;
+ }
+ PsiDirectory lessonDir = taskDir.getParentDirectory();
+ if (lessonDir == null) {
+ return;
+ }
+ Course course = CCProjectService.getInstance(project).getCourse();
+ if (course == null) {
+ return;
+ }
+ Lesson lesson = course.getLesson(lessonDir.getName());
+ Task task = lesson.getTask(taskDir.getName());
+ TaskFile taskFile = task.getTaskFile(file.getName());
+ final Map<TaskFile, TaskFile> taskFilesCopy = new HashMap<TaskFile, TaskFile>();
+ for (final Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
+ if (entry.getValue() == taskFile) {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ CreateCourseArchive.createUserFile(project, taskFilesCopy, taskDir.getVirtualFile(), taskDir.getVirtualFile(), entry);
+ }
+ });
+ }
+ }
+ String userFileName = FileUtil.getNameWithoutExtension(file.getName()) + ".py";
+ VirtualFile userFile = taskDir.getVirtualFile().findChild(userFileName);
+ if (userFile != null) {
+ FileEditorManager.getInstance(project).openFile(userFile, true);
+ Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
+ if (editor == null) {
+ return;
+ }
+ for (TaskWindow taskWindow : taskFile.getTaskWindows()) {
+ taskWindow.drawHighlighter(editor, true);
+ }
+ CreateCourseArchive.resetTaskFiles(taskFilesCopy);
+ }
+ }
+} \ No newline at end of file
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateCourseArchive.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateCourseArchive.java
index 05428f4e82d1..8db49156c987 100644
--- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateCourseArchive.java
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateCourseArchive.java
@@ -18,6 +18,7 @@ import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.util.io.ZipUtil;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.coursecreator.CCProjectService;
import org.jetbrains.plugins.coursecreator.StudyDocumentListener;
import org.jetbrains.plugins.coursecreator.format.*;
@@ -29,8 +30,8 @@ import java.util.zip.ZipOutputStream;
public class CreateCourseArchive extends DumbAwareAction {
private static final Logger LOG = Logger.getInstance(CreateCourseArchive.class.getName());
- String myZipName;
- String myLocationDir;
+ private String myZipName;
+ private String myLocationDir;
public void setZipName(String zipName) {
myZipName = zipName;
@@ -60,46 +61,124 @@ public class CreateCourseArchive extends DumbAwareAction {
}
final VirtualFile baseDir = project.getBaseDir();
final Map<String, Lesson> lessons = course.getLessonsMap();
- //List<FileEditor> editorList = new ArrayList<FileEditor>();
- Map<VirtualFile, TaskFile> taskFiles = new HashMap<VirtualFile, TaskFile>();
+ //map to store initial task file
+ final Map<TaskFile, TaskFile> taskFiles = new HashMap<TaskFile, TaskFile>();
for (Map.Entry<String, Lesson> lesson : lessons.entrySet()) {
final VirtualFile lessonDir = baseDir.findChild(lesson.getKey());
if (lessonDir == null) continue;
for (Map.Entry<String, Task> task : lesson.getValue().myTasksMap.entrySet()) {
final VirtualFile taskDir = lessonDir.findChild(task.getKey());
if (taskDir == null) continue;
- for (Map.Entry<String, TaskFile> entry : task.getValue().task_files.entrySet()) {
- final VirtualFile file = taskDir.findChild(entry.getKey());
- if (file == null) continue;
- final Document document = FileDocumentManager.getInstance().getDocument(file);
- if (document == null) continue;
- final TaskFile taskFile = entry.getValue();
- document.addDocumentListener(new InsertionListener(taskFile));
- taskFiles.put(file, taskFile);
- taskFile.setTrackChanges(false);
- Collections.sort(taskFile.getTaskWindows());
- for (int i = taskFile.getTaskWindows().size() - 1; i >=0 ; i--) {
- final TaskWindow taskWindow = taskFile.getTaskWindows().get(i);
- final String taskText = taskWindow.getTaskText();
- final int lineStartOffset = document.getLineStartOffset(taskWindow.line);
- final int offset = lineStartOffset + taskWindow.start;
- CommandProcessor.getInstance().executeCommand(project, new Runnable() {
- @Override
- public void run() {
- ApplicationManager.getApplication().runWriteAction(new Runnable() {
- @Override
- public void run() {
- document.replaceString(offset, offset + taskWindow.getReplacementLength(), taskText);
- FileDocumentManager.getInstance().saveDocument(document);
- }
- });
- }
- }, "x", "qwe");
- }
+ for (final Map.Entry<String, TaskFile> entry : task.getValue().task_files.entrySet()) {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ createUserFile(project, taskFiles, taskDir, taskDir, entry);
+ }
+ });
}
}
}
generateJson(project);
+ packCourse(baseDir, lessons);
+ resetTaskFiles(taskFiles);
+ synchronize(project);
+ }
+
+ public static void createUserFile(@NotNull final Project project,
+ @NotNull final Map<TaskFile, TaskFile> taskFilesCopy,
+ @NotNull final VirtualFile userFileDir,
+ @NotNull final VirtualFile answerFileDir,
+ @NotNull final Map.Entry<String, TaskFile> taskFiles) {
+ final String name = taskFiles.getKey();
+ VirtualFile file = userFileDir.findChild(name);
+ if (file != null) {
+ try {
+ file.delete(project);
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ }
+ try {
+ userFileDir.createChildData(project, name);
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+
+ file = userFileDir.findChild(name);
+ assert file != null;
+ String answerFileName = file.getNameWithoutExtension() + ".answer";
+ VirtualFile answerFile = answerFileDir.findChild(answerFileName);
+ if (answerFile == null) {
+ return;
+ }
+ final Document answerDocument = FileDocumentManager.getInstance().getDocument(answerFile);
+ if (answerDocument == null) {
+ return;
+ }
+ final Document document = FileDocumentManager.getInstance().getDocument(file);
+ if (document == null) return;
+ final TaskFile taskFile = taskFiles.getValue();
+ TaskFile taskFileSaved = new TaskFile();
+ taskFile.copy(taskFileSaved);
+ CommandProcessor.getInstance().executeCommand(project, new Runnable() {
+ @Override
+ public void run() {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ document.replaceString(0, document.getTextLength(), answerDocument.getText());
+ }
+ });
+ }
+ }, "x", "qwe");
+ InsertionListener listener = new InsertionListener(taskFile);
+ document.addDocumentListener(listener);
+ taskFilesCopy.put(taskFile, taskFileSaved);
+ Collections.sort(taskFile.getTaskWindows());
+ for (int i = taskFile.getTaskWindows().size() - 1; i >= 0; i--) {
+ final TaskWindow taskWindow = taskFile.getTaskWindows().get(i);
+ replaceTaskWindow(project, document, taskWindow);
+ }
+ document.removeDocumentListener(listener);
+ }
+
+ private static void replaceTaskWindow(@NotNull final Project project,
+ @NotNull final Document document,
+ @NotNull final TaskWindow taskWindow) {
+ final String taskText = taskWindow.getTaskText();
+ final int lineStartOffset = document.getLineStartOffset(taskWindow.line);
+ final int offset = lineStartOffset + taskWindow.start;
+ CommandProcessor.getInstance().executeCommand(project, new Runnable() {
+ @Override
+ public void run() {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ document.replaceString(offset, offset + taskWindow.getReplacementLength(), taskText);
+ FileDocumentManager.getInstance().saveDocument(document);
+ }
+ });
+ }
+ }, "x", "qwe");
+ }
+
+ private static void synchronize(@NotNull final Project project) {
+ VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
+ ProjectView.getInstance(project).refresh();
+ }
+
+ public static void resetTaskFiles(@NotNull final Map<TaskFile, TaskFile> taskFiles) {
+ for (Map.Entry<TaskFile, TaskFile> entry : taskFiles.entrySet()) {
+ TaskFile realTaskFile = entry.getKey();
+ TaskFile savedTaskFile = entry.getValue();
+ realTaskFile.update(savedTaskFile);
+ }
+ }
+
+ private void packCourse(@NotNull final VirtualFile baseDir, @NotNull final Map<String, Lesson> lessons) {
try {
File zipFile = new File(myLocationDir, myZipName + ".zip");
ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));
@@ -108,7 +187,12 @@ public class CreateCourseArchive extends DumbAwareAction {
final VirtualFile lessonDir = baseDir.findChild(entry.getKey());
if (lessonDir == null) continue;
- ZipUtil.addFileOrDirRecursively(zos, null, new File(lessonDir.getPath()), lessonDir.getName(), null, null);
+ ZipUtil.addFileOrDirRecursively(zos, null, new File(lessonDir.getPath()), lessonDir.getName(), new FileFilter() {
+ @Override
+ public boolean accept(File pathname) {
+ return !pathname.getName().contains(".answer");
+ }
+ }, null);
}
ZipUtil.addFileOrDirRecursively(zos, null, new File(baseDir.getPath(), "hints"), "hints", null, null);
ZipUtil.addFileOrDirRecursively(zos, null, new File(baseDir.getPath(), "course.json"), "course.json", null, null);
@@ -119,36 +203,9 @@ public class CreateCourseArchive extends DumbAwareAction {
catch (IOException e1) {
LOG.error(e1);
}
-
- for (Map.Entry<VirtualFile, TaskFile> entry: taskFiles.entrySet()) {
- TaskFile value = entry.getValue();
- final Document document = FileDocumentManager.getInstance().getDocument(entry.getKey());
- if (document == null) {
- continue;
- }
- for (final TaskWindow taskWindow : value.getTaskWindows()){
- final int lineStartOffset = document.getLineStartOffset(taskWindow.line);
- final int offset = lineStartOffset + taskWindow.start;
- CommandProcessor.getInstance().executeCommand(project, new Runnable() {
- @Override
- public void run() {
- ApplicationManager.getApplication().runWriteAction(new Runnable() {
- @Override
- public void run() {
- document.replaceString(offset, offset + taskWindow.length, taskWindow.getPossibleAnswer());
- FileDocumentManager.getInstance().saveDocument(document);
- }
- });
- }
- }, "x", "qwe");
- }
- value.setTrackChanges(true);
- }
- VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
- ProjectView.getInstance(project).refresh();
}
- private void generateJson(Project project) {
+ private static void generateJson(@NotNull final Project project) {
final CCProjectService service = CCProjectService.getInstance(project);
final Course course = service.getCourse();
final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create();
@@ -179,7 +236,7 @@ public class CreateCourseArchive extends DumbAwareAction {
}
}
- private class InsertionListener extends StudyDocumentListener {
+ private static class InsertionListener extends StudyDocumentListener {
public InsertionListener(TaskFile taskFile) {
super(taskFile);
@@ -187,12 +244,7 @@ public class CreateCourseArchive extends DumbAwareAction {
@Override
protected void updateTaskWindowLength(CharSequence fragment, TaskWindow taskWindow, int change) {
- //we don't need to update task window length
- }
-
- @Override
- protected boolean needModify() {
- return true;
+ //we don't need to update task window length
}
}
} \ No newline at end of file
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java
index 0940135b97be..57a37b3f4194 100644
--- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateTask.java
@@ -18,6 +18,7 @@ import com.intellij.openapi.ui.Messages;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.util.PlatformIcons;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.coursecreator.CCProjectService;
import org.jetbrains.plugins.coursecreator.format.Course;
import org.jetbrains.plugins.coursecreator.format.Lesson;
@@ -29,7 +30,7 @@ public class CreateTask extends DumbAwareAction {
}
@Override
- public void actionPerformed(AnActionEvent e) {
+ public void actionPerformed(final AnActionEvent e) {
final IdeView view = e.getData(LangDataKeys.IDE_VIEW);
final Project project = e.getData(CommonDataKeys.PROJECT);
@@ -42,7 +43,7 @@ public class CreateTask extends DumbAwareAction {
final CCProjectService service = CCProjectService.getInstance(project);
final Course course = service.getCourse();
final Lesson lesson = course.getLesson(directory.getName());
- final int size = lesson.getTasklist().size();
+ final int size = lesson.getTaskList().size();
final String taskName = Messages.showInputDialog("Name:", "Task Name", null, "task" + (size + 1), null);
if (taskName == null) return;
@@ -54,17 +55,16 @@ public class CreateTask extends DumbAwareAction {
if (taskDirectory != null) {
final FileTemplate template = FileTemplateManager.getInstance().getInternalTemplate("task.html");
final FileTemplate testsTemplate = FileTemplateManager.getInstance().getInternalTemplate("tests");
- final FileTemplate taskTemplate = FileTemplateManager.getInstance().getInternalTemplate("task.py");
+ final FileTemplate taskTemplate = FileTemplateManager.getInstance().getInternalTemplate("task.answer");
try {
final PsiElement taskFile = FileTemplateUtil.createFromTemplate(template, "task.html", null, taskDirectory);
final PsiElement testsFile = FileTemplateUtil.createFromTemplate(testsTemplate, "tests.py", null, taskDirectory);
- final PsiElement taskPyFile = FileTemplateUtil.createFromTemplate(taskTemplate, "file1" + ".py", null, taskDirectory);
+ final PsiElement taskPyFile = FileTemplateUtil.createFromTemplate(taskTemplate, "file1", null, taskDirectory);
final Task task = new Task(taskName);
- task.addTaskFile(taskPyFile.getContainingFile().getName(), size + 1);
+ task.addTaskFile("file1.py", size + 1);
task.setIndex(size + 1);
lesson.addTask(task, taskDirectory);
-
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
@@ -84,7 +84,7 @@ public class CreateTask extends DumbAwareAction {
}
@Override
- public void update(AnActionEvent event) {
+ public void update(@NotNull AnActionEvent event) {
final Presentation presentation = event.getPresentation();
final Project project = event.getData(CommonDataKeys.PROJECT);
if (project == null) {
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFile.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFile.java
index 5aafcebefd6e..368be275a5a4 100644
--- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFile.java
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CreateTaskFile.java
@@ -55,10 +55,10 @@ public class CreateTaskFile extends DumbAwareAction {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
- final FileTemplate taskTemplate = FileTemplateManager.getInstance().getInternalTemplate("task.py");
+ final FileTemplate taskTemplate = FileTemplateManager.getInstance().getInternalTemplate("task.answer");
try {
- final PsiElement taskPyFile = FileTemplateUtil.createFromTemplate(taskTemplate, taskFileName + ".py", null, taskDir);
- task.addTaskFile(taskPyFile.getContainingFile().getName(), index);
+ final PsiElement taskPyFile = FileTemplateUtil.createFromTemplate(taskTemplate, taskFileName, null, taskDir);
+ task.addTaskFile(taskFileName + ".py", index);
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Course.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Course.java
index eb62d59cd9b1..e124a6eb305d 100644
--- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Course.java
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Course.java
@@ -4,10 +4,7 @@ import com.google.gson.annotations.Expose;
import com.intellij.psi.PsiDirectory;
import org.jetbrains.annotations.NotNull;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
public class Course {
@Expose public List<Lesson> lessons = new ArrayList<Lesson>();
@@ -52,4 +49,13 @@ public class Course {
public String getDescription() {
return description;
}
+
+ public void init() {
+ lessons.clear();
+ for (Lesson lesson: myLessonsMap.values()) {
+ lessons.add(lesson);
+ lesson.init();
+ }
+ Collections.sort(lessons);
+ }
}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Lesson.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Lesson.java
index 38720140caf1..bd91e8ec30af 100644
--- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Lesson.java
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Lesson.java
@@ -4,12 +4,9 @@ import com.google.gson.annotations.Expose;
import com.intellij.psi.PsiDirectory;
import org.jetbrains.annotations.NotNull;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
-public class Lesson {
+public class Lesson implements Comparable{
@Expose public String name;
@Expose public List<Task> task_list = new ArrayList<Task>();
@@ -27,11 +24,15 @@ public class Lesson {
task_list.add(task);
}
+ public void setName(String name) {
+ this.name = name;
+ }
+
public Task getTask(@NotNull final String name) {
return myTasksMap.get(name);
}
- public List<Task> getTasklist() {
+ public List<Task> getTaskList() {
return task_list;
}
@@ -42,4 +43,22 @@ public class Lesson {
public int getIndex() {
return myIndex;
}
+
+ public Map<String, Task> getTasksMap() {
+ return myTasksMap;
+ }
+
+ public void init() {
+ task_list.clear();
+ for (Task task : myTasksMap.values()) {
+ task_list.add(task);
+ }
+ Collections.sort(task_list);
+ }
+
+ @Override
+ public int compareTo(@NotNull Object o) {
+ Lesson lesson = (Lesson) o;
+ return myIndex - lesson.getIndex();
+ }
}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Task.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Task.java
index e6c085b5d6a1..886add86ceb4 100644
--- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Task.java
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Task.java
@@ -2,11 +2,12 @@ package org.jetbrains.plugins.coursecreator.format;
import com.google.gson.annotations.Expose;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.coursecreator.CCProjectService;
import java.util.HashMap;
import java.util.Map;
-public class Task {
+public class Task implements Comparable{
@Expose public String name;
@Expose public Map<String, TaskFile> task_files = new HashMap<String, TaskFile>();
public int myIndex;
@@ -28,7 +29,8 @@ public class Task {
}
public TaskFile getTaskFile(@NotNull final String name) {
- return task_files.get(name);
+ String fileName = CCProjectService.getRealTaskFileName(name);
+ return fileName != null ? task_files.get(fileName) : null;
}
public void setIndex(int index) {
@@ -38,4 +40,18 @@ public class Task {
public Map<String, TaskFile> getTaskFiles() {
return task_files;
}
+
+ public boolean isTaskFile(String name) {
+ return task_files.get(name) != null;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public int compareTo(@NotNull Object o) {
+ Task task = (Task) o;
+ return myIndex - task.getIndex();
+ }
}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java
index 85f0d91983f2..b88e375bc265 100644
--- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java
@@ -13,15 +13,6 @@ import java.util.List;
public class TaskFile {
@Expose public List<TaskWindow> task_windows = new ArrayList<TaskWindow>();
public int myIndex;
- public boolean myTrackChanges = true;
-
- public boolean isTrackChanges() {
- return myTrackChanges;
- }
-
- public void setTrackChanges(boolean trackChanges) {
- myTrackChanges = trackChanges;
- }
public TaskFile() {}
@@ -108,4 +99,22 @@ public class TaskFile {
}
}
}
+
+ public void copy(@NotNull final TaskFile target) {
+ target.setIndex(myIndex);
+ for (TaskWindow taskWindow : task_windows) {
+ TaskWindow savedWindow = new TaskWindow(taskWindow.getLine(), taskWindow.getStart(),
+ taskWindow.getLength(), "");
+ target.getTaskWindows().add(savedWindow);
+ savedWindow.setIndex(taskWindow.getIndex());
+ }
+ }
+
+ public void update(@NotNull final TaskFile source) {
+ for (TaskWindow taskWindow : source.getTaskWindows()) {
+ TaskWindow taskWindowUpdated = task_windows.get(taskWindow.getIndex() - 1);
+ taskWindowUpdated.setLine(taskWindow.getLine());
+ taskWindowUpdated.setStart(taskWindow.getStart());
+ }
+ }
}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java
index cb6418ec75d7..6b1be7ef3e7d 100644
--- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java
@@ -68,16 +68,17 @@ public class TaskWindow implements Comparable{
}
}
- public void drawHighlighter(@NotNull final Editor editor) {
+ public void drawHighlighter(@NotNull final Editor editor, boolean useLength) {
int startOffset = editor.getDocument().getLineStartOffset(line) + start;
- int endOffset = startOffset + myReplacementLength;
+ int highlighterLength = useLength ? length : myReplacementLength;
+ int endOffset = startOffset + highlighterLength;
TextAttributes defaultTestAttributes =
EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.LIVE_TEMPLATE_ATTRIBUTES);
RangeHighlighter highlighter =
editor.getMarkupModel().addRangeHighlighter(startOffset, endOffset, HighlighterLayer.LAST + 1, defaultTestAttributes,
HighlighterTargetArea.EXACT_RANGE);
highlighter.setGreedyToLeft(true);
- highlighter.setGreedyToRight(true);
+ highlighter.setGreedyToRight(false);
}
public int getIndex() {
@@ -123,10 +124,6 @@ public class TaskWindow implements Comparable{
return lineDiff;
}
- public String getPossibleAnswer() {
- return possible_answer;
- }
-
public int getLength() {
return length;
}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/projectView/CCDirectoryNode.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/projectView/CCDirectoryNode.java
index 1a7304123f0a..0969c54563c2 100644
--- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/projectView/CCDirectoryNode.java
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/projectView/CCDirectoryNode.java
@@ -26,6 +26,8 @@ public class CCDirectoryNode extends PsiDirectoryNode {
@Override
protected void updateImpl(PresentationData data) {
+ //TODO:change presentable name for files with suffix _answer
+
String valueName = myValue.getName();
final Course course = CCProjectService.getInstance(myProject).getCourse();
if (course == null) return;
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/projectView/CCTreeStructureProvider.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/projectView/CCTreeStructureProvider.java
index 69b78ec9a92b..b4fb50a96dc4 100644
--- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/projectView/CCTreeStructureProvider.java
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/projectView/CCTreeStructureProvider.java
@@ -2,9 +2,11 @@ package org.jetbrains.plugins.coursecreator.projectView;
import com.intellij.ide.projectView.TreeStructureProvider;
import com.intellij.ide.projectView.ViewSettings;
+import com.intellij.ide.projectView.impl.nodes.PsiFileNode;
import com.intellij.ide.util.treeView.AbstractTreeNode;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -27,11 +29,21 @@ public class CCTreeStructureProvider implements TreeStructureProvider, DumbAware
Project project = node.getProject();
if (project != null) {
if (node.getValue() instanceof PsiDirectory) {
- PsiDirectory directory = (PsiDirectory) node.getValue();
+ PsiDirectory directory = (PsiDirectory)node.getValue();
nodes.add(new CCDirectoryNode(project, directory, settings));
- } else {
- nodes.add(node);
+ continue;
}
+ if (node instanceof PsiFileNode) {
+ PsiFileNode fileNode = (PsiFileNode)node;
+ VirtualFile virtualFile = fileNode.getVirtualFile();
+ if (virtualFile == null) {
+ continue;
+ }
+ if (CCProjectService.getInstance(project).isTaskFile(virtualFile)) {
+ continue;
+ }
+ }
+ nodes.add(node);
}
}
return nodes;
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.form b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.form
index 920dcb9494a7..096a85f8da59 100644
--- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.form
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateCourseArchivePanel.form
@@ -27,7 +27,7 @@
<component id="160bb" class="javax.swing.JTextField" binding="myNameField" default-binding="true">
<constraints>
<grid row="0" column="1" row-span="2" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
- <preferred-size width="150" height="-1"/>
+ <preferred-size width="300" height="-1"/>
</grid>
</constraints>
<properties/>
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java
index c7e8f715672c..53a4a77b97f7 100644
--- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java
@@ -150,4 +150,10 @@ public class CreateTaskWindowDialog extends DialogWrapper {
public void validateInput() {
super.initValidation();
}
+
+ @Nullable
+ @Override
+ public JComponent getPreferredFocusedComponent() {
+ return myPanel.getPreferredFocusedComponent();
+ }
}
diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java
index 21a7eb063c63..a50840deaf2f 100644
--- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java
+++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java
@@ -37,6 +37,7 @@ public class CreateTaskWindowPanel extends JPanel {
}
});
+ myTaskWindowText.grabFocus();
myHintName.getDocument().addDocumentListener(new DocumentAdapter() {
@Override
protected void textChanged(DocumentEvent e) {
@@ -93,4 +94,8 @@ public class CreateTaskWindowPanel extends JPanel {
public void setGeneratedHintName(String generatedHintName) {
myGeneratedHintName = generatedHintName;
}
+
+ public JComponent getPreferredFocusedComponent() {
+ return myTaskWindowText;
+ }
}
diff --git a/python/edu/learn-python/resources/META-INF/plugin.xml b/python/edu/learn-python/resources/META-INF/plugin.xml
index 8e8bddcce5e5..b6ff0ce5d0d7 100644
--- a/python/edu/learn-python/resources/META-INF/plugin.xml
+++ b/python/edu/learn-python/resources/META-INF/plugin.xml
@@ -40,7 +40,7 @@
<actions>
<action id="CheckAction" class="com.jetbrains.python.edu.actions.StudyCheckAction" text="check"
- description="Runs tests for current tasks" icon="/icons/icon.jpg">
+ description="Runs tests for current tasks">
</action>
<action id="PrevWindowAction" class="com.jetbrains.python.edu.actions.StudyPrevWindowAction" text="PrevWindowAction" description="prev">
</action>
@@ -60,10 +60,12 @@
<add-to-group group-id="MainToolBar" anchor="last"/>
</action>
- <action id="WelcomeScreen.LearnPython" class="com.jetbrains.python.edu.actions.StudyNewProject" icon="StudyIcons.EducationalProjectType">
+ <action id="WelcomeScreen.PythonIntro" class="com.jetbrains.python.edu.actions.StudyIntroductionCourseAction" icon="StudyIcons.EducationalProjectType">
<add-to-group group-id="WelcomeScreen.QuickStart" anchor="first"/>
</action>
+ <action id="ReloadCourseAction" class="com.jetbrains.python.edu.actions.StudyReloadCourseAction"/>
+
</actions>
<extensions defaultExtensionNs="com.intellij">
@@ -74,6 +76,7 @@
<highlightErrorFilter implementation="com.jetbrains.python.edu.StudyHighlightErrorFilter"/>
<applicationService serviceInterface="com.intellij.openapi.fileEditor.impl.EditorEmptyTextPainter"
serviceImplementation="com.jetbrains.python.edu.StudyInstructionPainter" overrides="true"/>
+ <errorHandler implementation="com.intellij.diagnostic.ITNReporter"/>
</extensions>
<extensions defaultExtensionNs="Pythonid">
<visitorFilter language="Python" implementationClass="com.jetbrains.python.edu.highlighting.StudyVisitorFilter"/>
diff --git a/python/edu/learn-python/resources/courses/introduction_course.zip b/python/edu/learn-python/resources/courses/introduction_course.zip
index c39695e0c380..4fd2dab43830 100644
--- a/python/edu/learn-python/resources/courses/introduction_course.zip
+++ b/python/edu/learn-python/resources/courses/introduction_course.zip
Binary files differ
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDirectoryProjectGenerator.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDirectoryProjectGenerator.java
index 59bd8bc2ea7c..b558667734bc 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDirectoryProjectGenerator.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDirectoryProjectGenerator.java
@@ -53,7 +53,7 @@ public class StudyDirectoryProjectGenerator extends PythonProjectGenerator imple
@NotNull
@Override
public String getName() {
- return "Learn Python";
+ return "Educational";
}
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
index b0cd5ba89fed..1800deb0a0d9 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyTestRunner.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyTestRunner.java
@@ -61,7 +61,9 @@ public class StudyTestRunner {
try {
while ((line = testOutputReader.readLine()) != null) {
if (line.contains(TEST_FAILED)) {
- return line.substring(TEST_FAILED.length(), line.length());
+ String res = line.substring(TEST_FAILED.length(), line.length());
+ StudyUtils.closeSilently(testOutputReader);
+ return res;
}
}
}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyIntroductionCourseAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyIntroductionCourseAction.java
new file mode 100644
index 000000000000..ed1fff22878f
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyIntroductionCourseAction.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.edu.actions;
+
+import com.intellij.ide.impl.ProjectUtil;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.jetbrains.python.configuration.PyConfigurableInterpreterList;
+import com.jetbrains.python.edu.StudyDirectoryProjectGenerator;
+import com.jetbrains.python.edu.StudyUtils;
+import com.jetbrains.python.edu.course.CourseInfo;
+import com.jetbrains.python.newProject.actions.GenerateProjectCallback;
+import com.jetbrains.python.newProject.actions.ProjectSpecificSettingsStep;
+import icons.StudyIcons;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+public class StudyIntroductionCourseAction extends AnAction {
+
+ public StudyIntroductionCourseAction() {
+ super("Introduction to Python", "Introduction to Python", StudyIcons.EducationalProjectType);
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ final File projectDir = new File(ProjectUtil.getBaseDir(), "PythonIntroduction");
+ if (projectDir.exists()) {
+ ProjectUtil.openProject(projectDir.getPath(), null, false);
+ }
+ else {
+ final GenerateProjectCallback callback = new GenerateProjectCallback(null);
+ final StudyDirectoryProjectGenerator generator = new StudyDirectoryProjectGenerator();
+ final Map<CourseInfo, File> courses = generator.getCourses();
+ CourseInfo introCourse = null;
+ for (CourseInfo info : courses.keySet()) {
+ if ("Introduction to Python".equals(info.getName())) {
+ introCourse = info;
+ }
+ }
+ if (introCourse == null) {
+ introCourse = StudyUtils.getFirst(courses.keySet());
+ }
+ generator.setSelectedCourse(introCourse);
+ final ProjectSpecificSettingsStep step = new ProjectSpecificSettingsStep(generator, callback, true);
+
+ step.createPanel(); // initialize panel to set location
+ step.setLocation(projectDir.toString());
+
+ final Project project = ProjectManager.getInstance().getDefaultProject();
+ final List<Sdk> sdks = PyConfigurableInterpreterList.getInstance(project).getAllPythonSdks();
+ Sdk sdk = sdks.isEmpty() ? null : sdks.iterator().next();
+ step.setSdk(sdk);
+ callback.consume(step);
+ }
+ }
+}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNewProject.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNewProject.java
deleted file mode 100644
index 0b75c4bc01c4..000000000000
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNewProject.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2000-2013 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jetbrains.python.edu.actions;
-
-import com.jetbrains.python.edu.StudyDirectoryProjectGenerator;
-import com.jetbrains.python.newProject.actions.GenerateProjectCallback;
-import com.jetbrains.python.newProject.actions.ProjectSpecificAction;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-public class StudyNewProject extends ProjectSpecificAction {
-
- public StudyNewProject(@NotNull final String name, @Nullable final Runnable runnable) {
- super(new GenerateProjectCallback(runnable), new StudyDirectoryProjectGenerator(), name, true);
- }
-
- public StudyNewProject() {
- this("Learn Python", null);
- }
-
-}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskFileAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskFileAction.java
index a9448ddea0e3..a6c16d2553bf 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskFileAction.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyRefreshTaskFileAction.java
@@ -22,108 +22,139 @@ import com.jetbrains.python.edu.StudyTaskManager;
import com.jetbrains.python.edu.StudyUtils;
import com.jetbrains.python.edu.course.*;
import com.jetbrains.python.edu.editor.StudyEditor;
+import org.jetbrains.annotations.NotNull;
import java.io.*;
public class StudyRefreshTaskFileAction extends DumbAwareAction {
private static final Logger LOG = Logger.getInstance(StudyRefreshTaskFileAction.class.getName());
- public void refresh(final Project project) {
- ApplicationManager.getApplication().invokeLater(new Runnable() {
+ public static void refresh(final Project project) {
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
@Override
public void run() {
- ApplicationManager.getApplication().runWriteAction(new Runnable() {
- @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
- @Override
- public void run() {
- final Editor editor = StudyEditor.getSelectedEditor(project);
- assert editor != null;
- final Document document = editor.getDocument();
- StudyDocumentListener listener = StudyEditor.getListener(document);
- if (listener != null) {
- document.removeDocumentListener(listener);
- }
- final int lineCount = document.getLineCount();
- if (lineCount != 0) {
- CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() {
- @Override
- public void run() {
- document.deleteString(0, document.getLineEndOffset(lineCount - 1));
- }
- });
- }
- StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
- Course course = taskManager.getCourse();
- assert course != null;
- File resourceFile = new File(course.getResourcePath());
- File resourceRoot = resourceFile.getParentFile();
- FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
- VirtualFile openedFile = fileDocumentManager.getFile(document);
- assert openedFile != null;
- final TaskFile selectedTaskFile = taskManager.getTaskFile(openedFile);
- assert selectedTaskFile != null;
- Task currentTask = selectedTaskFile.getTask();
- String lessonDir = Lesson.LESSON_DIR + String.valueOf(currentTask.getLesson().getIndex() + 1);
- String taskDir = Task.TASK_DIR + String.valueOf(currentTask.getIndex() + 1);
- File pattern = new File(new File(new File(resourceRoot, lessonDir), taskDir), openedFile.getName());
- BufferedReader reader = null;
- try {
- reader = new BufferedReader(new InputStreamReader(new FileInputStream(pattern)));
- String line;
- StringBuilder patternText = new StringBuilder();
- while ((line = reader.readLine()) != null) {
- patternText.append(line);
- patternText.append("\n");
- }
- int patternLength = patternText.length();
- if (patternText.charAt(patternLength - 1) == '\n') {
- patternText.delete(patternLength - 1, patternLength);
- }
- document.setText(patternText);
- StudyStatus oldStatus = currentTask.getStatus();
- LessonInfo lessonInfo = currentTask.getLesson().getLessonInfo();
- lessonInfo.update(oldStatus, -1);
- lessonInfo.update(StudyStatus.Unchecked, +1);
- StudyUtils.updateStudyToolWindow(project);
- for (TaskWindow taskWindow : selectedTaskFile.getTaskWindows()) {
- taskWindow.reset();
- }
- ProjectView.getInstance(project).refresh();
- if (listener != null) {
- document.addDocumentListener(listener);
- }
- selectedTaskFile.drawAllWindows(editor);
- ApplicationManager.getApplication().invokeLater(new Runnable() {
- @Override
- public void run() {
- IdeFocusManager.getInstance(project).requestFocus(editor.getContentComponent(), true);
- }
- });
- selectedTaskFile.navigateToFirstTaskWindow(editor);
- BalloonBuilder balloonBuilder =
- JBPopupFactory.getInstance().createHtmlTextBalloonBuilder("You can now start again", MessageType.INFO, null);
- final Balloon balloon = balloonBuilder.createBalloon();
- StudyEditor selectedStudyEditor = StudyEditor.getSelectedStudyEditor(project);
- assert selectedStudyEditor != null;
- balloon.showInCenterOf(selectedStudyEditor.getRefreshButton());
- Disposer.register(project, balloon);
- }
- catch (FileNotFoundException e1) {
- LOG.error(e1);
- }
- catch (IOException e1) {
- LOG.error(e1);
- }
- finally {
- StudyUtils.closeSilently(reader);
- }
- }
- });
+ final Editor editor = StudyEditor.getSelectedEditor(project);
+ assert editor != null;
+ final Document document = editor.getDocument();
+ refreshFile(editor, document, project);
}
});
+ }
+ });
}
- public void actionPerformed(AnActionEvent e) {
+ public static void refreshFile(@NotNull final Editor editor, @NotNull final Document document, @NotNull final Project project) {
+ StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
+ Course course = taskManager.getCourse();
+ assert course != null;
+ FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
+ VirtualFile openedFile = fileDocumentManager.getFile(document);
+ assert openedFile != null;
+ final TaskFile selectedTaskFile = taskManager.getTaskFile(openedFile);
+ assert selectedTaskFile != null;
+ String openedFileName = openedFile.getName();
+ Task currentTask = selectedTaskFile.getTask();
+ resetTaskFile(document, project, course, selectedTaskFile, openedFileName, currentTask);
+ selectedTaskFile.drawAllWindows(editor);
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ IdeFocusManager.getInstance(project).requestFocus(editor.getContentComponent(), true);
+ }
+ });
+ selectedTaskFile.navigateToFirstTaskWindow(editor);
+ showBaloon(project);
+ }
+
+ public static void resetTaskFile(Document document, Project project, Course course, TaskFile taskFile, String name, Task task) {
+ resetDocument(document, course, name, task);
+ updateLessonInfo(task);
+ StudyUtils.updateStudyToolWindow(project);
+ resetTaskWindows(taskFile);
+ ProjectView.getInstance(project).refresh();
+ }
+
+ private static void showBaloon(Project project) {
+ BalloonBuilder balloonBuilder =
+ JBPopupFactory.getInstance().createHtmlTextBalloonBuilder("You can now start again", MessageType.INFO, null);
+ final Balloon balloon = balloonBuilder.createBalloon();
+ StudyEditor selectedStudyEditor = StudyEditor.getSelectedStudyEditor(project);
+ assert selectedStudyEditor != null;
+ balloon.showInCenterOf(selectedStudyEditor.getRefreshButton());
+ Disposer.register(project, balloon);
+ }
+
+ private static void resetTaskWindows(TaskFile selectedTaskFile) {
+ for (TaskWindow taskWindow : selectedTaskFile.getTaskWindows()) {
+ taskWindow.reset();
+ }
+ }
+
+ private static void updateLessonInfo(Task currentTask) {
+ StudyStatus oldStatus = currentTask.getStatus();
+ LessonInfo lessonInfo = currentTask.getLesson().getLessonInfo();
+ lessonInfo.update(oldStatus, -1);
+ lessonInfo.update(StudyStatus.Unchecked, +1);
+ }
+
+ @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
+ private static void resetDocument(Document document, Course course, String fileName, Task task) {
+ BufferedReader reader = null;
+ StudyDocumentListener listener = StudyEditor.getListener(document);
+ if (listener != null) {
+ document.removeDocumentListener(listener);
+ }
+ clearDocument(document);
+ try {
+ String lessonDir = Lesson.LESSON_DIR + String.valueOf(task.getLesson().getIndex() + 1);
+ String taskDir = Task.TASK_DIR + String.valueOf(task.getIndex() + 1);
+ File resourceFile = new File(course.getResourcePath());
+ File resourceRoot = resourceFile.getParentFile();
+ File pattern = new File(new File(new File(resourceRoot, lessonDir), taskDir), fileName);
+ reader = new BufferedReader(new InputStreamReader(new FileInputStream(pattern)));
+ String line;
+ StringBuilder patternText = new StringBuilder();
+ while ((line = reader.readLine()) != null) {
+ patternText.append(line);
+ patternText.append("\n");
+ }
+ int patternLength = patternText.length();
+ if (patternText.charAt(patternLength - 1) == '\n') {
+ patternText.delete(patternLength - 1, patternLength);
+ }
+ document.setText(patternText);
+ }
+ catch (FileNotFoundException e) {
+ LOG.error(e);
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ finally {
+ StudyUtils.closeSilently(reader);
+ }
+ if (listener != null) {
+ document.addDocumentListener(listener);
+ }
+ }
+
+ private static void clearDocument(final Document document) {
+ final int lineCount = document.getLineCount();
+ if (lineCount != 0) {
+ CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() {
+ @Override
+ public void run() {
+ document.deleteString(0, document.getLineEndOffset(lineCount - 1));
+ }
+ });
+ }
+ }
+
+ public void actionPerformed(@NotNull AnActionEvent e) {
refresh(e.getProject());
}
}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyReloadCourseAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyReloadCourseAction.java
new file mode 100644
index 000000000000..beefaaa94f7e
--- /dev/null
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyReloadCourseAction.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.edu.actions;
+
+import com.intellij.ide.projectView.ProjectView;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.ui.tree.TreeUtil;
+import com.jetbrains.python.edu.StudyTaskManager;
+import com.jetbrains.python.edu.StudyUtils;
+import com.jetbrains.python.edu.course.Course;
+import com.jetbrains.python.edu.course.Lesson;
+import com.jetbrains.python.edu.course.Task;
+import com.jetbrains.python.edu.course.TaskFile;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import javax.swing.tree.TreePath;
+import java.util.List;
+import java.util.Map;
+
+public class StudyReloadCourseAction extends DumbAwareAction {
+
+ public StudyReloadCourseAction() {
+ super("Reload Course", "Reload Course", null);
+ }
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ Presentation presentation = e.getPresentation();
+ Project project = e.getProject();
+ if (project != null) {
+ Course course = StudyTaskManager.getInstance(project).getCourse();
+ if (course != null) {
+ presentation.setVisible(true);
+ presentation.setEnabled(true);
+ }
+ }
+ presentation.setVisible(false);
+ presentation.setEnabled(false);
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ Project project = e.getProject();
+ if (project == null) {
+ return;
+ }
+ reloadCourse(project);
+ }
+
+ public static void reloadCourse(@NotNull final Project project) {
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ Course course = StudyTaskManager.getInstance(project).getCourse();
+ if (course == null) {
+ return;
+ }
+ for (VirtualFile file : FileEditorManager.getInstance(project).getOpenFiles()) {
+ FileEditorManager.getInstance(project).closeFile(file);
+ }
+ JTree tree = ProjectView.getInstance(project).getCurrentProjectViewPane().getTree();
+ TreePath path = TreeUtil.getFirstNodePath(tree);
+ tree.collapsePath(path);
+ List<Lesson> lessons = course.getLessons();
+ for (Lesson lesson : lessons) {
+ List<Task> tasks = lesson.getTaskList();
+ VirtualFile lessonDir = project.getBaseDir().findChild(Lesson.LESSON_DIR + (lesson.getIndex() + 1));
+ if (lessonDir == null) {
+ continue;
+ }
+ for (Task task : tasks) {
+ VirtualFile taskDir = lessonDir.findChild(Task.TASK_DIR + (task.getIndex() + 1));
+ if (taskDir == null) {
+ continue;
+ }
+ Map<String, TaskFile> taskFiles = task.getTaskFiles();
+ for (Map.Entry<String, TaskFile> entry : taskFiles.entrySet()) {
+ String name = entry.getKey();
+ TaskFile taskFile = entry.getValue();
+ VirtualFile file = taskDir.findChild(name);
+ if (file == null) {
+ continue;
+ }
+ Document document = FileDocumentManager.getInstance().getDocument(file);
+ if (document == null) {
+ continue;
+ }
+ StudyRefreshTaskFileAction.resetTaskFile(document, project, course, taskFile, name, task);
+ }
+ }
+ }
+ Lesson firstLesson = StudyUtils.getFirst(lessons);
+ if (firstLesson == null) {
+ return;
+ }
+ Task firstTask = StudyUtils.getFirst(firstLesson.getTaskList());
+ VirtualFile lessonDir = project.getBaseDir().findChild(Lesson.LESSON_DIR + (firstLesson.getIndex() + 1));
+ if (lessonDir != null) {
+ VirtualFile taskDir = lessonDir.findChild(Task.TASK_DIR + (firstTask.getIndex() + 1));
+ if (taskDir != null) {
+ ProjectView.getInstance(project).select(taskDir, taskDir, true);
+ }
+ }
+ }
+ });
+ }
+ });
+ }
+}
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 46c0981cb964..b98bbd098776 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyTaskNavigationAction.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyTaskNavigationAction.java
@@ -1,5 +1,6 @@
package com.jetbrains.python.edu.actions;
+import com.intellij.ide.projectView.ProjectView;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.DumbAwareAction;
@@ -12,6 +13,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowId;
import com.intellij.openapi.wm.ToolWindowManager;
+import com.intellij.util.ui.tree.TreeUtil;
import com.jetbrains.python.edu.StudyState;
import com.jetbrains.python.edu.course.Lesson;
import com.jetbrains.python.edu.course.Task;
@@ -20,6 +22,7 @@ import com.jetbrains.python.edu.editor.StudyEditor;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
+import javax.swing.tree.TreePath;
import java.util.Map;
@@ -74,7 +77,11 @@ abstract public class StudyTaskNavigationAction extends DumbAwareAction {
}
}
}
+ JTree tree = ProjectView.getInstance(project).getCurrentProjectViewPane().getTree();
+ TreePath path = TreeUtil.getFirstNodePath(tree);
+ tree.collapsePath(path);
if (shouldBeActive != null) {
+ ProjectView.getInstance(project).select(shouldBeActive, shouldBeActive, false);
FileEditorManager.getInstance(project).openFile(shouldBeActive, true);
}
ToolWindow runToolWindow = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.RUN);
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/projectView/StudyDirectoryNode.java b/python/edu/learn-python/src/com/jetbrains/python/edu/projectView/StudyDirectoryNode.java
index 2f80dba12695..d8faacd23946 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/projectView/StudyDirectoryNode.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/projectView/StudyDirectoryNode.java
@@ -1,8 +1,10 @@
package com.jetbrains.python.edu.projectView;
import com.intellij.ide.projectView.PresentationData;
+import com.intellij.ide.projectView.ProjectView;
import com.intellij.ide.projectView.ViewSettings;
import com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode;
+import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
@@ -17,6 +19,7 @@ import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
+import java.util.Set;
public class StudyDirectoryNode extends PsiDirectoryNode {
private final PsiDirectory myValue;
@@ -110,4 +113,56 @@ public class StudyDirectoryNode extends PsiDirectoryNode {
data.addText(additionalName, new SimpleTextAttributes(Font.PLAIN, color));
data.setIcon(icon);
}
+
+ @Override
+ public boolean canNavigate() {
+ return true;
+ }
+
+ @Override
+ public boolean canNavigateToSource() {
+ return true;
+ }
+
+ @Override
+ public void navigate(boolean requestFocus) {
+ if (myValue.getName().contains(Task.TASK_DIR)) {
+ TaskFile taskFile = null;
+ VirtualFile virtualFile = null;
+ for (PsiElement child : myValue.getChildren()) {
+ VirtualFile childFile = child.getContainingFile().getVirtualFile();
+ taskFile = StudyTaskManager.getInstance(myProject).getTaskFile(childFile);
+ if (taskFile != null) {
+ virtualFile = childFile;
+ break;
+ }
+ }
+ if (taskFile != null) {
+ VirtualFile taskDir = virtualFile.getParent();
+ Task task = taskFile.getTask();
+ for (VirtualFile openFile : FileEditorManager.getInstance(myProject).getOpenFiles()) {
+ FileEditorManager.getInstance(myProject).closeFile(openFile);
+ }
+ VirtualFile child = null;
+ Set<String> fileNames = task.getTaskFiles().keySet();
+ for (String name : fileNames) {
+ child = taskDir.findChild(name);
+ if (child != null) {
+ FileEditorManager.getInstance(myProject).openFile(child, true);
+ }
+ }
+ if (child != null) {
+ ProjectView.getInstance(myProject).select(child, child, false);
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean expandOnDoubleClick() {
+ if (myValue.getName().contains(Task.TASK_DIR)) {
+ return false;
+ }
+ return super.expandOnDoubleClick();
+ }
}
diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyToolWindowFactory.java b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyToolWindowFactory.java
index a553978c416a..0f8c5a53511a 100644
--- a/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyToolWindowFactory.java
+++ b/python/edu/learn-python/src/com/jetbrains/python/edu/ui/StudyToolWindowFactory.java
@@ -9,6 +9,7 @@ import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentFactory;
import com.intellij.util.ui.UIUtil;
import com.jetbrains.python.edu.StudyTaskManager;
+import com.jetbrains.python.edu.actions.StudyReloadCourseAction;
import com.jetbrains.python.edu.course.Course;
import com.jetbrains.python.edu.course.Lesson;
import com.jetbrains.python.edu.course.LessonInfo;
@@ -17,6 +18,8 @@ import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
import java.util.List;
public class StudyToolWindowFactory implements ToolWindowFactory, DumbAware {
@@ -41,7 +44,15 @@ public class StudyToolWindowFactory implements ToolWindowFactory, DumbAware {
contentPanel.add(new JLabel(authorLabel));
contentPanel.add(Box.createRigidArea(new Dimension(0, 10)));
contentPanel.add(new JLabel(description));
+ contentPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+ JButton reloadCourseButton = new JButton("reload course");
+ reloadCourseButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ StudyReloadCourseAction.reloadCourse(project);
+ }
+ });
+ contentPanel.add(reloadCourseButton);
int taskNum = 0;
int taskSolved = 0;
int lessonsCompleted = 0;
diff --git a/python/edu/resources/idea/PyCharmEduApplicationInfo.xml b/python/edu/resources/idea/PyCharmEduApplicationInfo.xml
index eed232dcd3c7..939c84129455 100644
--- a/python/edu/resources/idea/PyCharmEduApplicationInfo.xml
+++ b/python/edu/resources/idea/PyCharmEduApplicationInfo.xml
@@ -19,5 +19,5 @@
<feedback eap-url="http://www.jetbrains.com/feedback/feedback.jsp?product=PyCharm&amp;build=$BUILD&amp;timezone=$TIMEZONE&amp;eval=$EVAL"
release-url="http://www.jetbrains.com/feedback/feedback.jsp?product=PyCharm&amp;build=$BUILD&amp;timezone=$TIMEZONE&amp;eval=$EVAL"/>
- <help file="pycharm-eduhelp.jar" root="pycharm"/>
+ <help file="pycharm-eduhelp.jar" root="pycharm-edu"/>
</component>