Merge remote-tracking branch 'origin/master' into meanmail/EDU-839

This commit is contained in:
meanmail
2017-04-08 09:27:57 +07:00
249 changed files with 2685 additions and 1986 deletions
@@ -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() {
@@ -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);
@@ -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);
@@ -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
@@ -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();
@@ -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;
@@ -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() {
@@ -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);
@@ -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,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;
@@ -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() {
@@ -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));
}
}
@@ -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,
@@ -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);
}
}
@@ -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));
}
}
};
}
}
@@ -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);
}
};
}
}
@@ -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";
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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();
@@ -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(() -> {
@@ -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
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
}
@@ -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";
}
@@ -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());
}
@@ -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>
@@ -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
@@ -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,
@@ -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 = []
+10 -3
View File
@@ -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);
}
@@ -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));
}
@@ -0,0 +1,3 @@
print(<error descr="Unresolved reference '_T'">_T</error>, <error descr="Unresolved reference '_KT'">_KT</error>)
print(__import__)
print(len)
@@ -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";
@@ -763,6 +763,11 @@ public class PyUnresolvedReferencesInspectionTest extends PyInspectionTestCase {
doTest();
}
// PY-22828
public void testNoProtectedBuiltinNames() {
doTest();
}
@NotNull
@Override
protected Class<? extends PyInspection> getInspectionClass() {