From 33d7918a875ce173eec47d42559f1c32769e89a1 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Mon, 18 Jul 2016 18:55:23 +0300 Subject: [PATCH] add cross-course links in format #lessonName#taskName# --- .../jetbrains/edu/learning/StudyUtils.java | 1 + .../actions/StudyTaskNavigationAction.java | 30 +----- .../learning/navigation/StudyNavigator.java | 93 ++++++++++++++++++- .../edu/learning/ui/StudyBrowserWindow.java | 32 +++++-- .../learning/ui/StudyJavaFxToolWindow.java | 2 +- 5 files changed, 117 insertions(+), 41 deletions(-) diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java index cd579561d7f5..4a6cfc30adee 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java @@ -114,6 +114,7 @@ public class StudyUtils { return fileName.contains(".zip"); } + @Nullable public static T getFirst(@NotNull final Iterable container) { Iterator iterator = container.iterator(); if (!iterator.hasNext()) { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java index 9e85361f40a1..e958b90ab1b2 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java @@ -15,6 +15,7 @@ import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; import com.jetbrains.edu.learning.editor.StudyEditor; +import com.jetbrains.edu.learning.navigation.StudyNavigator; import com.jetbrains.edu.learning.statistics.EduUsagesCollector; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -76,7 +77,7 @@ abstract public class StudyTaskNavigationAction extends StudyActionWithShortcut } } - private static void updateProjectView(@NotNull Project project, VirtualFile shouldBeActive) { + public static void updateProjectView(@NotNull Project project, VirtualFile shouldBeActive) { JTree tree = ProjectView.getInstance(project).getCurrentProjectViewPane().getTree(); if (shouldBeActive != null) { ProjectView.getInstance(project).selectCB(shouldBeActive, shouldBeActive, false).doWhenDone(() -> { @@ -113,32 +114,7 @@ abstract public class StudyTaskNavigationAction extends StudyActionWithShortcut @Nullable protected VirtualFile getFileToActivate(@NotNull Project project, Map nextTaskFiles, VirtualFile taskDir) { - VirtualFile shouldBeActive = null; - for (Map.Entry entry : nextTaskFiles.entrySet()) { - String name = entry.getKey(); - TaskFile taskFile = entry.getValue(); - VirtualFile srcDir = taskDir.findChild(EduNames.SRC); - VirtualFile vf = srcDir == null ? taskDir.findChild(name) : srcDir.findChild(name); - if (vf != null) { - if (shouldBeActive != null) { - FileEditorManager.getInstance(project).openFile(vf, true); - } - if (shouldBeActive == null && !taskFile.getAnswerPlaceholders().isEmpty()) { - shouldBeActive = vf; - } - } - } - return shouldBeActive != null ? shouldBeActive : getFirstTaskFile(taskDir, project); - } - - @Nullable - private static VirtualFile getFirstTaskFile(@NotNull final VirtualFile taskDir, @NotNull final Project project) { - for (VirtualFile virtualFile : taskDir.getChildren()) { - if (StudyUtils.getTaskFile(project, virtualFile) != null) { - return virtualFile; - } - } - return null; + return StudyNavigator.getFileToActivate(project, nextTaskFiles, taskDir); } @Override diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/navigation/StudyNavigator.java b/python/educational-core/student/src/com/jetbrains/edu/learning/navigation/StudyNavigator.java index 02b920104152..a3e4696d10e5 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/navigation/StudyNavigator.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/navigation/StudyNavigator.java @@ -1,13 +1,22 @@ package com.jetbrains.edu.learning.navigation; +import com.intellij.ide.projectView.ProjectView; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.courseFormat.*; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.Map; + +import static com.jetbrains.edu.learning.actions.StudyTaskNavigationAction.updateProjectView; public class StudyNavigator { private StudyNavigator() { @@ -41,19 +50,20 @@ public class StudyNavigator { return prevLesson.getTaskList().get(prevLesson.getTaskList().size() - 1); } - public static Lesson nextLesson(@NotNull final Lesson lesson) { + public static Lesson nextLesson(@NotNull final Lesson lesson) { List lessons = lesson.getCourse().getLessons(); int nextLessonIndex = lesson.getIndex(); if (nextLessonIndex >= lessons.size()) { return null; } final Lesson nextLesson = lessons.get(nextLessonIndex); - if (EduNames.PYCHARM_ADDITIONAL.equals(nextLesson.getName())) + if (EduNames.PYCHARM_ADDITIONAL.equals(nextLesson.getName())) { return null; + } return nextLesson; } - public static Lesson previousLesson(@NotNull final Lesson lesson) { + public static Lesson previousLesson(@NotNull final Lesson lesson) { int prevLessonIndex = lesson.getIndex() - 2; if (prevLessonIndex < 0) { return null; @@ -73,7 +83,7 @@ public class StudyNavigator { } } - public static void navigateToAnswerPlaceholder(@NotNull final Editor editor, @NotNull final AnswerPlaceholder answerPlaceholder) { + public static void navigateToAnswerPlaceholder(@NotNull final Editor editor, @NotNull final AnswerPlaceholder answerPlaceholder) { if (editor.isDisposed()) { return; } @@ -81,11 +91,84 @@ public class StudyNavigator { } - public static void navigateToFirstAnswerPlaceholder(@NotNull final Editor editor, @NotNull final TaskFile taskFile) { + public static void navigateToFirstAnswerPlaceholder(@NotNull final Editor editor, @NotNull final TaskFile taskFile) { if (!taskFile.getAnswerPlaceholders().isEmpty()) { AnswerPlaceholder firstAnswerPlaceholder = StudyUtils.getFirst(taskFile.getAnswerPlaceholders()); + if (firstAnswerPlaceholder == null) return; navigateToAnswerPlaceholder(editor, firstAnswerPlaceholder); } } + @Nullable + public static VirtualFile getFileToActivate(@NotNull Project project, Map nextTaskFiles, VirtualFile taskDir) { + VirtualFile shouldBeActive = null; + for (Map.Entry entry : nextTaskFiles.entrySet()) { + String name = entry.getKey(); + TaskFile taskFile = entry.getValue(); + VirtualFile srcDir = taskDir.findChild(EduNames.SRC); + VirtualFile vf = srcDir == null ? taskDir.findChild(name) : srcDir.findChild(name); + if (vf != null) { + if (shouldBeActive != null) { + FileEditorManager.getInstance(project).openFile(vf, true); + } + if (shouldBeActive == null && !taskFile.getAnswerPlaceholders().isEmpty()) { + shouldBeActive = vf; + } + } + } + return shouldBeActive != null ? shouldBeActive : getFirstTaskFile(taskDir, project); + } + + @Nullable + private static VirtualFile getFirstTaskFile(@NotNull final VirtualFile taskDir, @NotNull final Project project) { + for (VirtualFile virtualFile : taskDir.getChildren()) { + if (StudyUtils.getTaskFile(project, virtualFile) != null) { + return virtualFile; + } + } + return null; + } + + public static void navigateToTask(@NotNull final Project project, @NotNull final String lessonName, @NotNull final String taskName) { + final Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course == null) { + return; + } + final Lesson lesson = course.getLesson(lessonName); + if (lesson == null) { + return; + } + final Task task = lesson.getTask(taskName); + if (task == null) { + return; + } + ApplicationManager.getApplication().invokeLater(() -> { + for (VirtualFile file : FileEditorManager.getInstance(project).getOpenFiles()) { + FileEditorManager.getInstance(project).closeFile(file); + } + int nextTaskIndex = task.getIndex(); + int lessonIndex = task.getLesson().getIndex(); + Map nextTaskFiles = task.getTaskFiles(); + VirtualFile projectDir = project.getBaseDir(); + String lessonDirName = EduNames.LESSON + String.valueOf(lessonIndex); + if (projectDir == null) { + return; + } + VirtualFile lessonDir = projectDir.findChild(lessonDirName); + if (lessonDir == null) { + return; + } + String taskDirName = EduNames.TASK + String.valueOf(nextTaskIndex); + VirtualFile taskDir = lessonDir.findChild(taskDirName); + if (taskDir == null) { + return; + } + if (nextTaskFiles.isEmpty()) { + ProjectView.getInstance(project).select(taskDir, taskDir, false); + } + VirtualFile toActivate = getFileToActivate(project, nextTaskFiles, taskDir); + + updateProjectView(project, toActivate); + }); + } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyBrowserWindow.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyBrowserWindow.java index 75d6146c48c9..72a7f47c33b6 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyBrowserWindow.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyBrowserWindow.java @@ -9,8 +9,10 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.EditorColorsScheme; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.io.StreamUtil; import com.jetbrains.edu.learning.StudyPluginConfigurator; +import com.jetbrains.edu.learning.navigation.StudyNavigator; import javafx.application.Platform; import javafx.concurrent.Worker; import javafx.embed.swing.JFXPanel; @@ -34,20 +36,25 @@ import java.awt.event.MouseEvent; import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class StudyBrowserWindow extends JFrame { private static final Logger LOG = Logger.getInstance(StudyToolWindow.class); private static final String EVENT_TYPE_CLICK = "click"; + private static final Pattern IN_COURSE_LINK = Pattern.compile("#(\\w+)#(\\w+)#"); private JFXPanel myPanel; private WebView myWebComponent; private StackPane myPane; private WebEngine myEngine; private ProgressBar myProgressBar; + private final Project myProject; private boolean myLinkInNewBrowser = true; private boolean myShowProgress = false; - public StudyBrowserWindow(final boolean linkInNewWindow, final boolean showProgress) { + public StudyBrowserWindow(@NotNull final Project project, final boolean linkInNewWindow, final boolean showProgress) { + myProject = project; myLinkInNewBrowser = linkInNewWindow; myShowProgress = showProgress; setSize(new Dimension(900, 800)); @@ -218,13 +225,22 @@ public class StudyBrowserWindow extends JFrame { public void handleEvent(Event ev) { String domEventType = ev.getType(); if (domEventType.equals(EVENT_TYPE_CLICK)) { - myEngine.setJavaScriptEnabled(true); - myEngine.getLoadWorker().cancel(); - ev.preventDefault(); - final String href = getLink((Element)ev.getTarget()); - if (href == null) return; - BrowserUtil.browse(href); - + Element target = (Element)ev.getTarget(); + String hrefAttribute = target.getAttribute("href"); + final Matcher matcher = IN_COURSE_LINK.matcher(hrefAttribute); + if (matcher.matches()) { + final String lessonName = matcher.group(1); + final String taskName = matcher.group(2); + StudyNavigator.navigateToTask(myProject, lessonName, taskName); + } + else { + myEngine.setJavaScriptEnabled(true); + myEngine.getLoadWorker().cancel(); + ev.preventDefault(); + final String href = getLink(target); + if (href == null) return; + BrowserUtil.browse(href); + } } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyJavaFxToolWindow.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyJavaFxToolWindow.java index d9507f11164f..0a34de7f6e5d 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyJavaFxToolWindow.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyJavaFxToolWindow.java @@ -31,7 +31,7 @@ public class StudyJavaFxToolWindow extends StudyToolWindow { @Override public JComponent createTaskInfoPanel(Project project) { - myBrowserWindow = new StudyBrowserWindow(true, false); + myBrowserWindow = new StudyBrowserWindow(project, true, false); myBrowserWindow.addBackAndOpenButtons(); JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));