diff options
Diffstat (limited to 'python/edu/course-creator/src/org/jetbrains/plugins/coursecreator')
27 files changed, 1290 insertions, 133 deletions
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; + } } |