mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master' into meanmail/EDU-839
This commit is contained in:
@@ -41,14 +41,11 @@
|
||||
|
||||
<extensionPoints>
|
||||
<extensionPoint qualifiedName="Edu.eduIntelliJProjectTemplate" interface="com.jetbrains.edu.learning.intellij.EduIntelliJProjectTemplate"/>
|
||||
<extensionPoint qualifiedName="Edu.StudyExecutor" beanClass="com.intellij.lang.LanguageExtensionPoint">
|
||||
<with attribute="implementationClass" implements="com.jetbrains.edu.learning.checker.StudyExecutor"/>
|
||||
</extensionPoint>
|
||||
<extensionPoint qualifiedName="Edu.pluginConfigurator" beanClass="com.intellij.lang.LanguageExtensionPoint">
|
||||
<with attribute="implementationClass" implements="com.jetbrains.edu.learning.EduPluginConfigurator"/>
|
||||
</extensionPoint>
|
||||
<extensionPoint qualifiedName="Edu.studyTwitterPluginConfigurator" interface="com.jetbrains.edu.learning.StudyTwitterPluginConfigurator"/>
|
||||
<extensionPoint qualifiedName="Edu.checkListener" interface="com.jetbrains.edu.learning.StudyCheckListener"/>
|
||||
<extensionPoint qualifiedName="Edu.checkListener" interface="com.jetbrains.edu.learning.checker.StudyCheckListener"/>
|
||||
<extensionPoint qualifiedName="Edu.optionsProvider" beanClass="com.jetbrains.edu.learning.settings.StudyOptionsProviderEP">
|
||||
<with attribute="instance" implements="com.jetbrains.edu.learning.settings.StudyOptionsProvider"/>
|
||||
</extensionPoint>
|
||||
@@ -78,8 +75,6 @@
|
||||
description="Watch test input "/>
|
||||
<action id="ShowHintAction" class="com.jetbrains.edu.learning.actions.StudyShowHintAction" text="Show Hint for Answer Placeholder"
|
||||
description="Show hint for answer placeholder"/>
|
||||
<action id="StudyRunAction" class="com.jetbrains.edu.learning.actions.StudyRunAction" text="Run File With Tests"
|
||||
description="Run your code with tests"/>
|
||||
<action id="StudyShowAnswersAction" class="com.jetbrains.edu.learning.actions.StudyFillPlaceholdersAction"
|
||||
text="Fill Answer Placeholders">
|
||||
<add-to-group group-id="HelpMenu" anchor="before" relative-to-action="HelpTopics"/>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 228 B |
Binary file not shown.
|
After Width: | Height: | Size: 290 B |
@@ -105,8 +105,10 @@ public class CCProjectComponent extends AbstractProjectComponent {
|
||||
|
||||
public void projectOpened() {
|
||||
migrateIfNeeded();
|
||||
VirtualFileManager.getInstance().addVirtualFileListener(myTaskFileLifeListener);
|
||||
EduUsagesCollector.projectTypeOpened(CCUtils.COURSE_MODE);
|
||||
if (CCUtils.isCourseCreator(myProject)) {
|
||||
VirtualFileManager.getInstance().addVirtualFileListener(myTaskFileLifeListener);
|
||||
EduUsagesCollector.projectTypeOpened(CCUtils.COURSE_MODE);
|
||||
}
|
||||
}
|
||||
|
||||
public void projectClosed() {
|
||||
|
||||
+2
-1
@@ -28,6 +28,7 @@ import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
|
||||
import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholderSubtaskInfo;
|
||||
import com.jetbrains.edu.learning.courseFormat.Lesson;
|
||||
import com.jetbrains.edu.learning.courseFormat.TaskFile;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.PyCharmTask;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -227,7 +228,7 @@ public class CCSubtaskEditorNotificationProvider extends EditorNotifications.Pro
|
||||
final Lesson lesson = myTask.getLesson();
|
||||
final List<Task> list = lesson.getTaskList();
|
||||
final int i = list.indexOf(myTask);
|
||||
final Task task = new Task();
|
||||
final Task task = new PyCharmTask();
|
||||
task.copyTaskParameters(myTask);
|
||||
for (TaskFile taskFile : task.getTaskFiles().values()) {
|
||||
taskFile.setTask(task);
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.courseFormat.Lesson;
|
||||
import com.jetbrains.edu.learning.courseFormat.StudyItem;
|
||||
import com.jetbrains.edu.learning.courseFormat.TaskFile;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.PyCharmTask;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -264,7 +265,7 @@ public class CCUtils {
|
||||
|
||||
final Lesson lesson = new Lesson();
|
||||
lesson.setName(EduNames.PYCHARM_ADDITIONAL);
|
||||
final Task task = new Task();
|
||||
final Task task = new PyCharmTask();
|
||||
task.setLesson(lesson);
|
||||
task.setName(EduNames.PYCHARM_ADDITIONAL);
|
||||
task.setIndex(1);
|
||||
|
||||
+1
-3
@@ -16,8 +16,6 @@ import com.jetbrains.edu.learning.courseFormat.TaskFile;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class CCVirtualFileListener implements VirtualFileListener {
|
||||
|
||||
@Override
|
||||
@@ -37,7 +35,7 @@ public class CCVirtualFileListener implements VirtualFileListener {
|
||||
return;
|
||||
}
|
||||
Course course = StudyTaskManager.getInstance(project).getCourse();
|
||||
if (course == null || !CCUtils.isCourseCreator(project)) {
|
||||
if (course == null) {
|
||||
return;
|
||||
}
|
||||
TaskFile taskFile = StudyUtils.getTaskFile(project, createdFile);
|
||||
|
||||
+2
-4
@@ -15,8 +15,6 @@ import icons.EducationalCoreIcons;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CCCreateLesson extends CCCreateStudyItemActionBase {
|
||||
public static final String TITLE = "Create New " + EduNames.LESSON_TITLED;
|
||||
|
||||
@@ -82,8 +80,8 @@ public class CCCreateLesson extends CCCreateStudyItemActionBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<? extends StudyItem> getSiblings(@NotNull Course course, @Nullable StudyItem parentItem) {
|
||||
return course.getLessons(true);
|
||||
protected void sortSiblings(@NotNull Course course, @Nullable StudyItem parentItem) {
|
||||
course.sortLessons();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-9
@@ -18,15 +18,12 @@ import com.jetbrains.edu.coursecreator.CCUtils;
|
||||
import com.jetbrains.edu.coursecreator.ui.CCCreateStudyItemDialog;
|
||||
import com.jetbrains.edu.learning.StudyTaskManager;
|
||||
import com.jetbrains.edu.learning.StudyUtils;
|
||||
import com.jetbrains.edu.learning.core.EduUtils;
|
||||
import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.courseFormat.StudyItem;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class CCCreateStudyItemActionBase extends DumbAwareAction {
|
||||
|
||||
@@ -50,7 +47,6 @@ public abstract class CCCreateStudyItemActionBase extends DumbAwareAction {
|
||||
createItem(view, project, directory, course);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void update(@NotNull AnActionEvent event) {
|
||||
final Presentation presentation = event.getPresentation();
|
||||
@@ -81,13 +77,11 @@ public abstract class CCCreateStudyItemActionBase extends DumbAwareAction {
|
||||
presentation.setEnabledAndVisible(true);
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
protected abstract PsiDirectory getParentDir(@NotNull final Project project,
|
||||
@NotNull final Course course,
|
||||
@NotNull final PsiDirectory directory);
|
||||
|
||||
|
||||
@Nullable
|
||||
public PsiDirectory createItem(@Nullable final IdeView view, @NotNull final Project project,
|
||||
@NotNull final PsiDirectory sourceDirectory, @NotNull final Course course) {
|
||||
@@ -103,13 +97,12 @@ public abstract class CCCreateStudyItemActionBase extends DumbAwareAction {
|
||||
CCUtils.updateHigherElements(parentDir.getVirtualFile().getChildren(), getStudyOrderable(item),
|
||||
item.getIndex() - 1, getItemName(), 1);
|
||||
addItem(course, item);
|
||||
Collections.sort(getSiblings(course, parentItem), EduUtils.INDEX_COMPARATOR);
|
||||
sortSiblings(course, parentItem);
|
||||
return createItemDir(project, item, view, parentDir, course);
|
||||
}
|
||||
|
||||
protected abstract void addItem(@NotNull final Course course, @NotNull final StudyItem item);
|
||||
|
||||
|
||||
protected abstract Function<VirtualFile, ? extends StudyItem> getStudyOrderable(@NotNull final StudyItem item);
|
||||
|
||||
protected abstract PsiDirectory createItemDir(@NotNull final Project project, @NotNull final StudyItem item,
|
||||
@@ -168,7 +161,7 @@ public abstract class CCCreateStudyItemActionBase extends DumbAwareAction {
|
||||
@NotNull final Project project,
|
||||
@NotNull final Course course);
|
||||
|
||||
protected abstract List<? extends StudyItem> getSiblings(@NotNull final Course course, @Nullable final StudyItem parentItem);
|
||||
protected abstract void sortSiblings(@NotNull final Course course, @Nullable final StudyItem parentItem);
|
||||
|
||||
protected abstract String getItemName();
|
||||
|
||||
|
||||
+6
-10
@@ -11,13 +11,13 @@ import com.jetbrains.edu.learning.core.EduUtils;
|
||||
import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.courseFormat.Lesson;
|
||||
import com.jetbrains.edu.learning.courseFormat.StudyItem;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.PyCharmTask;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
import icons.EducationalCoreIcons;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class CCCreateTask extends CCCreateStudyItemActionBase {
|
||||
public static final String TITLE = "Create New " + EduNames.TASK_TITLED;
|
||||
@@ -26,9 +26,6 @@ public class CCCreateTask extends CCCreateStudyItemActionBase {
|
||||
super(EduNames.TASK_TITLED, TITLE, EducationalCoreIcons.Task);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected PsiDirectory getParentDir(@NotNull Project project, @NotNull Course course, @NotNull PsiDirectory directory) {
|
||||
@@ -59,8 +56,8 @@ public class CCCreateTask extends CCCreateStudyItemActionBase {
|
||||
@Override
|
||||
@Nullable
|
||||
protected PsiDirectory createItemDir(@NotNull final Project project, @NotNull final StudyItem item,
|
||||
@Nullable final IdeView view, @NotNull final PsiDirectory parentDirectory,
|
||||
@NotNull final Course course) {
|
||||
@Nullable final IdeView view, @NotNull final PsiDirectory parentDirectory,
|
||||
@NotNull final Course course) {
|
||||
EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(course.getLanguageById());
|
||||
if (configurator != null) {
|
||||
return configurator.createTaskContent(project, (Task)item, view, parentDirectory, course);
|
||||
@@ -100,11 +97,10 @@ public class CCCreateTask extends CCCreateStudyItemActionBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<? extends StudyItem> getSiblings(@NotNull Course course, @Nullable StudyItem parentItem) {
|
||||
protected void sortSiblings(@NotNull Course course, @Nullable StudyItem parentItem) {
|
||||
if (parentItem instanceof Lesson) {
|
||||
return ((Lesson)parentItem).getTaskList();
|
||||
Collections.sort(((Lesson)parentItem).getTaskList(), EduUtils.INDEX_COMPARATOR);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -114,7 +110,7 @@ public class CCCreateTask extends CCCreateStudyItemActionBase {
|
||||
|
||||
@Override
|
||||
protected StudyItem createAndInitItem(@NotNull Course course, @Nullable StudyItem parentItem, String name, int index) {
|
||||
final Task task = new Task(name);
|
||||
final Task task = new PyCharmTask(name);
|
||||
task.setIndex(index);
|
||||
if (parentItem == null) {
|
||||
return null;
|
||||
|
||||
+1
-3
@@ -22,14 +22,12 @@ import com.jetbrains.edu.coursecreator.CCUtils;
|
||||
import com.jetbrains.edu.coursecreator.ui.CCMoveStudyItemDialog;
|
||||
import com.jetbrains.edu.learning.StudyTaskManager;
|
||||
import com.jetbrains.edu.learning.core.EduNames;
|
||||
import com.jetbrains.edu.learning.core.EduUtils;
|
||||
import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.courseFormat.Lesson;
|
||||
import com.jetbrains.edu.learning.courseFormat.StudyItem;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
public class CCLessonMoveHandlerDelegate extends MoveHandlerDelegate {
|
||||
|
||||
@@ -109,7 +107,7 @@ public class CCLessonMoveHandlerDelegate extends MoveHandlerDelegate {
|
||||
newItemIndex - 1, EduNames.LESSON, 1);
|
||||
|
||||
sourceLesson.setIndex(newItemIndex);
|
||||
Collections.sort(course.getLessons(true), EduUtils.INDEX_COMPARATOR);
|
||||
course.sortLessons();
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
+1
@@ -108,6 +108,7 @@ public class CCStepicConnector {
|
||||
postedCourse.setLessons(course.getLessons(true));
|
||||
postedCourse.setAuthors(course.getAuthors());
|
||||
postedCourse.setCourseMode(CCUtils.COURSE_MODE);
|
||||
postedCourse.setLanguage(course.getLanguageID());
|
||||
final int sectionId = postModule(postedCourse.getId(), 1, String.valueOf(postedCourse.getName()));
|
||||
int position = 1;
|
||||
for (Lesson lesson : course.getLessons()) {
|
||||
|
||||
@@ -105,7 +105,6 @@ public interface EduPluginConfigurator {
|
||||
}
|
||||
group.add(action);
|
||||
}
|
||||
group.add(new StudyRunAction());
|
||||
group.add(new StudyEditInputAction());
|
||||
return group;
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@ public class StudySerializationUtils {
|
||||
private static String THEORY_TAG = "theoryTask";
|
||||
private static String ADAPTIVE_TASK_PARAMETERS = "adaptiveTaskParameters";
|
||||
private static String ADAPTIVE = "adaptive";
|
||||
public static final String PYCHARM_TASK = "PyCharmTask";
|
||||
private static String TASK_WITH_SUBTASKS = "TaskWithSubtasks";
|
||||
private static String THEORY_TASK = "TheoryTask";
|
||||
private static String CHOICE_TASK = "ChoiceTask";
|
||||
@@ -285,6 +286,9 @@ public class StudySerializationUtils {
|
||||
else if (Boolean.valueOf(adaptive.getAttributeValue(VALUE))) {
|
||||
task.setName(CODE_TASK);
|
||||
}
|
||||
else {
|
||||
task.setName(PYCHARM_TASK);
|
||||
}
|
||||
task.removeContent(adaptiveParams);
|
||||
task.removeContent(theoryTask);
|
||||
}
|
||||
@@ -638,7 +642,7 @@ public class StudySerializationUtils {
|
||||
case "choice": return gson.fromJson(json, ChoiceTask.class);
|
||||
case "theory": return gson.fromJson(json, TheoryTask.class);
|
||||
case "code": return gson.fromJson(json, CodeTask.class);
|
||||
case "pycharm": return gson.fromJson(json, Task.class);
|
||||
case "pycharm": return gson.fromJson(json, PyCharmTask.class);
|
||||
case "subtasks": return gson.fromJson(json, TaskWithSubtasks.class);
|
||||
default: {
|
||||
LOG.warn("Unsupported task type " + taskType);
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
package com.jetbrains.edu.learning;
|
||||
|
||||
import com.intellij.execution.RunContentExecutor;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.execution.process.ProcessHandler;
|
||||
import com.intellij.ide.IdeView;
|
||||
import com.intellij.ide.fileTemplates.FileTemplate;
|
||||
import com.intellij.ide.fileTemplates.FileTemplateManager;
|
||||
import com.intellij.ide.fileTemplates.FileTemplateUtil;
|
||||
import com.intellij.ide.projectView.ProjectView;
|
||||
import com.intellij.ide.util.EditorHelper;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
@@ -31,7 +27,6 @@ import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
|
||||
import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.ui.popup.Balloon;
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
||||
@@ -61,8 +56,6 @@ import com.intellij.util.TimeoutUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.text.MarkdownUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.jetbrains.edu.learning.checker.StudyExecutor;
|
||||
import com.jetbrains.edu.learning.checker.StudyTestRunner;
|
||||
import com.jetbrains.edu.learning.core.EduAnswerPlaceholderDeleteHandler;
|
||||
import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter;
|
||||
import com.jetbrains.edu.learning.core.EduNames;
|
||||
@@ -237,42 +230,6 @@ public class StudyUtils {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Sdk findSdk(@NotNull final Task task, @NotNull final Project project) {
|
||||
final Language language = task.getLesson().getCourse().getLanguageById();
|
||||
return StudyExecutor.INSTANCE.forLanguage(language).findSdk(project);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static StudyTestRunner getTestRunner(@NotNull final Task task, @NotNull final VirtualFile taskDir) {
|
||||
final Language language = task.getLesson().getCourse().getLanguageById();
|
||||
return StudyExecutor.INSTANCE.forLanguage(language).getTestRunner(task, taskDir);
|
||||
}
|
||||
|
||||
public static RunContentExecutor getExecutor(@NotNull final Project project, @NotNull final Task currentTask,
|
||||
@NotNull final ProcessHandler handler) {
|
||||
final Language language = currentTask.getLesson().getCourse().getLanguageById();
|
||||
return StudyExecutor.INSTANCE.forLanguage(language).getExecutor(project, handler);
|
||||
}
|
||||
|
||||
public static void setCommandLineParameters(@NotNull final GeneralCommandLine cmd,
|
||||
@NotNull final Project project,
|
||||
@NotNull final String filePath,
|
||||
@NotNull final String sdkPath,
|
||||
@NotNull final Task currentTask) {
|
||||
final Language language = currentTask.getLesson().getCourse().getLanguageById();
|
||||
StudyExecutor.INSTANCE.forLanguage(language).setCommandLineParameters(cmd, project, filePath, sdkPath, currentTask);
|
||||
}
|
||||
|
||||
public static void showNoSdkNotification(@NotNull final Task currentTask, @NotNull final Project project) {
|
||||
final Lesson lesson = currentTask.getLesson();
|
||||
if (lesson == null) return;
|
||||
final Course course = lesson.getCourse();
|
||||
if (course == null) return;
|
||||
final Language language = course.getLanguageById();
|
||||
StudyExecutor.INSTANCE.forLanguage(language).showNoSdkNotification(project);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* shows pop up in the center of "check task" button in study editor
|
||||
@@ -735,7 +692,7 @@ public class StudyUtils {
|
||||
}
|
||||
|
||||
public static void registerStudyToolWindow(@Nullable final Course course, Project project) {
|
||||
if (course != null && "PyCharm".equals(course.getCourseType())) {
|
||||
if (course != null && EduNames.PYCHARM.equals(course.getCourseType())) {
|
||||
final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project);
|
||||
registerToolWindows(toolWindowManager, project);
|
||||
final ToolWindow studyToolWindow = toolWindowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW);
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.jetbrains.edu.learning.StudyCheckListener;
|
||||
import com.jetbrains.edu.learning.checker.StudyCheckListener;
|
||||
import com.jetbrains.edu.learning.StudyUtils;
|
||||
import com.jetbrains.edu.learning.checker.StudyCheckUtils;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
package com.jetbrains.edu.learning.actions;
|
||||
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.RunContentExecutor;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.execution.process.OSProcessHandler;
|
||||
import com.intellij.execution.process.ProcessHandler;
|
||||
import com.intellij.execution.process.ProcessListener;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.jetbrains.edu.learning.core.EduUtils;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
import com.jetbrains.edu.learning.courseFormat.TaskFile;
|
||||
import com.jetbrains.edu.learning.StudyTaskManager;
|
||||
import com.jetbrains.edu.learning.StudyUtils;
|
||||
import com.jetbrains.edu.learning.courseFormat.UserTest;
|
||||
import com.jetbrains.edu.learning.editor.StudyEditor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class StudyRunAction extends DumbAwareAction {
|
||||
private static final Logger LOG = Logger.getInstance(StudyRunAction.class.getName());
|
||||
public static final String ACTION_ID = "StudyRunAction";
|
||||
private ProcessHandler myHandler;
|
||||
private List<ProcessListener> myProcessListeners = new LinkedList<>();
|
||||
|
||||
public StudyRunAction() {
|
||||
super("Run File With Tests", "Run your code with tests", AllIcons.General.Run);
|
||||
}
|
||||
|
||||
public void run(@NotNull final Project project) {
|
||||
if (myHandler != null && !myHandler.isProcessTerminated()) return;
|
||||
Editor selectedEditor = StudyUtils.getSelectedEditor(project);
|
||||
FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
|
||||
assert selectedEditor != null;
|
||||
VirtualFile openedFile = fileDocumentManager.getFile(selectedEditor.getDocument());
|
||||
|
||||
if (openedFile != null && openedFile.getCanonicalPath() != null) {
|
||||
String filePath = openedFile.getCanonicalPath();
|
||||
executeFile(project, openedFile, filePath);
|
||||
}
|
||||
}
|
||||
|
||||
private void executeFile(@NotNull final Project project,
|
||||
@NotNull final VirtualFile openedFile, @NotNull final String filePath) {
|
||||
GeneralCommandLine cmd = new GeneralCommandLine();
|
||||
cmd.withWorkDirectory(openedFile.getParent().getCanonicalPath());
|
||||
|
||||
TaskFile selectedTaskFile = StudyUtils.getTaskFile(project, openedFile);
|
||||
assert selectedTaskFile != null;
|
||||
final Task currentTask = selectedTaskFile.getTask();
|
||||
final Sdk sdk = StudyUtils.findSdk(currentTask, project);
|
||||
if (sdk == null) {
|
||||
StudyUtils.showNoSdkNotification(currentTask, project);
|
||||
return;
|
||||
}
|
||||
String sdkHomePath = sdk.getHomePath();
|
||||
if (sdkHomePath != null) {
|
||||
cmd.setExePath(sdkHomePath);
|
||||
StudyUtils.setCommandLineParameters(cmd, project, filePath, sdkHomePath, currentTask);
|
||||
|
||||
try {
|
||||
myHandler = new OSProcessHandler(cmd);
|
||||
}
|
||||
catch (ExecutionException e) {
|
||||
LOG.error(e);
|
||||
return;
|
||||
}
|
||||
|
||||
for (ProcessListener processListener : myProcessListeners) {
|
||||
myHandler.addProcessListener(processListener);
|
||||
}
|
||||
|
||||
final RunContentExecutor executor = StudyUtils.getExecutor(project, currentTask, myHandler);
|
||||
if (executor != null) {
|
||||
Disposer.register(project, executor);
|
||||
executor.run();
|
||||
}
|
||||
EduUtils.synchronize();
|
||||
}
|
||||
}
|
||||
|
||||
public void addProcessListener(@NotNull final ProcessListener processListener) {
|
||||
myProcessListeners.add(processListener);
|
||||
}
|
||||
|
||||
public void removeProcessListener(@NotNull final ProcessListener processListener) {
|
||||
myProcessListeners.remove(processListener);
|
||||
}
|
||||
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
final Project project = e.getProject();
|
||||
if (project != null) {
|
||||
run(project);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(AnActionEvent e) {
|
||||
EduUtils.enableAction(e, false);
|
||||
|
||||
final Project project = e.getProject();
|
||||
if (project != null) {
|
||||
StudyEditor studyEditor = StudyUtils.getSelectedStudyEditor(project);
|
||||
if (studyEditor != null) {
|
||||
final List<UserTest> userTests = StudyTaskManager.getInstance(project).getUserTests(studyEditor.getTaskFile().getTask());
|
||||
if (!userTests.isEmpty()) {
|
||||
EduUtils.enableAction(e, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.jetbrains.edu.learning;
|
||||
package com.jetbrains.edu.learning.checker;
|
||||
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.project.Project;
|
||||
+10
-107
@@ -3,29 +3,24 @@ package com.jetbrains.edu.learning.checker;
|
||||
import com.intellij.execution.process.CapturingProcessHandler;
|
||||
import com.intellij.execution.process.ProcessOutput;
|
||||
import com.intellij.ide.projectView.ProjectView;
|
||||
import com.intellij.openapi.actionSystem.ActionManager;
|
||||
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.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.jetbrains.edu.learning.*;
|
||||
import com.jetbrains.edu.learning.actions.StudyRunAction;
|
||||
import com.jetbrains.edu.learning.StudySettings;
|
||||
import com.jetbrains.edu.learning.StudyState;
|
||||
import com.jetbrains.edu.learning.StudyTaskManager;
|
||||
import com.jetbrains.edu.learning.StudyUtils;
|
||||
import com.jetbrains.edu.learning.core.EduNames;
|
||||
import com.jetbrains.edu.learning.core.EduUtils;
|
||||
import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
|
||||
import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.courseFormat.StudyStatus;
|
||||
import com.jetbrains.edu.learning.courseFormat.TaskFile;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.ChoiceTask;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.TheoryTask;
|
||||
import com.jetbrains.edu.learning.stepic.EduAdaptiveStepicConnector;
|
||||
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
|
||||
@@ -33,8 +28,6 @@ import com.jetbrains.edu.learning.stepic.StepicUser;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroundable {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(StudyCheckTask.class);
|
||||
@@ -47,9 +40,7 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun
|
||||
private final Ref<Boolean> myCheckInProcess;
|
||||
private final Process myTestProcess;
|
||||
private final String myCommandLine;
|
||||
private boolean myRunTestFile = true;
|
||||
private static final String FAILED_CHECK_LAUNCH = "Failed to launch checking";
|
||||
private static final String DO_NOT_RUN_ON_CHECK = "DO_NOT_RUN_ON_CHECK";
|
||||
|
||||
public StudyCheckTask(Project project, StudyState studyState, Ref<Boolean> checkInProcess, Process testProcess, String commandLine) {
|
||||
super(project, "Checking Task");
|
||||
@@ -98,9 +89,6 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun
|
||||
|
||||
private void checkForEduCourse(@NotNull ProgressIndicator indicator) {
|
||||
final StudyTestsOutputParser.TestsOutput testsOutput = getTestOutput(indicator);
|
||||
if (myRunTestFile) {
|
||||
ApplicationManager.getApplication().invokeLater(() -> runTask(myProject));
|
||||
}
|
||||
if (testsOutput != null) {
|
||||
if (testsOutput.isSuccess()) {
|
||||
onTaskSolved(testsOutput.getMessage());
|
||||
@@ -119,14 +107,6 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun
|
||||
}
|
||||
}
|
||||
|
||||
private static void runTask(@NotNull Project project) {
|
||||
final StudyRunAction runAction = (StudyRunAction)ActionManager.getInstance().getAction(StudyRunAction.ACTION_ID);
|
||||
if (runAction == null) {
|
||||
return;
|
||||
}
|
||||
runAction.run(project);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private StudyTestsOutputParser.TestsOutput getTestOutput(@NotNull ProgressIndicator indicator) {
|
||||
final CapturingProcessHandler handler = new CapturingProcessHandler(myTestProcess, null, myCommandLine);
|
||||
@@ -135,7 +115,6 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun
|
||||
ApplicationManager.getApplication().invokeLater(
|
||||
() -> StudyCheckUtils.showTestResultPopUp("Check cancelled", MessageType.WARNING.getPopupBackground(), myProject));
|
||||
}
|
||||
myRunTestFile = !output.getStdout().contains(DO_NOT_RUN_ON_CHECK);
|
||||
final Course course = StudyTaskManager.getInstance(myProject).getCourse();
|
||||
if (course != null) {
|
||||
final StudyTestsOutputParser.TestsOutput testsOutput = StudyTestsOutputParser.getTestsOutput(output, course.isAdaptive());
|
||||
@@ -173,7 +152,8 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun
|
||||
if (reactionPosted) {
|
||||
if (myStatusBeforeCheck != StudyStatus.Solved) {
|
||||
myTask.setStatus(StudyStatus.Solved);
|
||||
EduAdaptiveStepicConnector.addNextRecommendedTask(myProject, indicator, EduAdaptiveStepicConnector.NEXT_RECOMMENDATION_REACTION);
|
||||
EduAdaptiveStepicConnector.addNextRecommendedTask(myProject, myTask.getLesson(), indicator,
|
||||
EduAdaptiveStepicConnector.NEXT_RECOMMENDATION_REACTION);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -203,7 +183,8 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun
|
||||
if (pair.getFirst()) {
|
||||
onTaskSolved("Congratulations! Remote tests passed.");
|
||||
if (myStatusBeforeCheck != StudyStatus.Solved) {
|
||||
EduAdaptiveStepicConnector.addNextRecommendedTask(myProject, indicator, EduAdaptiveStepicConnector.NEXT_RECOMMENDATION_REACTION);
|
||||
EduAdaptiveStepicConnector.addNextRecommendedTask(myProject, myTask.getLesson(), indicator,
|
||||
EduAdaptiveStepicConnector.NEXT_RECOMMENDATION_REACTION);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -224,91 +205,13 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun
|
||||
}
|
||||
|
||||
protected void onTaskFailed(@NotNull String message) {
|
||||
final Course course = StudyTaskManager.getInstance(myProject).getCourse();
|
||||
myTask.setStatus(StudyStatus.Failed);
|
||||
if (course != null) {
|
||||
if (course.isAdaptive()) {
|
||||
ApplicationManager.getApplication().invokeLater(
|
||||
() -> {
|
||||
StudyCheckUtils.showTestResultPopUp("Failed", MessageType.ERROR.getPopupBackground(), myProject);
|
||||
StudyCheckUtils.showTestResultsToolWindow(myProject, message, false);
|
||||
});
|
||||
}
|
||||
else {
|
||||
ApplicationManager.getApplication()
|
||||
.invokeLater(() -> StudyCheckUtils.showTestResultPopUp(message, MessageType.ERROR.getPopupBackground(), myProject));
|
||||
}
|
||||
}
|
||||
myTask.getChecker(myProject).onTaskFailed(message);
|
||||
}
|
||||
|
||||
protected void onTaskSolved(@NotNull String message) {
|
||||
final Course course = StudyTaskManager.getInstance(myProject).getCourse();
|
||||
myTask.setStatus(StudyStatus.Solved);
|
||||
if (course != null) {
|
||||
if (course.isAdaptive()) {
|
||||
ApplicationManager.getApplication().invokeLater(
|
||||
() -> {
|
||||
if (myTask instanceof ChoiceTask) {
|
||||
StudyCheckUtils.showTestResultPopUp("Congratulations!", MessageType.INFO.getPopupBackground(), myProject);
|
||||
}
|
||||
else {
|
||||
StudyCheckUtils.showTestResultPopUp("Congratulations!", MessageType.INFO.getPopupBackground(), myProject);
|
||||
StudyCheckUtils.showTestResultsToolWindow(myProject, message, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
if (myTask instanceof TaskWithSubtasks) {
|
||||
boolean hasMoreSubtasks = ((TaskWithSubtasks)myTask).activeSubtaskNotLast();
|
||||
final int activeSubtaskIndex = ((TaskWithSubtasks)myTask).getActiveSubtaskIndex();
|
||||
int visibleSubtaskIndex = activeSubtaskIndex + 1;
|
||||
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
int subtaskSize = ((TaskWithSubtasks)myTask).getLastSubtaskIndex() + 1;
|
||||
String resultMessage = !hasMoreSubtasks ? message : "Subtask " + visibleSubtaskIndex + "/" + subtaskSize + " solved";
|
||||
StudyCheckUtils.showTestResultPopUp(resultMessage, MessageType.INFO.getPopupBackground(), myProject);
|
||||
if (hasMoreSubtasks) {
|
||||
int nextSubtaskIndex = activeSubtaskIndex + 1;
|
||||
StudySubtaskUtils.switchStep(myProject, (TaskWithSubtasks)myTask, nextSubtaskIndex);
|
||||
rememberAnswers(nextSubtaskIndex);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
ApplicationManager.getApplication().invokeLater(
|
||||
() -> StudyCheckUtils.showTestResultPopUp(message, MessageType.INFO.getPopupBackground(), myProject));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void rememberAnswers(int nextSubtaskIndex) {
|
||||
VirtualFile taskDir = myTask.getTaskDir(myProject);
|
||||
if (taskDir == null) {
|
||||
return;
|
||||
}
|
||||
VirtualFile srcDir = taskDir.findChild(EduNames.SRC);
|
||||
if (srcDir != null) {
|
||||
taskDir = srcDir;
|
||||
}
|
||||
for (Map.Entry<String, TaskFile> entry : myTask.getTaskFiles().entrySet()) {
|
||||
TaskFile taskFile = entry.getValue();
|
||||
VirtualFile virtualFile = taskDir.findFileByRelativePath(entry.getKey());
|
||||
if (virtualFile == null) {
|
||||
continue;
|
||||
}
|
||||
Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
|
||||
if (document == null) {
|
||||
continue;
|
||||
}
|
||||
for (AnswerPlaceholder placeholder : taskFile.getActivePlaceholders()) {
|
||||
if (placeholder.getSubtaskInfos().containsKey(nextSubtaskIndex - 1)) {
|
||||
int offset = placeholder.getOffset();
|
||||
String answer = document.getText(TextRange.create(offset, offset + placeholder.getRealLength()));
|
||||
placeholder.getSubtaskInfos().get(nextSubtaskIndex - 1).setAnswer(answer);
|
||||
}
|
||||
}
|
||||
}
|
||||
myTask.getChecker(myProject).onTaskSolved(message);
|
||||
}
|
||||
|
||||
private void runAfterTaskCheckedActions() {
|
||||
|
||||
+22
-26
@@ -28,8 +28,8 @@ import com.jetbrains.edu.learning.core.EduDocumentListener;
|
||||
import com.jetbrains.edu.learning.core.EduNames;
|
||||
import com.jetbrains.edu.learning.core.EduUtils;
|
||||
import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
import com.jetbrains.edu.learning.courseFormat.TaskFile;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
import com.jetbrains.edu.learning.editor.StudyEditor;
|
||||
import com.jetbrains.edu.learning.navigation.StudyNavigator;
|
||||
import com.jetbrains.edu.learning.ui.StudyTestResultsToolWindowFactory;
|
||||
@@ -108,7 +108,7 @@ public class StudyCheckUtils {
|
||||
}
|
||||
|
||||
|
||||
public static void showTestResultPopUp(final String text, Color color, @NotNull final Project project) {
|
||||
public static void showTestResultPopUp(@NotNull final String text, Color color, @NotNull final Project project) {
|
||||
BalloonBuilder balloonBuilder =
|
||||
JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(text, null, color, null);
|
||||
final Balloon balloon = balloonBuilder.createBalloon();
|
||||
@@ -201,30 +201,26 @@ public class StudyCheckUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void showTestResultsToolWindow(@NotNull final Project project, @NotNull final String message, boolean solved) {
|
||||
final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project);
|
||||
ToolWindow window = toolWindowManager.getToolWindow(StudyTestResultsToolWindowFactoryKt.ID);
|
||||
if (window == null) {
|
||||
toolWindowManager.registerToolWindow(StudyTestResultsToolWindowFactoryKt.ID, true, ToolWindowAnchor.BOTTOM);
|
||||
window = toolWindowManager.getToolWindow(StudyTestResultsToolWindowFactoryKt.ID);
|
||||
new StudyTestResultsToolWindowFactory().createToolWindowContent(project, window);
|
||||
}
|
||||
|
||||
final Content[] contents = window.getContentManager().getContents();
|
||||
for (Content content : contents) {
|
||||
final JComponent component = content.getComponent();
|
||||
if (component instanceof ConsoleViewImpl) {
|
||||
((ConsoleViewImpl)component).clear();
|
||||
if (!solved) {
|
||||
((ConsoleViewImpl)component).print(message, ConsoleViewContentType.ERROR_OUTPUT);
|
||||
}
|
||||
else {
|
||||
((ConsoleViewImpl)component).print(message, ConsoleViewContentType.NORMAL_OUTPUT);
|
||||
}
|
||||
window.setAvailable(true, () -> {});
|
||||
window.show(() -> {});
|
||||
return;
|
||||
public static void showTestResultsToolWindow(@NotNull final Project project, @NotNull final String message) {
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project);
|
||||
ToolWindow window = toolWindowManager.getToolWindow(StudyTestResultsToolWindowFactoryKt.ID);
|
||||
if (window == null) {
|
||||
toolWindowManager.registerToolWindow(StudyTestResultsToolWindowFactoryKt.ID, true, ToolWindowAnchor.BOTTOM);
|
||||
window = toolWindowManager.getToolWindow(StudyTestResultsToolWindowFactoryKt.ID);
|
||||
new StudyTestResultsToolWindowFactory().createToolWindowContent(project, window);
|
||||
}
|
||||
}
|
||||
|
||||
final Content[] contents = window.getContentManager().getContents();
|
||||
for (Content content : contents) {
|
||||
final JComponent component = content.getComponent();
|
||||
if (component instanceof ConsoleViewImpl) {
|
||||
((ConsoleViewImpl)component).clear();
|
||||
((ConsoleViewImpl)component).print(message, ConsoleViewContentType.ERROR_OUTPUT);
|
||||
window.setAvailable(true,null);
|
||||
window.show(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* 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.edu.learning.checker;
|
||||
|
||||
import com.intellij.execution.RunContentExecutor;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.execution.process.ProcessHandler;
|
||||
import com.intellij.lang.LanguageExtension;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface StudyExecutor {
|
||||
LanguageExtension<StudyExecutor> INSTANCE = new LanguageExtension<>("Edu.StudyExecutor");
|
||||
|
||||
@Nullable
|
||||
Sdk findSdk(@NotNull final Project project);
|
||||
|
||||
StudyTestRunner getTestRunner(@NotNull final Task task, @NotNull final VirtualFile taskDir);
|
||||
|
||||
RunContentExecutor getExecutor(@NotNull final Project project, @NotNull final ProcessHandler handler);
|
||||
|
||||
void setCommandLineParameters(@NotNull final GeneralCommandLine cmd,
|
||||
@NotNull final Project project,
|
||||
@NotNull final String filePath,
|
||||
@NotNull final String sdkPath,
|
||||
@NotNull final Task currentTask);
|
||||
|
||||
void showNoSdkNotification(@NotNull final Project project);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.jetbrains.edu.learning.checker;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class StudyTaskChecker<T extends Task> {
|
||||
@NotNull protected final T myTask;
|
||||
@NotNull protected final Project myProject;
|
||||
|
||||
public StudyTaskChecker(@NotNull T task, @NotNull Project project) {
|
||||
myTask = task;
|
||||
myProject = project;
|
||||
}
|
||||
|
||||
public void onTaskSolved(@NotNull String message) {
|
||||
ApplicationManager.getApplication().invokeLater(
|
||||
() -> StudyCheckUtils.showTestResultPopUp(message, MessageType.INFO.getPopupBackground(), myProject));
|
||||
}
|
||||
|
||||
public void onTaskFailed(@NotNull String message) {
|
||||
ApplicationManager.getApplication()
|
||||
.invokeLater(() -> StudyCheckUtils.showTestResultPopUp(message, MessageType.ERROR.getPopupBackground(), myProject));
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package com.jetbrains.edu.learning.checker;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.jetbrains.edu.learning.StudySubtaskUtils;
|
||||
import com.jetbrains.edu.learning.core.EduNames;
|
||||
import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
|
||||
import com.jetbrains.edu.learning.courseFormat.TaskFile;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class TaskWithSubtasksChecker extends StudyTaskChecker<TaskWithSubtasks> {
|
||||
public TaskWithSubtasksChecker(@NotNull TaskWithSubtasks task,
|
||||
@NotNull Project project) {
|
||||
super(task, project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskSolved(@NotNull String message) {
|
||||
boolean hasMoreSubtasks = myTask.activeSubtaskNotLast();
|
||||
final int activeSubtaskIndex = myTask.getActiveSubtaskIndex();
|
||||
int visibleSubtaskIndex = activeSubtaskIndex + 1;
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
int subtaskSize = myTask.getLastSubtaskIndex() + 1;
|
||||
String resultMessage = !hasMoreSubtasks ? message : "Subtask " + visibleSubtaskIndex + "/" + subtaskSize + " solved";
|
||||
StudyCheckUtils.showTestResultPopUp(resultMessage, MessageType.INFO.getPopupBackground(), myProject);
|
||||
if (hasMoreSubtasks) {
|
||||
int nextSubtaskIndex = activeSubtaskIndex + 1;
|
||||
StudySubtaskUtils.switchStep(myProject, myTask, nextSubtaskIndex);
|
||||
rememberAnswers(nextSubtaskIndex, myTask);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void rememberAnswers(int nextSubtaskIndex, @NotNull TaskWithSubtasks task) {
|
||||
VirtualFile taskDir = task.getTaskDir(myProject);
|
||||
if (taskDir == null) {
|
||||
return;
|
||||
}
|
||||
VirtualFile srcDir = taskDir.findChild(EduNames.SRC);
|
||||
if (srcDir != null) {
|
||||
taskDir = srcDir;
|
||||
}
|
||||
for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
|
||||
TaskFile taskFile = entry.getValue();
|
||||
VirtualFile virtualFile = taskDir.findFileByRelativePath(entry.getKey());
|
||||
if (virtualFile == null) {
|
||||
continue;
|
||||
}
|
||||
Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
|
||||
if (document == null) {
|
||||
continue;
|
||||
}
|
||||
for (AnswerPlaceholder placeholder : taskFile.getActivePlaceholders()) {
|
||||
if (placeholder.getSubtaskInfos().containsKey(nextSubtaskIndex - 1)) {
|
||||
int offset = placeholder.getOffset();
|
||||
String answer = document.getText(TextRange.create(offset, offset + placeholder.getRealLength()));
|
||||
placeholder.getSubtaskInfos().get(nextSubtaskIndex - 1).setAnswer(answer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,6 @@ public class EduNames {
|
||||
public static final String WINDOWS_POSTFIX = "_windows";
|
||||
public static final String ANSWERS_POSTFIX = "_answers";
|
||||
public static final String USER_TESTS = "userTests";
|
||||
public static final String TESTS_FILE = "tests.py";
|
||||
public static final String TEST_HELPER = "test_helper.py";
|
||||
|
||||
public static final String COURSE_META_FILE = "course.json";
|
||||
|
||||
@@ -44,13 +44,19 @@ public class Course {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* exclude service lesson containing additional files for the course. Returns lessons copy.
|
||||
*/
|
||||
public List<Lesson> getLessons() {
|
||||
return getLessons(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns service lesson as well. Meant to be used in project generation/serialization
|
||||
*/
|
||||
public List<Lesson> getLessons(boolean withAdditional) {
|
||||
return withAdditional ? lessons
|
||||
: lessons.stream().filter(lesson -> !EduNames.PYCHARM_ADDITIONAL.equals(lesson.getName())).collect(Collectors.toList());
|
||||
return withAdditional ? lessons : lessons.stream().filter(lesson -> !EduNames.PYCHARM_ADDITIONAL.equals(lesson.getName()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public void setLessons(List<Lesson> lessons) {
|
||||
|
||||
@@ -28,7 +28,7 @@ public class Lesson implements StudyItem {
|
||||
@Expose
|
||||
@SerializedName("task_list")
|
||||
@AbstractCollection(elementTypes = {
|
||||
Task.class,
|
||||
PyCharmTask.class,
|
||||
ChoiceTask.class,
|
||||
TheoryTask.class,
|
||||
CodeTask.class,
|
||||
|
||||
+35
-10
@@ -2,9 +2,15 @@ package com.jetbrains.edu.learning.courseFormat;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.intellij.openapi.progress.EmptyProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.Task.Backgroundable;
|
||||
import com.jetbrains.edu.learning.core.EduNames;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
|
||||
import com.jetbrains.edu.learning.stepic.EduStepicNames;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
@@ -12,12 +18,14 @@ import java.util.List;
|
||||
|
||||
public class RemoteCourse extends Course {
|
||||
//course type in format "pycharm<version> <language>"
|
||||
@SerializedName("course_format") private String myType = "pycharm" + EduStepicConnector.CURRENT_VERSION + " Python";
|
||||
@SerializedName("course_format") private String myType =
|
||||
String.format("%s%d %s", EduStepicNames.PYCHARM_PREFIX, EduStepicConnector.CURRENT_VERSION, getLanguageID());
|
||||
@SerializedName("is_idea_compatible") private boolean isCompatible = true;
|
||||
List<Integer> sections;
|
||||
List<Integer> instructors = new ArrayList<>();
|
||||
@Expose private int id;
|
||||
@Expose @SerializedName("update_date") private Date myUpdateDate;
|
||||
private Boolean isUpToDate = true;
|
||||
@Expose private boolean isAdaptive = false;
|
||||
@SerializedName("is_public") boolean isPublic;
|
||||
|
||||
@@ -25,6 +33,11 @@ public class RemoteCourse extends Course {
|
||||
return myType;
|
||||
}
|
||||
|
||||
public void setLanguage(@NotNull final String language) {
|
||||
super.setLanguage(language);
|
||||
updateType(language);
|
||||
}
|
||||
|
||||
public List<Integer> getSections() {
|
||||
return sections;
|
||||
}
|
||||
@@ -45,14 +58,24 @@ public class RemoteCourse extends Course {
|
||||
public boolean isUpToDate() {
|
||||
if (id == 0) return true;
|
||||
if (!EduNames.STUDY.equals(courseMode)) return true;
|
||||
final Date date = EduStepicConnector.getCourseUpdateDate(id);
|
||||
if (date == null) return true;
|
||||
if (myUpdateDate == null) return true;
|
||||
if (date.after(myUpdateDate)) return false;
|
||||
for (Lesson lesson : lessons) {
|
||||
if (!lesson.isUpToDate()) return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
ProgressManager.getInstance().runProcessWithProgressAsynchronously(new Backgroundable(null, "Updating Course") {
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
final Date date = EduStepicConnector.getCourseUpdateDate(id);
|
||||
if (date == null) return;
|
||||
if (date.after(myUpdateDate)) {
|
||||
isUpToDate = false;
|
||||
}
|
||||
for (Lesson lesson : lessons) {
|
||||
if (!lesson.isUpToDate()) {
|
||||
isUpToDate = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, new EmptyProgressIndicator());
|
||||
|
||||
return isUpToDate;
|
||||
}
|
||||
|
||||
public void setUpdated() {
|
||||
@@ -91,8 +114,10 @@ public class RemoteCourse extends Course {
|
||||
|
||||
public void copyCourseParameters(RemoteCourse course) {
|
||||
setName(course.getName());
|
||||
|
||||
setUpdateDate(course.getUpdateDate());
|
||||
}
|
||||
|
||||
private void updateType(String language) {
|
||||
myType = String.format("%s%d %s", EduStepicNames.PYCHARM_PREFIX, EduStepicConnector.CURRENT_VERSION, language);
|
||||
}
|
||||
}
|
||||
|
||||
+23
@@ -2,6 +2,11 @@ package com.jetbrains.edu.learning.courseFormat.tasks;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.jetbrains.edu.learning.StudyUtils;
|
||||
import com.jetbrains.edu.learning.checker.StudyTaskChecker;
|
||||
import com.jetbrains.edu.learning.editor.StudyChoiceVariantsPanel;
|
||||
import com.jetbrains.edu.learning.ui.StudyToolWindow;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -48,4 +53,22 @@ public class ChoiceTask extends Task {
|
||||
public String getTaskType() {
|
||||
return "choice";
|
||||
}
|
||||
|
||||
@Override
|
||||
public StudyTaskChecker getChecker(@NotNull Project project) {
|
||||
return new StudyTaskChecker<ChoiceTask>(this, project) {
|
||||
@Override
|
||||
public void onTaskFailed(@NotNull String message) {
|
||||
super.onTaskFailed(message);
|
||||
repaintChoicePanel(project, myTask);
|
||||
}
|
||||
|
||||
private void repaintChoicePanel(@NotNull Project project, @NotNull ChoiceTask task) {
|
||||
final StudyToolWindow toolWindow = StudyUtils.getStudyToolWindow(project);
|
||||
if (toolWindow != null) {
|
||||
toolWindow.setBottomComponent(new StudyChoiceVariantsPanel(task));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+14
@@ -1,5 +1,8 @@
|
||||
package com.jetbrains.edu.learning.courseFormat.tasks;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.jetbrains.edu.learning.checker.StudyCheckUtils;
|
||||
import com.jetbrains.edu.learning.checker.StudyTaskChecker;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class CodeTask extends Task {
|
||||
@@ -14,4 +17,15 @@ public class CodeTask extends Task {
|
||||
public String getTaskType() {
|
||||
return "code";
|
||||
}
|
||||
|
||||
@Override
|
||||
public StudyTaskChecker getChecker(@NotNull Project project) {
|
||||
return new StudyTaskChecker<CodeTask>(this, project) {
|
||||
@Override
|
||||
public void onTaskFailed(@NotNull String message) {
|
||||
super.onTaskFailed("Wrong solution");
|
||||
StudyCheckUtils.showTestResultsToolWindow(myProject, message);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.jetbrains.edu.learning.courseFormat.tasks;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Original PyCharm Edu tasks with local tests and answer placeholders
|
||||
*/
|
||||
public class PyCharmTask extends Task {
|
||||
|
||||
public PyCharmTask() {
|
||||
}
|
||||
|
||||
public PyCharmTask(@NotNull String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTaskType() {
|
||||
return "pycharm";
|
||||
}
|
||||
}
|
||||
+12
-4
@@ -2,12 +2,15 @@ package com.jetbrains.edu.learning.courseFormat.tasks;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.xmlb.XmlSerializer;
|
||||
import com.intellij.util.xmlb.annotations.Transient;
|
||||
import com.jetbrains.edu.learning.checker.StudyTaskChecker;
|
||||
import com.jetbrains.edu.learning.EduPluginConfigurator;
|
||||
import com.jetbrains.edu.learning.core.EduNames;
|
||||
import com.jetbrains.edu.learning.courseFormat.*;
|
||||
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
|
||||
@@ -27,7 +30,7 @@ import java.util.Map;
|
||||
* - go to Lesson and update elementTypes in taskList AbstractCollection. Needed for proper xml serialization
|
||||
* - Update TaskSerializer and TaskDeserializer in StudySerializationUtil to handle json serialization
|
||||
*/
|
||||
public class Task implements StudyItem {
|
||||
public abstract class Task implements StudyItem {
|
||||
@Expose private String name;
|
||||
|
||||
// index is visible to user number of task from 1 to task number
|
||||
@@ -167,9 +170,12 @@ public class Task implements StudyItem {
|
||||
|
||||
@NotNull
|
||||
public String getTestsText(@NotNull final Project project) {
|
||||
final Course course = getLesson().getCourse();
|
||||
final Language language = course.getLanguageById();
|
||||
final EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(language);
|
||||
final VirtualFile taskDir = getTaskDir(project);
|
||||
if (taskDir != null) {
|
||||
final VirtualFile file = taskDir.findChild(EduNames.TESTS_FILE);
|
||||
final VirtualFile file = taskDir.findChild(configurator.getTestFileName());
|
||||
if (file == null) return "";
|
||||
final Document document = FileDocumentManager.getInstance().getDocument(file);
|
||||
if (document != null) {
|
||||
@@ -263,7 +269,9 @@ public class Task implements StudyItem {
|
||||
}
|
||||
|
||||
// used in json serialization/deserialization
|
||||
public String getTaskType() {
|
||||
return "pycharm";
|
||||
public abstract String getTaskType();
|
||||
|
||||
public StudyTaskChecker getChecker(@NotNull Project project) {
|
||||
return new StudyTaskChecker<>(this, project);
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -2,6 +2,9 @@ package com.jetbrains.edu.learning.courseFormat.tasks;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.jetbrains.edu.learning.checker.StudyTaskChecker;
|
||||
import com.jetbrains.edu.learning.checker.TaskWithSubtasksChecker;
|
||||
import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
|
||||
import com.jetbrains.edu.learning.courseFormat.StudyStatus;
|
||||
import com.jetbrains.edu.learning.courseFormat.TaskFile;
|
||||
@@ -58,4 +61,9 @@ public class TaskWithSubtasks extends Task {
|
||||
public String getTaskType() {
|
||||
return "subtasks";
|
||||
}
|
||||
|
||||
@Override
|
||||
public StudyTaskChecker getChecker(@NotNull Project project) {
|
||||
return new TaskWithSubtasksChecker(this, project);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -73,7 +73,7 @@ public class StudyGenerator {
|
||||
createChildFile(taskDir, name, taskFile.text);
|
||||
}
|
||||
|
||||
public static void createDescriptions(VirtualFile taskDir, Task task) throws IOException {
|
||||
private static void createDescriptions(VirtualFile taskDir, Task task) throws IOException {
|
||||
final Map<String, String> texts = task.getTaskTexts();
|
||||
for (Map.Entry<String, String> entry : texts.entrySet()) {
|
||||
final String name = entry.getKey();
|
||||
@@ -82,7 +82,7 @@ public class StudyGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
public static void createTestFiles(VirtualFile taskDir, Task task) throws IOException {
|
||||
private static void createTestFiles(VirtualFile taskDir, Task task) throws IOException {
|
||||
final Map<String, String> tests = task.getTestsText();
|
||||
for (Map.Entry<String, String> entry : tests.entrySet()) {
|
||||
final String name = entry.getKey();
|
||||
|
||||
+1
-1
@@ -144,7 +144,7 @@ public class StudyProjectGenerator {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<Course> getCoursesUnderProgress(boolean force, @NotNull final String progressTitle, @NotNull final Project project) {
|
||||
public List<Course> getCoursesUnderProgress(boolean force, @NotNull final String progressTitle, @Nullable final Project project) {
|
||||
try {
|
||||
return ProgressManager.getInstance()
|
||||
.runProcessWithProgressSynchronously(() -> {
|
||||
|
||||
+27
-1
@@ -3,16 +3,35 @@ package com.jetbrains.edu.learning.intellij;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.ide.util.projectWizard.WizardContext;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
import com.intellij.platform.ProjectTemplate;
|
||||
import com.intellij.platform.ProjectTemplatesFactory;
|
||||
import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
|
||||
import com.jetbrains.edu.learning.intellij.stepik.EduRemoteCourseTemplate;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings("unused") //used in other educational plugins that are stored in separate repository
|
||||
public class EduIntelliJTemplateFactory extends ProjectTemplatesFactory {
|
||||
private static final String GROUP_NAME = "Education";
|
||||
private static List<Course> ourRemoteCourses;
|
||||
|
||||
static {
|
||||
ProgressManager.getInstance().run(new Task.Backgroundable(null, "Updating Course") {
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
ourRemoteCourses = new StudyProjectGenerator().getCourses(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
@@ -23,7 +42,14 @@ public class EduIntelliJTemplateFactory extends ProjectTemplatesFactory {
|
||||
@NotNull
|
||||
@Override
|
||||
public ProjectTemplate[] createTemplates(@Nullable String group, WizardContext context) {
|
||||
return ApplicationManager.getApplication().getExtensions(EduIntelliJProjectTemplate.EP_NAME);
|
||||
final ArrayList<EduIntelliJProjectTemplate> templates = new ArrayList<>();
|
||||
if (ourRemoteCourses != null) {
|
||||
for (Course course : ourRemoteCourses) {
|
||||
templates.add(new EduRemoteCourseTemplate(course));
|
||||
}
|
||||
}
|
||||
Collections.addAll(templates, ApplicationManager.getApplication().getExtensions(EduIntelliJProjectTemplate.EP_NAME));
|
||||
return templates.toArray(new ProjectTemplate[templates.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package com.jetbrains.edu.learning.intellij.stepik;
|
||||
|
||||
import com.intellij.ide.util.projectWizard.JavaModuleBuilder;
|
||||
import com.intellij.ide.util.projectWizard.ModuleWizardStep;
|
||||
import com.intellij.ide.util.projectWizard.ProjectWizardStepFactory;
|
||||
import com.intellij.ide.util.projectWizard.SettingsStep;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.module.ModifiableModuleModel;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleWithNameAlreadyExists;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Conditions;
|
||||
import com.intellij.openapi.util.InvalidDataException;
|
||||
import com.jetbrains.edu.learning.EduPluginConfigurator;
|
||||
import com.jetbrains.edu.learning.StudyTaskManager;
|
||||
import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.intellij.generation.EduProjectGenerator;
|
||||
import org.jdom.JDOMException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class EduRemoteCourseModuleBuilder extends JavaModuleBuilder {
|
||||
private static final Logger LOG = Logger.getInstance(EduRemoteCourseModuleBuilder.class);
|
||||
private final Course myCourse;
|
||||
private EduProjectGenerator myGenerator = new EduProjectGenerator();
|
||||
|
||||
public EduRemoteCourseModuleBuilder(Course course) {
|
||||
myCourse = course;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Module commitModule(@NotNull Project project, @Nullable ModifiableModuleModel model) {
|
||||
Module module = super.commitModule(project, model);
|
||||
if (module == null) {
|
||||
return null;
|
||||
}
|
||||
String languageName = myCourse.getLanguageID();
|
||||
Language language = Language.findLanguageByID(languageName);
|
||||
if (language == null) {
|
||||
return module;
|
||||
}
|
||||
EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(language);
|
||||
if (configurator == null) {
|
||||
return module;
|
||||
}
|
||||
configurator.configureModule(module);
|
||||
return module;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getBuilderId() {
|
||||
return "remote.course.builder." + myCourse.getName();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ModuleWizardStep modifySettingsStep(@NotNull SettingsStep settingsStep) {
|
||||
return ProjectWizardStepFactory.getInstance().createJavaSettingsStep(settingsStep, this, Conditions.alwaysTrue());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Module createModule(@NotNull ModifiableModuleModel moduleModel)
|
||||
throws InvalidDataException, IOException, ModuleWithNameAlreadyExists, JDOMException, ConfigurationException {
|
||||
Module baseModule = super.createModule(moduleModel);
|
||||
String languageName = myCourse.getLanguageID();
|
||||
Language language = Language.findLanguageByID(languageName);
|
||||
if (language != null) {
|
||||
EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(language);
|
||||
if (configurator != null) {
|
||||
Project project = baseModule.getProject();
|
||||
myGenerator.setSelectedCourse(myCourse);
|
||||
myGenerator.generateProject(project, project.getBaseDir());
|
||||
Course course = StudyTaskManager.getInstance(project).getCourse();
|
||||
if (course == null) {
|
||||
LOG.info("failed to generate course");
|
||||
return baseModule;
|
||||
}
|
||||
configurator.createCourseModuleContent(moduleModel, project, course, getModuleFileDirectory());
|
||||
}
|
||||
}
|
||||
return baseModule;
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.jetbrains.edu.learning.intellij.stepik;
|
||||
|
||||
import com.intellij.ide.util.projectWizard.AbstractModuleBuilder;
|
||||
import com.intellij.openapi.ui.ValidationInfo;
|
||||
import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.intellij.EduIntelliJProjectTemplate;
|
||||
import icons.EducationalCoreIcons;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class EduRemoteCourseTemplate implements EduIntelliJProjectTemplate {
|
||||
private final Course myCourse;
|
||||
|
||||
public EduRemoteCourseTemplate(Course course) {
|
||||
myCourse = course;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return myCourse.getName();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return myCourse.getDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return EducationalCoreIcons.Stepik;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public AbstractModuleBuilder createModuleBuilder() {
|
||||
return new EduRemoteCourseModuleBuilder(myCourse);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ValidationInfo validateSettings() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+31
-69
@@ -7,20 +7,19 @@ import com.intellij.ide.projectView.ProjectView;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
import com.jetbrains.edu.learning.StudySettings;
|
||||
import com.jetbrains.edu.learning.StudyTaskManager;
|
||||
import com.jetbrains.edu.learning.StudyUtils;
|
||||
import com.jetbrains.edu.learning.checker.StudyExecutor;
|
||||
import com.jetbrains.edu.learning.core.EduNames;
|
||||
import com.jetbrains.edu.learning.courseFormat.*;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.ChoiceTask;
|
||||
@@ -28,7 +27,6 @@ import com.jetbrains.edu.learning.courseFormat.tasks.CodeTask;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.TheoryTask;
|
||||
import com.jetbrains.edu.learning.courseGeneration.StudyGenerator;
|
||||
import com.jetbrains.edu.learning.editor.StudyEditor;
|
||||
import com.jetbrains.edu.learning.navigation.StudyNavigator;
|
||||
import com.jetbrains.edu.learning.ui.StudyToolWindow;
|
||||
import org.apache.http.HttpEntity;
|
||||
@@ -49,7 +47,10 @@ import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.jetbrains.edu.learning.stepic.EduStepicConnector.getStep;
|
||||
@@ -146,7 +147,7 @@ public class EduAdaptiveStepicConnector {
|
||||
else if (stepType.equals(CHOICE_TYPE_TEXT)) {
|
||||
return getChoiceTaskFromStep(name, step.block, stepId, userId);
|
||||
}
|
||||
else if (stepType.startsWith(EduStepicConnector.PYCHARM_PREFIX)) {
|
||||
else if (stepType.startsWith(EduStepicNames.PYCHARM_PREFIX)) {
|
||||
return EduStepicConnector.createTask(stepId);
|
||||
}
|
||||
else if (stepType.equals(TEXT_STEP_TYPE)) {
|
||||
@@ -280,10 +281,15 @@ public class EduAdaptiveStepicConnector {
|
||||
}
|
||||
}
|
||||
|
||||
public static void addNextRecommendedTask(@NotNull Project project, @NotNull ProgressIndicator indicator, int reactionToPost) {
|
||||
final StudyEditor editor = StudyUtils.getSelectedStudyEditor(project);
|
||||
public static void addNextRecommendedTask(@NotNull Project project,
|
||||
@NotNull Lesson lesson,
|
||||
@NotNull ProgressIndicator indicator,
|
||||
int reactionToPost) {
|
||||
final Course course = StudyTaskManager.getInstance(project).getCourse();
|
||||
if (course == null || editor == null || editor.getTaskFile() == null || !(course instanceof RemoteCourse)) {
|
||||
if (!(course instanceof RemoteCourse)) {
|
||||
LOG.warn("Course is in incorrect state");
|
||||
ApplicationManager.getApplication().invokeLater(() -> StudyUtils.showErrorPopupOnToolbar(project,
|
||||
"Can't get next recommendation: course is broken"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -296,7 +302,6 @@ public class EduAdaptiveStepicConnector {
|
||||
return;
|
||||
}
|
||||
|
||||
final Lesson lesson = editor.getTaskFile().getTask().getLesson();
|
||||
final boolean reactionPosted = postRecommendationReaction(String.valueOf(lesson.getId()), String.valueOf(user.getId()), reactionToPost);
|
||||
if (!reactionPosted) {
|
||||
LOG.warn("Recommendation reaction wasn't posted");
|
||||
@@ -315,10 +320,10 @@ public class EduAdaptiveStepicConnector {
|
||||
task.initTask(lesson, false);
|
||||
boolean replaceCurrentTask = reactionToPost == TOO_HARD_RECOMMENDATION_REACTION || reactionToPost == TOO_BORING_RECOMMENDATION_REACTION;
|
||||
if (replaceCurrentTask) {
|
||||
replaceCurrentTask(project, editor, task);
|
||||
replaceCurrentTask(project, task, lesson);
|
||||
}
|
||||
else {
|
||||
addAsNextTask(project, editor, task);
|
||||
addAsNextTask(project, task, lesson);
|
||||
}
|
||||
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
@@ -328,11 +333,10 @@ public class EduAdaptiveStepicConnector {
|
||||
});
|
||||
}
|
||||
|
||||
private static void addAsNextTask(@NotNull Project project, @NotNull StudyEditor editor, @NotNull Task task) {
|
||||
private static void addAsNextTask(@NotNull Project project, @NotNull Task task, @NotNull Lesson lesson) {
|
||||
Course course = StudyTaskManager.getInstance(project).getCourse();
|
||||
assert course != null;
|
||||
|
||||
final Lesson lesson = editor.getTaskFile().getTask().getLesson();
|
||||
lesson.addTask(task);
|
||||
task.setIndex(lesson.getTaskList().size());
|
||||
lesson.initLesson(course, true);
|
||||
@@ -357,19 +361,15 @@ public class EduAdaptiveStepicConnector {
|
||||
}
|
||||
}
|
||||
|
||||
private static void replaceCurrentTask(@NotNull Project project, @NotNull StudyEditor editor, @NotNull Task task) {
|
||||
private static void replaceCurrentTask(@NotNull Project project, @NotNull Task task, @NotNull Lesson lesson) {
|
||||
Course course = StudyTaskManager.getInstance(project).getCourse();
|
||||
assert course != null;
|
||||
|
||||
final Lesson lesson = editor.getTaskFile().getTask().getLesson();
|
||||
int taskIndex = lesson.getTaskList().size();
|
||||
|
||||
task.setLesson(lesson);
|
||||
task.setIndex(taskIndex);
|
||||
lesson.getTaskList().set(taskIndex - 1, task);
|
||||
|
||||
copyTaskFileParameters(editor, task);
|
||||
|
||||
final String lessonName = EduNames.LESSON + lesson.getIndex();
|
||||
updateProjectFiles(project, task, lessonName);
|
||||
setToolWindowText(project, task);
|
||||
@@ -378,12 +378,10 @@ public class EduAdaptiveStepicConnector {
|
||||
private static void updateProjectFiles(@NotNull Project project, @NotNull Task task, @NotNull String lessonName) {
|
||||
final VirtualFile lessonDir = project.getBaseDir().findChild(lessonName);
|
||||
if (lessonDir != null) {
|
||||
final VirtualFile taskResourceRoot = lessonDir.findChild(EduNames.TASK + task.getIndex());
|
||||
ApplicationManager.getApplication().invokeLater(() -> ApplicationManager.getApplication().runWriteAction(() -> {
|
||||
try {
|
||||
removeOldProjectFiles(project, taskResourceRoot);
|
||||
StudyGenerator.createTestFiles(taskResourceRoot, task);
|
||||
StudyGenerator.createDescriptions(taskResourceRoot, task);
|
||||
removeOldProjectFiles(lessonDir, task.getIndex());
|
||||
StudyGenerator.createTask(task, lessonDir);
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.warn(e.getMessage());
|
||||
@@ -392,46 +390,14 @@ public class EduAdaptiveStepicConnector {
|
||||
}
|
||||
}
|
||||
|
||||
private static void removeOldProjectFiles(@NotNull Project project, @Nullable VirtualFile taskRecourseRoot) throws IOException {
|
||||
if (taskRecourseRoot == null) {
|
||||
private static void removeOldProjectFiles(@NotNull VirtualFile lessonDir, int taskIndex) throws IOException {
|
||||
final VirtualFile taskDir = lessonDir.findChild(EduNames.TASK + taskIndex);
|
||||
if (taskDir == null) {
|
||||
LOG.warn("Failed to update files for a new recommendation: task directory is null");
|
||||
return;
|
||||
}
|
||||
|
||||
VirtualFile[] children = VfsUtil.getChildren(taskRecourseRoot);
|
||||
for (VirtualFile file : children) {
|
||||
String name = file.getName();
|
||||
if (StudyUtils.isTestsFile(project, name) || StudyUtils.isTaskDescriptionFile(name)) {
|
||||
file.delete(taskRecourseRoot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void copyTaskFileParameters(@NotNull StudyEditor editor, @NotNull Task task) {
|
||||
final Map<String, TaskFile> taskFiles = task.getTaskFiles();
|
||||
if (taskFiles.size() == 1) {
|
||||
TaskFile newTaskFile = (TaskFile)taskFiles.values().toArray()[0];
|
||||
setTaskFileParameters(editor, task, newTaskFile);
|
||||
updateEditorText(editor, newTaskFile);
|
||||
}
|
||||
else {
|
||||
LOG.warn("Got task without unexpected number of task files: " + taskFiles.size());
|
||||
}
|
||||
}
|
||||
private static void updateEditorText(@NotNull StudyEditor editor, @NotNull TaskFile newTaskFile) {
|
||||
ApplicationManager.getApplication().invokeLater(() -> ApplicationManager.getApplication().runWriteAction(() -> {
|
||||
final Document document = editor.getEditor().getDocument();
|
||||
document.setText(newTaskFile.text);
|
||||
}));
|
||||
}
|
||||
|
||||
private static void setTaskFileParameters(@NotNull StudyEditor editor, @NotNull Task task, @NotNull TaskFile newTaskFile) {
|
||||
TaskFile currentTaskFile = editor.getTaskFile();
|
||||
currentTaskFile.text = newTaskFile.text;
|
||||
currentTaskFile.name = newTaskFile.name;
|
||||
currentTaskFile.setTask(task);
|
||||
task.getTaskFiles().clear();
|
||||
task.taskFiles.put(CODE_TASK_NAME, currentTaskFile);
|
||||
taskDir.delete(EduAdaptiveStepicConnector.class);
|
||||
}
|
||||
|
||||
private static void setToolWindowText(@NotNull Project project, @NotNull Task task) {
|
||||
@@ -489,7 +455,7 @@ public class EduAdaptiveStepicConnector {
|
||||
}
|
||||
}
|
||||
else {
|
||||
final String templateForTask = getCodeTemplateForTask(project, task, step.options.codeTemplates);
|
||||
final String templateForTask = getCodeTemplateForTask(project, step.options.codeTemplates);
|
||||
String text = templateForTask == null ? "# write your answer here \n" : templateForTask;
|
||||
createMockTaskFile(task, text);
|
||||
}
|
||||
@@ -497,10 +463,9 @@ public class EduAdaptiveStepicConnector {
|
||||
}
|
||||
|
||||
private static String getCodeTemplateForTask(@NotNull Project project,
|
||||
@NotNull Task task,
|
||||
@Nullable StepicWrappers.CodeTemplatesWrapper codeTemplates) {
|
||||
if (codeTemplates != null) {
|
||||
final String languageString = getLanguageString(task, project);
|
||||
final String languageString = getLanguageString(project);
|
||||
if (languageString != null) {
|
||||
return codeTemplates.getTemplateForLanguage(languageString);
|
||||
}
|
||||
@@ -656,12 +621,12 @@ public class EduAdaptiveStepicConnector {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getLanguageString(@NotNull Task task, @NotNull Project project) {
|
||||
private static String getLanguageString(@NotNull Project project) {
|
||||
final Language pythonLanguage = Language.findLanguageByID("Python");
|
||||
if (pythonLanguage != null) {
|
||||
final Sdk language = StudyExecutor.INSTANCE.forLanguage(pythonLanguage).findSdk(project);
|
||||
if (language != null) {
|
||||
final String versionString = language.getVersionString();
|
||||
Sdk sdk = ModuleRootManager.getInstance(ModuleManager.getInstance(project).getModules()[0]).getSdk();
|
||||
if (sdk != null) {
|
||||
final String versionString = sdk.getVersionString();
|
||||
if (versionString != null) {
|
||||
final List<String> versionStringParts = StringUtil.split(versionString, " ");
|
||||
if (versionStringParts.size() == 2) {
|
||||
@@ -669,9 +634,6 @@ public class EduAdaptiveStepicConnector {
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
StudyUtils.showNoSdkNotification(task, project);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
+38
-13
@@ -2,16 +2,20 @@ package com.jetbrains.edu.learning.stepic;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.intellij.lang.LanguageExtensionPoint;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.jetbrains.edu.learning.EduPluginConfigurator;
|
||||
import com.jetbrains.edu.learning.StudySettings;
|
||||
import com.jetbrains.edu.learning.core.EduNames;
|
||||
import com.jetbrains.edu.learning.courseFormat.*;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.PyCharmTask;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.TaskWithSubtasks;
|
||||
import org.apache.http.HttpEntity;
|
||||
@@ -32,14 +36,15 @@ import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.*;
|
||||
|
||||
import static com.jetbrains.edu.learning.stepic.EduStepicNames.PYCHARM_PREFIX;
|
||||
|
||||
public class EduStepicConnector {
|
||||
private static final Logger LOG = Logger.getInstance(EduStepicConnector.class.getName());
|
||||
|
||||
public static final int CURRENT_VERSION = 2;
|
||||
//this prefix indicates that course can be opened by educational plugin
|
||||
public static final String PYCHARM_PREFIX = "pycharm";
|
||||
private static final String ADAPTIVE_NOTE =
|
||||
"\n\nInitially, the adaptive system may behave somewhat randomly, but the more problems you solve, the smarter it become!";
|
||||
"\n\nInitially, the adaptive system may behave somewhat randomly, but the more problems you solve, the smarter it becomes!";
|
||||
|
||||
private EduStepicConnector() {
|
||||
}
|
||||
@@ -147,6 +152,8 @@ public class EduStepicConnector {
|
||||
final List<RemoteCourse> courses = coursesContainer.courses;
|
||||
for (RemoteCourse info : courses) {
|
||||
if (!info.isAdaptive() && StringUtil.isEmptyOrSpaces(info.getType())) continue;
|
||||
setCourseLanguage(info);
|
||||
|
||||
if (canBeOpened(info)) {
|
||||
final ArrayList<StepicUser> authors = new ArrayList<>();
|
||||
for (Integer instructor : info.getInstructors()) {
|
||||
@@ -164,13 +171,34 @@ public class EduStepicConnector {
|
||||
}
|
||||
}
|
||||
|
||||
static boolean canBeOpened(RemoteCourse courseInfo) {
|
||||
if (courseInfo.isAdaptive()) {
|
||||
return true;
|
||||
private static void setCourseLanguage(RemoteCourse info) {
|
||||
if (info.isAdaptive()) {
|
||||
info.setLanguage("Python"); // adaptive courses available only in PyCharm now
|
||||
return;
|
||||
}
|
||||
String courseType = info.getType();
|
||||
final int separator = courseType.indexOf(" ");
|
||||
assert separator != -1;
|
||||
final String language = courseType.substring(separator + 1);
|
||||
info.setLanguage(language);
|
||||
}
|
||||
|
||||
static boolean canBeOpened(RemoteCourse courseInfo) {
|
||||
final ArrayList<String> supportedLanguages = new ArrayList<>();
|
||||
final LanguageExtensionPoint[] extensions = Extensions.getExtensions(EduPluginConfigurator.EP_NAME, null);
|
||||
for (LanguageExtensionPoint extension : extensions) {
|
||||
String languageId = extension.getKey();
|
||||
supportedLanguages.add(languageId);
|
||||
}
|
||||
|
||||
if (courseInfo.isAdaptive()) {
|
||||
return supportedLanguages.contains(courseInfo.getLanguageID());
|
||||
}
|
||||
|
||||
String courseType = courseInfo.getType();
|
||||
final List<String> typeLanguage = StringUtil.split(courseType, " ");
|
||||
String prefix = typeLanguage.get(0);
|
||||
if (!supportedLanguages.contains(courseInfo.getLanguageID())) return false;
|
||||
if (typeLanguage.size() < 2 || !prefix.startsWith(PYCHARM_PREFIX)) {
|
||||
return false;
|
||||
}
|
||||
@@ -188,14 +216,8 @@ public class EduStepicConnector {
|
||||
}
|
||||
}
|
||||
|
||||
public static RemoteCourse getCourse(@NotNull final Project project, @NotNull final RemoteCourse course) {
|
||||
final RemoteCourse remoteCourse = (RemoteCourse)course.copy();
|
||||
public static RemoteCourse getCourse(@NotNull final Project project, @NotNull final RemoteCourse remoteCourse) {
|
||||
if (!remoteCourse.isAdaptive()) {
|
||||
String courseType = remoteCourse.getType();
|
||||
final int separator = courseType.indexOf(" ");
|
||||
assert separator != -1;
|
||||
final String language = courseType.substring(separator + 1);
|
||||
remoteCourse.setLanguage(language);
|
||||
try {
|
||||
for (Integer section : remoteCourse.getSections()) {
|
||||
remoteCourse.addLessons(getLessons(section));
|
||||
@@ -267,7 +289,7 @@ public class EduStepicConnector {
|
||||
return null;
|
||||
}
|
||||
final int lastSubtaskIndex = block.options.lastSubtaskIndex;
|
||||
Task task = new Task();
|
||||
Task task = new PyCharmTask();
|
||||
if (lastSubtaskIndex != 0) {
|
||||
task = createTaskWithSubtasks(lastSubtaskIndex);
|
||||
}
|
||||
@@ -301,6 +323,9 @@ public class EduStepicConnector {
|
||||
final List<AnswerPlaceholder> placeholders = file.getAnswerPlaceholders();
|
||||
for (AnswerPlaceholder placeholder : placeholders) {
|
||||
final AnswerPlaceholderSubtaskInfo info = placeholder.getActiveSubtaskInfo();
|
||||
if (info == null) {
|
||||
continue;
|
||||
}
|
||||
final int offset = placeholder.getOffset();
|
||||
final int length = placeholder.getLength();
|
||||
if (fileText.length() > offset + length) {
|
||||
|
||||
@@ -21,4 +21,6 @@ public class EduStepicNames {
|
||||
public static final String SECTIONS = "/sections/";
|
||||
public static final String ENROLLMENTS = "/enrollments";
|
||||
public static final String STEPIC_REGISTRATION_LINK = "https://stepic.org/registration?next=%2Fexplore%2Fcourses";
|
||||
|
||||
public static final String PYCHARM_PREFIX = "pycharm";
|
||||
}
|
||||
|
||||
+2
-1
@@ -148,7 +148,8 @@ public class StepicAdaptiveReactionsPanel extends JPanel {
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
StepicAdaptiveReactionsPanel.this.setEnabledRecursive(false);
|
||||
ApplicationManager.getApplication().invokeLater(()->setBackground(UIUtil.getLabelBackground()));
|
||||
EduAdaptiveStepicConnector.addNextRecommendedTask(StepicAdaptiveReactionsPanel.this.myProject, indicator, myReaction);
|
||||
EduAdaptiveStepicConnector.addNextRecommendedTask(StepicAdaptiveReactionsPanel.this.myProject, task.getLesson(), indicator,
|
||||
myReaction);
|
||||
StepicAdaptiveReactionsPanel.this.setEnabledRecursive(true);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,13 +2,14 @@ package com.jetbrains.edu.learning.stepic;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.jetbrains.edu.learning.EduPluginConfigurator;
|
||||
import com.jetbrains.edu.learning.StudyUtils;
|
||||
import com.jetbrains.edu.learning.core.EduNames;
|
||||
import com.jetbrains.edu.learning.core.EduUtils;
|
||||
import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.courseFormat.Lesson;
|
||||
@@ -136,13 +137,16 @@ public class StepicWrappers {
|
||||
}
|
||||
|
||||
private static List<VirtualFile> getTestFiles(@NotNull Task task, @NotNull Project project) {
|
||||
final Course course = task.getLesson().getCourse();
|
||||
final Language language = course.getLanguageById();
|
||||
final EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(language);
|
||||
List<VirtualFile> testFiles = new ArrayList<>();
|
||||
VirtualFile taskDir = task.getTaskDir(project);
|
||||
if (taskDir == null) {
|
||||
return testFiles;
|
||||
}
|
||||
if (!(task instanceof TaskWithSubtasks)) {
|
||||
VirtualFile testFile = taskDir.findChild(EduNames.TESTS_FILE);
|
||||
VirtualFile testFile = taskDir.findChild(configurator.getTestFileName());
|
||||
testFiles.add(testFile);
|
||||
return testFiles;
|
||||
}
|
||||
@@ -190,6 +194,7 @@ public class StepicWrappers {
|
||||
public CourseWrapper(Course course) {
|
||||
this.course = new RemoteCourse();
|
||||
this.course.setName(course.getName());
|
||||
this.course.setLanguage(course.getLanguageID());
|
||||
this.course.setDescription(course.getDescription());
|
||||
this.course.setAuthors(course.getAuthors());
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ package com.jetbrains.edu.learning.twitter;
|
||||
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.jetbrains.edu.learning.StudyCheckListener;
|
||||
import com.jetbrains.edu.learning.checker.StudyCheckListener;
|
||||
import com.jetbrains.edu.learning.StudyTwitterPluginConfigurator;
|
||||
import com.jetbrains.edu.learning.courseFormat.StudyStatus;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
|
||||
@@ -22,6 +22,7 @@ public class EducationalCoreIcons {
|
||||
public static final Icon ResetTaskFile = load("/icons/com/jetbrains/edu/learning/ResetTaskFile.png"); // 16x16
|
||||
public static final Icon Sandbox = load("/icons/com/jetbrains/edu/learning/Sandbox.png"); // 16x16
|
||||
public static final Icon ShowHint = load("/icons/com/jetbrains/edu/learning/showHint.png"); // 16x16
|
||||
public static final Icon Stepik = load("/icons/com/jetbrains/edu/learning/Stepik.png"); // 16x16
|
||||
public static final Icon Task = load("/icons/com/jetbrains/edu/learning/Task.png"); // 16x16
|
||||
public static final Icon TaskCompl = load("/icons/com/jetbrains/edu/learning/TaskCompl.png"); // 16x16
|
||||
public static final Icon TaskDescription = load("/icons/com/jetbrains/edu/learning/TaskDescription.png"); // 13x13
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.intellij.testFramework.fixtures.CodeInsightFixtureTestCase;
|
||||
import com.jetbrains.edu.learning.StudyTaskManager;
|
||||
import com.jetbrains.edu.learning.StudyUtils;
|
||||
import com.jetbrains.edu.learning.courseFormat.*;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.PyCharmTask;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -85,7 +86,7 @@ public abstract class CCTestCase extends CodeInsightFixtureTestCase {
|
||||
|
||||
Lesson lesson = new Lesson();
|
||||
lesson.setName("lesson1");
|
||||
Task task = new Task();
|
||||
Task task = new PyCharmTask();
|
||||
task.setName("task1");
|
||||
task.setIndex(1);
|
||||
lesson.addTask(task);
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
<orderEntry type="library" name="gson" level="project" />
|
||||
<orderEntry type="module" module-name="educational-python" />
|
||||
<orderEntry type="module" module-name="python-community-configure" />
|
||||
<orderEntry type="module" module-name="course-creator" />
|
||||
<orderEntry type="module" module-name="educational-core" />
|
||||
<orderEntry type="module" module-name="python-community-ide" />
|
||||
</component>
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
<importResolver implementation="com.jetbrains.edu.learning.PyStudyImportResolver"/>
|
||||
</extensions>
|
||||
<extensions defaultExtensionNs="Edu">
|
||||
<StudyExecutor implementationClass="com.jetbrains.edu.learning.PyStudyExecutor" language="Python"/>
|
||||
<pluginConfigurator implementationClass="com.jetbrains.edu.learning.PyEduPluginConfigurator" language="Python"/>
|
||||
<eduProjectCreator implementation="com.jetbrains.edu.learning.builtInServer.EduPythonProjectCreator"/>
|
||||
</extensions>
|
||||
|
||||
+5
-5
@@ -77,10 +77,10 @@ public class PyEduPluginConfigurator implements EduPluginConfigurator {
|
||||
@Override
|
||||
public boolean isTestFile(VirtualFile file) {
|
||||
String name = file.getName();
|
||||
if (EduNames.TESTS_FILE.equals(name)) {
|
||||
if (TESTS_PY.equals(name)) {
|
||||
return true;
|
||||
}
|
||||
return name.contains(FileUtil.getNameWithoutExtension(EduNames.TESTS_FILE)) && name.contains(EduNames.SUBTASK_MARKER);
|
||||
return name.contains(FileUtil.getNameWithoutExtension(TESTS_PY)) && name.contains(EduNames.SUBTASK_MARKER);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,7 +94,7 @@ public class PyEduPluginConfigurator implements EduPluginConfigurator {
|
||||
ApplicationManager.getApplication().runWriteAction(() -> {
|
||||
try {
|
||||
PsiDirectory taskPsiDir = PsiManager.getInstance(project).findDirectory(taskDir);
|
||||
FileTemplate testsTemplate = FileTemplateManager.getInstance(project).getInternalTemplate(EduNames.TESTS_FILE);
|
||||
FileTemplate testsTemplate = FileTemplateManager.getInstance(project).getInternalTemplate(TESTS_PY);
|
||||
if (taskPsiDir == null || testsTemplate == null) {
|
||||
return;
|
||||
}
|
||||
@@ -108,10 +108,10 @@ public class PyEduPluginConfigurator implements EduPluginConfigurator {
|
||||
|
||||
@NotNull
|
||||
public static String getSubtaskTestsFileName(int index) {
|
||||
return index == 0 ? EduNames.TESTS_FILE : FileUtil.getNameWithoutExtension(EduNames.TESTS_FILE) +
|
||||
return index == 0 ? TESTS_PY : FileUtil.getNameWithoutExtension(TESTS_PY) +
|
||||
EduNames.SUBTASK_MARKER +
|
||||
index + "." +
|
||||
FileUtilRt.getExtension(EduNames.TESTS_FILE);
|
||||
FileUtilRt.getExtension(TESTS_PY);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+3
-34
@@ -6,7 +6,6 @@ import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.wm.IdeFocusManager;
|
||||
import com.jetbrains.edu.learning.actions.StudyCheckAction;
|
||||
@@ -15,14 +14,10 @@ import com.jetbrains.edu.learning.checker.StudyCheckUtils;
|
||||
import com.jetbrains.edu.learning.checker.StudyTestRunner;
|
||||
import com.jetbrains.edu.learning.core.EduNames;
|
||||
import com.jetbrains.edu.learning.courseFormat.Course;
|
||||
import com.jetbrains.edu.learning.courseFormat.StudyStatus;
|
||||
import com.jetbrains.edu.learning.courseFormat.TaskFile;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.ChoiceTask;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
import com.jetbrains.edu.learning.editor.StudyChoiceVariantsPanel;
|
||||
import com.jetbrains.edu.learning.editor.StudyEditor;
|
||||
import com.jetbrains.edu.learning.statistics.EduUsagesCollector;
|
||||
import com.jetbrains.edu.learning.ui.StudyToolWindow;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -52,7 +47,7 @@ public class PyStudyCheckAction extends StudyCheckAction {
|
||||
ApplicationManager.getApplication().invokeLater(
|
||||
() -> IdeFocusManager.getInstance(project).requestFocus(studyState.getEditor().getComponent(), true));
|
||||
|
||||
final StudyTestRunner testRunner = StudyUtils.getTestRunner(task, taskDir);
|
||||
final StudyTestRunner testRunner = new PyStudyTestRunner(task, taskDir);
|
||||
Process testProcess = null;
|
||||
String commandLine = "";
|
||||
try {
|
||||
@@ -83,9 +78,9 @@ public class PyStudyCheckAction extends StudyCheckAction {
|
||||
return new StudyCheckTask(project, studyState, myCheckInProgress, testProcess, commandLine) {
|
||||
@Override
|
||||
protected void onTaskFailed(@NotNull String message) {
|
||||
super.onTaskFailed(message);
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
if (myTaskDir == null) return;
|
||||
myTask.setStatus(StudyStatus.Failed);
|
||||
for (Map.Entry<String, TaskFile> entry : myTask.getTaskFiles().entrySet()) {
|
||||
final String name = entry.getKey();
|
||||
final TaskFile taskFile = entry.getValue();
|
||||
@@ -99,38 +94,12 @@ public class PyStudyCheckAction extends StudyCheckAction {
|
||||
() -> StudyCheckUtils.runSmartTestProcess(myTaskDir, testRunner, name, taskFile, project)));
|
||||
}
|
||||
}
|
||||
final StudyToolWindow toolWindow = StudyUtils.getStudyToolWindow(project);
|
||||
if (toolWindow != null) {
|
||||
final Course course = StudyTaskManager.getInstance(project).getCourse();
|
||||
if (course != null) {
|
||||
if (course.isAdaptive()) {
|
||||
if (myTask instanceof ChoiceTask) {
|
||||
StudyCheckUtils.showTestResultPopUp("Wrong answer", MessageType.ERROR.getPopupBackground(), project);
|
||||
repaintChoicePanel(project, (ChoiceTask)myTask);
|
||||
}
|
||||
else {
|
||||
StudyCheckUtils.showTestResultPopUp("Wrong answer", MessageType.ERROR.getPopupBackground(), project);
|
||||
StudyCheckUtils.showTestResultsToolWindow(project, message, false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
StudyCheckUtils.showTestResultPopUp(message, MessageType.ERROR.getPopupBackground(), project);
|
||||
}
|
||||
}
|
||||
StudyCheckUtils.navigateToFailedPlaceholder(myStudyState, myTask, myTaskDir, project);
|
||||
}
|
||||
StudyCheckUtils.navigateToFailedPlaceholder(myStudyState, myTask, myTaskDir, project);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void repaintChoicePanel(@NotNull Project project, @NotNull ChoiceTask task) {
|
||||
final StudyToolWindow toolWindow = StudyUtils.getStudyToolWindow(project);
|
||||
if (toolWindow != null) {
|
||||
toolWindow.setBottomComponent(new StudyChoiceVariantsPanel(task));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static VirtualFile getTaskVirtualFile(@NotNull final StudyState studyState,
|
||||
@NotNull final Task task,
|
||||
|
||||
-96
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* 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.edu.learning;
|
||||
|
||||
import com.intellij.execution.RunContentExecutor;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.execution.process.ProcessHandler;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.options.ShowSettingsUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.ui.popup.Balloon;
|
||||
import com.intellij.openapi.ui.popup.BalloonBuilder;
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.jetbrains.edu.learning.courseFormat.tasks.Task;
|
||||
import com.jetbrains.edu.learning.checker.StudyExecutor;
|
||||
import com.jetbrains.edu.learning.checker.StudyTestRunner;
|
||||
import com.jetbrains.edu.learning.courseFormat.UserTest;
|
||||
import com.jetbrains.python.run.PythonTracebackFilter;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
public class PyStudyExecutor implements StudyExecutor {
|
||||
|
||||
@Override
|
||||
public Sdk findSdk(@NotNull final Project project) {
|
||||
return PythonSdkType.findPythonSdk(ModuleManager.getInstance(project).getModules()[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StudyTestRunner getTestRunner(@NotNull final Task task, @NotNull final VirtualFile taskDir) {
|
||||
return new PyStudyTestRunner(task, taskDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RunContentExecutor getExecutor(@NotNull final Project project, @NotNull final ProcessHandler handler) {
|
||||
return new RunContentExecutor(project, handler).withFilter(new PythonTracebackFilter(project));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCommandLineParameters(@NotNull final GeneralCommandLine cmd,
|
||||
@NotNull final Project project,
|
||||
@NotNull final String filePath,
|
||||
@NotNull final String sdkPath,
|
||||
@NotNull final Task currentTask) {
|
||||
final List<UserTest> userTests = StudyTaskManager.getInstance(project).getUserTests(currentTask);
|
||||
if (!userTests.isEmpty()) {
|
||||
EduPluginConfigurator configurator = EduPluginConfigurator.INSTANCE.forLanguage(currentTask.getLesson().getCourse().getLanguageById());
|
||||
if (configurator != null) {
|
||||
cmd.addParameter(new File(project.getBaseDir().getPath(), "user_tester.py").getPath());
|
||||
cmd.addParameter(sdkPath);
|
||||
cmd.addParameter(filePath);
|
||||
}
|
||||
}
|
||||
else {
|
||||
cmd.addParameter(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
public void showNoSdkNotification(@NotNull final Project project) {
|
||||
final String text = "<html>No Python interpreter configured for the project<br><a href=\"\">Configure interpreter</a></html>";
|
||||
final BalloonBuilder balloonBuilder = JBPopupFactory.getInstance().
|
||||
createHtmlTextBalloonBuilder(text, null,
|
||||
MessageType.WARNING.getPopupBackground(),
|
||||
event -> {
|
||||
if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
|
||||
ApplicationManager.getApplication()
|
||||
.invokeLater(
|
||||
() -> ShowSettingsUtil.getInstance().showSettingsDialog(project, "Project Interpreter"));
|
||||
}
|
||||
});
|
||||
balloonBuilder.setHideOnLinkClick(true);
|
||||
final Balloon balloon = balloonBuilder.createBalloon();
|
||||
StudyUtils.showCheckPopUp(project, balloon);
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@
|
||||
<orderEntry type="module" module-name="platform-main" />
|
||||
<orderEntry type="module" module-name="ShortcutPromoter" />
|
||||
<orderEntry type="module" module-name="educational-python" />
|
||||
<orderEntry type="module" module-name="course-creator" />
|
||||
<orderEntry type="module" module-name="Edu-Python" />
|
||||
<orderEntry type="module" module-name="hg4idea" />
|
||||
<orderEntry type="module" module-name="ipnb" />
|
||||
|
||||
@@ -218,13 +218,6 @@ def check_samples(samples=()):
|
||||
passed()
|
||||
|
||||
|
||||
def do_not_run_on_check():
|
||||
"""
|
||||
Do not execute task file on check
|
||||
"""
|
||||
print("#educational_plugin DO_NOT_RUN_ON_CHECK")
|
||||
|
||||
|
||||
def run_common_tests(error_text="Please, reload file and try again"):
|
||||
test_is_not_empty()
|
||||
test_answer_placeholders_text_deleted()
|
||||
|
||||
@@ -313,16 +313,17 @@ class _PyDevFrontEnd:
|
||||
version = release.__version__
|
||||
|
||||
def __init__(self, show_banner=True):
|
||||
|
||||
# Create and initialize our IPython instance.
|
||||
self.ipython = PyDevTerminalInteractiveShell.instance()
|
||||
if hasattr(PyDevTerminalInteractiveShell, '_instance') and PyDevTerminalInteractiveShell._instance is not None:
|
||||
self.ipython = PyDevTerminalInteractiveShell._instance
|
||||
else:
|
||||
self.ipython = PyDevTerminalInteractiveShell.instance()
|
||||
|
||||
if show_banner:
|
||||
# Display the IPython banner, this has version info and
|
||||
# help info
|
||||
self.ipython.show_banner()
|
||||
|
||||
|
||||
self._curr_exec_line = 0
|
||||
self._curr_exec_lines = []
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ from _pydevd_bundle.pydevd_comm import CMD_SET_BREAK, CMD_SET_NEXT_STATEMENT, CM
|
||||
from _pydevd_bundle.pydevd_custom_frames import CustomFramesContainer, custom_frames_container_init
|
||||
from _pydevd_bundle.pydevd_frame_utils import add_exception_to_frame
|
||||
from _pydevd_bundle.pydevd_kill_all_pydevd_threads import kill_all_pydev_threads
|
||||
from _pydevd_bundle.pydevd_trace_dispatch import trace_dispatch as _trace_dispatch
|
||||
from _pydevd_bundle.pydevd_trace_dispatch import trace_dispatch as _trace_dispatch, global_cache_skips, global_cache_frame_skips
|
||||
from _pydevd_frame_eval.pydevd_frame_eval_main import frame_eval_func, stop_frame_eval, set_use_code_extra
|
||||
from _pydevd_bundle.pydevd_utils import save_main_module
|
||||
from pydevd_concurrency_analyser.pydevd_concurrency_logger import ThreadingLogger, AsyncioLogger, send_message, cur_time
|
||||
@@ -76,6 +76,7 @@ connected = False
|
||||
bufferStdOutToServer = False
|
||||
bufferStdErrToServer = False
|
||||
remote = False
|
||||
inside_fork = False
|
||||
|
||||
file_system_encoding = getfilesystemencoding()
|
||||
|
||||
@@ -586,7 +587,6 @@ class PyDB:
|
||||
|
||||
|
||||
def consolidate_breakpoints(self, file, id_to_breakpoint, breakpoints):
|
||||
from _pydevd_bundle.pydevd_trace_dispatch import global_cache_skips, global_cache_frame_skips
|
||||
break_dict = {}
|
||||
for breakpoint_id, pybreakpoint in dict_iter_items(id_to_breakpoint):
|
||||
break_dict[pybreakpoint.line] = pybreakpoint
|
||||
@@ -1172,7 +1172,7 @@ def _locked_settrace(
|
||||
global bufferStdOutToServer
|
||||
global bufferStdErrToServer
|
||||
|
||||
if not connected :
|
||||
if not connected:
|
||||
pydevd_vm_type.setup_type()
|
||||
|
||||
if SetupHolder.setup is None:
|
||||
@@ -1220,6 +1220,11 @@ def _locked_settrace(
|
||||
while not debugger.ready_to_run:
|
||||
time.sleep(0.1) # busy wait until we receive run command
|
||||
|
||||
global inside_fork
|
||||
if frame_eval_func is not None and not inside_fork:
|
||||
# Disable frame evaluation for Remote Debug Server
|
||||
debugger.frame_eval_func = None
|
||||
|
||||
# note that we do that through pydevd_tracing.SetTrace so that the tracing
|
||||
# is not warned to the user!
|
||||
pydevd_tracing.SetTrace(debugger.trace_dispatch, debugger.frame_eval_func)
|
||||
@@ -1359,6 +1364,8 @@ def settrace_forked():
|
||||
if port is not None:
|
||||
global connected
|
||||
connected = False
|
||||
global inside_fork
|
||||
inside_fork = True
|
||||
|
||||
custom_frames_container_init()
|
||||
|
||||
|
||||
@@ -637,7 +637,19 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference
|
||||
|
||||
// include our own names
|
||||
final int underscores = PyUtil.getInitialUnderscores(element.getName());
|
||||
final CompletionVariantsProcessor processor = new CompletionVariantsProcessor(element);
|
||||
final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(element);
|
||||
final CompletionVariantsProcessor processor = new CompletionVariantsProcessor(element, e -> {
|
||||
if (builtinCache.isBuiltin(e)) {
|
||||
final String name = e instanceof PyElement ? ((PyElement)e).getName() : null;
|
||||
if (e instanceof PyImportElement) {
|
||||
return false;
|
||||
}
|
||||
if (name != null && PyUtil.getInitialUnderscores(name) == 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}, null);
|
||||
final ScopeOwner owner = realContext instanceof ScopeOwner ? (ScopeOwner)realContext : ScopeUtil.getScopeOwner(realContext);
|
||||
if (owner != null) {
|
||||
PyResolveUtil.scopeCrawlUp(processor, owner, null, null);
|
||||
@@ -648,7 +660,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference
|
||||
KeywordArgumentCompletionUtil.collectFunctionArgNames(element, ret, TypeEvalContext.codeCompletion(element.getProject(), element.getContainingFile()));
|
||||
|
||||
// include builtin names
|
||||
final PyFile builtinsFile = PyBuiltinCache.getInstance(element).getBuiltinsFile();
|
||||
final PyFile builtinsFile = builtinCache.getBuiltinsFile();
|
||||
if (builtinsFile != null) {
|
||||
PyResolveUtil.scopeCrawlUp(processor, builtinsFile, null, null);
|
||||
}
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ public class PythonBuiltinReferenceResolveProvider implements PyReferenceResolve
|
||||
|
||||
// ...as a builtin symbol
|
||||
final PyFile builtinsFile = builtinCache.getBuiltinsFile();
|
||||
if (builtinsFile != null && !PyUtil.isClassPrivateName(referencedName)) {
|
||||
if (builtinsFile != null && !PyUtil.isClassPrivateName(referencedName) && PyUtil.getInitialUnderscores(referencedName) != 1) {
|
||||
for (RatedResolveResult resolveResult : builtinsFile.multiResolveName(referencedName)) {
|
||||
result.add(new ImportedResolveResult(resolveResult.getElement(), resolveResult.getRate(), null));
|
||||
}
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
print(<error descr="Unresolved reference '_T'">_T</error>, <error descr="Unresolved reference '_KT'">_KT</error>)
|
||||
print(__import__)
|
||||
print(len)
|
||||
+35
-25
@@ -46,12 +46,12 @@ import java.util.Optional;
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
abstract class CreateConfigurationTestTask<T extends RunConfiguration> extends PyExecutionFixtureTestTask {
|
||||
public abstract class CreateConfigurationTestTask<T extends RunConfiguration> extends PyExecutionFixtureTestTask {
|
||||
|
||||
@Nullable
|
||||
private final String myTestRunnerName;
|
||||
@NotNull
|
||||
private final Class<? extends RunConfiguration> myExpectedConfigurationType;
|
||||
private final Class<T> myExpectedConfigurationType;
|
||||
|
||||
/**
|
||||
* @param testRunnerName test runner name (to set as default to make sure producer launched)
|
||||
@@ -77,39 +77,49 @@ abstract class CreateConfigurationTestTask<T extends RunConfiguration> extends P
|
||||
for (final PsiElement elementToRightClickOn : getPsiElementsToRightClickOn()) {
|
||||
|
||||
|
||||
final List<ConfigurationFromContext> configurationsFromContext =
|
||||
new ConfigurationContext(elementToRightClickOn).getConfigurationsFromContext();
|
||||
Assert.assertNotNull("Producers were not able to create any configuration in " + elementToRightClickOn, configurationsFromContext);
|
||||
|
||||
|
||||
final Optional<ConfigurationFromContext> maybeConfig = configurationsFromContext.stream()
|
||||
.filter(o -> myExpectedConfigurationType.isAssignableFrom(o.getConfiguration().getClass()))
|
||||
.findFirst();
|
||||
Assert.assertTrue("No configuration of expected type created", maybeConfig.isPresent());
|
||||
RunnerAndConfigurationSettings runnerAndConfigurationSettings = maybeConfig.get().getConfigurationSettings();
|
||||
|
||||
|
||||
Assert.assertNotNull("Producers were not able to create any configuration in " + elementToRightClickOn, runnerAndConfigurationSettings);
|
||||
final RunConfiguration configuration = runnerAndConfigurationSettings.getConfiguration();
|
||||
Assert.assertNotNull("No real configuration created", configuration);
|
||||
Assert.assertThat("No name for configuration", configuration.getName(), Matchers.not(Matchers.isEmptyOrNullString()));
|
||||
Assert.assertThat("Bad configuration type in " + elementToRightClickOn, configuration,
|
||||
Matchers.is(Matchers.instanceOf(myExpectedConfigurationType)));
|
||||
|
||||
RunManager.getInstance(getProject()).addConfiguration(runnerAndConfigurationSettings, false);
|
||||
|
||||
@SuppressWarnings("unchecked") // Checked one line above
|
||||
final T typedConfiguration = (T)configuration;
|
||||
final T typedConfiguration = createConfigurationByElement(elementToRightClickOn, myExpectedConfigurationType);
|
||||
checkConfiguration(typedConfiguration, elementToRightClickOn);
|
||||
}
|
||||
}), ModalityState.NON_MODAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emulates right click and create configurwation
|
||||
*/
|
||||
@NotNull
|
||||
public static <T extends RunConfiguration> T createConfigurationByElement(@NotNull final PsiElement elementToRightClickOn,
|
||||
@NotNull Class<T> expectedConfigurationType) {
|
||||
final List<ConfigurationFromContext> configurationsFromContext =
|
||||
new ConfigurationContext(elementToRightClickOn).getConfigurationsFromContext();
|
||||
Assert.assertNotNull("Producers were not able to create any configuration in " + elementToRightClickOn, configurationsFromContext);
|
||||
|
||||
|
||||
final Optional<ConfigurationFromContext> maybeConfig = configurationsFromContext.stream()
|
||||
.filter(o -> expectedConfigurationType.isAssignableFrom(o.getConfiguration().getClass()))
|
||||
.findFirst();
|
||||
Assert.assertTrue("No configuration of expected type created", maybeConfig.isPresent());
|
||||
RunnerAndConfigurationSettings runnerAndConfigurationSettings = maybeConfig.get().getConfigurationSettings();
|
||||
|
||||
|
||||
Assert.assertNotNull("Producers were not able to create any configuration in " + elementToRightClickOn, runnerAndConfigurationSettings);
|
||||
final RunConfiguration configuration = runnerAndConfigurationSettings.getConfiguration();
|
||||
Assert.assertNotNull("No real configuration created", configuration);
|
||||
Assert.assertThat("No name for configuration", configuration.getName(), Matchers.not(Matchers.isEmptyOrNullString()));
|
||||
Assert.assertThat("Bad configuration type in " + elementToRightClickOn, configuration,
|
||||
Matchers.is(Matchers.instanceOf(expectedConfigurationType)));
|
||||
|
||||
RunManager.getInstance(elementToRightClickOn.getProject()).addConfiguration(runnerAndConfigurationSettings, false);
|
||||
|
||||
@SuppressWarnings("unchecked") // Checked one line above
|
||||
final T typedConfiguration = (T)configuration;
|
||||
return typedConfiguration;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected abstract List<PsiElement> getPsiElementsToRightClickOn();
|
||||
|
||||
|
||||
|
||||
protected void checkConfiguration(@NotNull final T configuration, @NotNull final PsiElement elementToRightClickOn) {
|
||||
// Configuration already checked when created, but you can do specific checks here
|
||||
}
|
||||
|
||||
@@ -1114,6 +1114,22 @@ public class PythonCompletionTest extends PyTestCase {
|
||||
assertSameElements(variants, "mod1", "mod2", "foo", "_bar");
|
||||
}
|
||||
|
||||
// PY-22828
|
||||
public void testNoImportedBuiltinNames() {
|
||||
final List<String> suggested = doTestByText("T<caret>\n");
|
||||
assertNotNull(suggested);
|
||||
assertContainsElements(suggested, "TypeError");
|
||||
assertDoesntContain(suggested, "TypeVar");
|
||||
}
|
||||
|
||||
// PY-22828
|
||||
public void testNoProtectedBuiltinNames() {
|
||||
final List<String> suggested = doTestByText("_<caret>\n");
|
||||
assertNotNull(suggested);
|
||||
assertContainsElements(suggested, "__import__");
|
||||
assertDoesntContain(suggested, "_T", "_KT");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
return super.getTestDataPath() + "/completion";
|
||||
|
||||
+5
@@ -763,6 +763,11 @@ public class PyUnresolvedReferencesInspectionTest extends PyInspectionTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-22828
|
||||
public void testNoProtectedBuiltinNames() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Class<? extends PyInspection> getInspectionClass() {
|
||||
|
||||
Reference in New Issue
Block a user