<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/testSrc" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/../../educational/course-creator/src/com/jetbrains/edu/coursecreator/format" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="module" module-name="lang-impl" />
<orderEntry type="module" module-name="student" />
<orderEntry type="library" name="Guava" level="project" />
+ <orderEntry type="module" module-name="testFramework" scope="TEST" />
</component>
</module>
\ No newline at end of file
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
-import com.intellij.ide.projectView.actions.MarkRootActionBase;
import com.intellij.lang.Language;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.project.DumbModePermission;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.roots.ContentEntry;
-import com.intellij.openapi.roots.ModifiableRootModel;
-import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.ModuleRootModificationUtil;
+import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
public void run() {
try {
generatedRoot.set(baseDir.createChildDirectory(this, GENERATED_FILES_FOLDER));
- final ModifiableRootModel model = ModuleRootManager.getInstance(module).getModifiableModel();
- ContentEntry entry = MarkRootActionBase.findContentEntry(model, generatedRoot.get());
- if (entry == null) {
- LOG.info("Failed to find contentEntry for archive folder");
+ VirtualFile contentRootForFile =
+ ProjectRootManager.getInstance(module.getProject()).getFileIndex().getContentRootForFile(generatedRoot.get());
+ if (contentRootForFile == null) {
return;
}
- entry.addExcludeFolder(generatedRoot.get());
- model.commit();
- module.getProject().save();
+ ModuleRootModificationUtil.updateExcludedFolders(module, contentRootForFile, Collections.emptyList(), Collections.singletonList(generatedRoot.get().getUrl()));
}
catch (IOException e) {
LOG.info("Failed to create folder for generated files", e);
@Override
public void fileCreated(@NotNull VirtualFileEvent event) {
VirtualFile createdFile = event.getFile();
+ if (createdFile.isDirectory()) {
+ return;
+ }
+ if (createdFile.getPath().contains(CCUtils.GENERATED_FILES_FOLDER)) {
+ return;
+ }
Project project = ProjectUtil.guessProjectForFile(createdFile);
if (project == null) {
return;
}
+ if (project.getBasePath() !=null && !FileUtil.isAncestor(project.getBasePath(), createdFile.getPath(), true)) {
+ return;
+ }
Course course = StudyTaskManager.getInstance(project).getCourse();
if (course == null || !CCUtils.isCourseCreator(project)) {
return;
if (project == null) {
return;
}
+ if (project.getBasePath() !=null && !FileUtil.isAncestor(project.getBasePath(), removedFile.getPath(), true)) {
+ return;
+ }
Course course = StudyTaskManager.getInstance(project).getCourse();
if (course == null || path.contains(FileUtil.toSystemIndependentName(course.getCourseDirectory()))) {
return;
if (task == null) {
return;
}
- //TODO: remove from steps as well
task.getTaskFiles().remove(removedTaskFile.getName());
}
}
import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.ui.JBColor;
return false;
}
- private static void addPlaceholder(@NotNull CCState state) {
+ private void addPlaceholder(@NotNull CCState state) {
Editor editor = state.getEditor();
Project project = state.getProject();
PsiFile file = state.getFile();
String defaultPlaceholderText = "type here";
answerPlaceholder.setPossibleAnswer(model.hasSelection() ? model.getSelectedText() : defaultPlaceholderText);
- CCCreateAnswerPlaceholderDialog dlg = new CCCreateAnswerPlaceholderDialog(project, answerPlaceholder);
- dlg.show();
- if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) {
+ CCCreateAnswerPlaceholderDialog dlg = createDialog(project, answerPlaceholder);
+ if (!dlg.showAndGet()) {
return;
}
+ String answerPlaceholderText = dlg.getTaskText();
+ answerPlaceholder.setTaskText(StringUtil.notNullize(answerPlaceholderText));
+ answerPlaceholder.setLength(StringUtil.notNullize(answerPlaceholderText).length());
+ answerPlaceholder.setHints(dlg.getHints());
if (!model.hasSelection()) {
DocumentUtil.writeInRunUndoTransparentAction(() -> document.insertString(offset, defaultPlaceholderText));
}
return state.getAnswerPlaceholder() != null;
}
+
+ protected CCCreateAnswerPlaceholderDialog createDialog(Project project, AnswerPlaceholder answerPlaceholder) {
+ return new CCCreateAnswerPlaceholderDialog(project, StringUtil.notNullize(answerPlaceholder.getTaskText()), answerPlaceholder.getHints());
+ }
}
\ No newline at end of file
package com.jetbrains.edu.coursecreator.actions;
import com.intellij.ide.projectView.ProjectView;
-import com.intellij.openapi.command.undo.DocumentReference;
-import com.intellij.openapi.command.undo.UndoableAction;
+import com.intellij.openapi.command.undo.BasicUndoableAction;
import com.intellij.openapi.command.undo.UnexpectedUndoException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.Task;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
-import org.jetbrains.annotations.Nullable;
public class CCAddAsTaskFile extends CCTaskFileActionBase {
public static final String ACTION_NAME = "Make Visible to Student";
return StudyUtils.getTaskFile(project, file) == null && !CCUtils.isTestsFile(project, file);
}
- private static class AddTaskFile implements UndoableAction {
+ private static class AddTaskFile extends BasicUndoableAction {
private final VirtualFile myFile;
private TaskFile myTaskFile;
private final Course myCourse;
private final Task myTask;
public AddTaskFile(VirtualFile file, TaskFile taskFile, Course course, Project project, Task task) {
+ super(file);
myFile = file;
myTaskFile = taskFile;
myCourse = course;
ProjectView.getInstance(myProject).refresh();
}
- @Nullable
- @Override
- public DocumentReference[] getAffectedDocuments() {
- return new DocumentReference[0];
- }
-
@Override
public boolean isGlobal() {
return true;
if (answerPlaceholder == null) {
return;
}
- CCCreateAnswerPlaceholderDialog dlg = new CCCreateAnswerPlaceholderDialog(project, answerPlaceholder
- );
+ CCCreateAnswerPlaceholderDialog dlg = new CCCreateAnswerPlaceholderDialog(project, answerPlaceholder.getTaskText(), answerPlaceholder.getHints());
dlg.setTitle("Edit Answer Placeholder");
dlg.show();
}
import com.intellij.ide.projectView.ProjectView;
import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.command.undo.DocumentReference;
-import com.intellij.openapi.command.undo.UndoableAction;
+import com.intellij.openapi.command.undo.BasicUndoableAction;
import com.intellij.openapi.command.undo.UnexpectedUndoException;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
-import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorImpl;
+import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.edu.coursecreator.CCUtils;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseFormat.Task;
import com.jetbrains.edu.learning.courseFormat.TaskFile;
-import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.Map;
EduUtils.runUndoableAction(project, ACTION_NAME, new HideTaskFile(project, course, file, task, taskFile));
}
- private static class HideTaskFile implements UndoableAction {
+ private static class HideTaskFile extends BasicUndoableAction {
private final Project myProject;
private final Course myCourse;
private final TaskFile myTaskFile;
public HideTaskFile(Project project, Course course, VirtualFile file, Task task, TaskFile taskFile) {
+ super(file);
myProject = project;
myCourse = course;
myFile = file;
CCUtils.createResourceFile(myFile, myCourse, StudyUtils.getTaskDir(myFile));
if (!myTaskFile.getAnswerPlaceholders().isEmpty() && FileEditorManager.getInstance(myProject).isFileOpen(myFile)) {
for (FileEditor fileEditor : FileEditorManager.getInstance(myProject).getEditors(myFile)) {
- if (fileEditor instanceof PsiAwareTextEditorImpl) {
- Editor editor = ((PsiAwareTextEditorImpl)fileEditor).getEditor();
+ if (fileEditor instanceof TextEditor) {
+ Editor editor = ((TextEditor)fileEditor).getEditor();
StudyUtils.drawAllWindows(editor, myTaskFile);
}
}
ProjectView.getInstance(myProject).refresh();
}
- @Nullable
- @Override
- public DocumentReference[] getAffectedDocuments() {
- return new DocumentReference[0];
- }
-
@Override
public boolean isGlobal() {
return true;
public static void hideFromStudent(VirtualFile file, Project project, Map<String, TaskFile> taskFiles, TaskFile taskFile) {
if (!taskFile.getAnswerPlaceholders().isEmpty() && FileEditorManager.getInstance(project).isFileOpen(file)) {
for (FileEditor fileEditor : FileEditorManager.getInstance(project).getEditors(file)) {
- if (fileEditor instanceof PsiAwareTextEditorImpl) {
- Editor editor = ((PsiAwareTextEditorImpl)fileEditor).getEditor();
+ if (fileEditor instanceof TextEditor) {
+ Editor editor = ((TextEditor)fileEditor).getEditor();
editor.getMarkupModel().removeAllHighlighters();
}
}
public class CCShowPreview extends DumbAwareAction {
public static final String SHOW_PREVIEW = "Show Preview";
+ public static final String NO_PREVIEW_MESSAGE = "Preview is available for task files with answer placeholders only";
public CCShowPreview() {
super(SHOW_PREVIEW, SHOW_PREVIEW, null);
createdEditor.setCaretEnabled(false);
showPreviewFrame.setComponent(labeledEditor);
showPreviewFrame.setSize(new Dimension(500, 500));
- showPreviewFrame.show();
+ if (!ApplicationManager.getApplication().isUnitTestMode()) {
+ showPreviewFrame.show();
+ }
}
}
\ No newline at end of file
--- /dev/null
+we're going to delete this placeholder
\ No newline at end of file
--- /dev/null
+we're going to delete <placeholder>thi<caret>s</placeholder> placeholder
\ No newline at end of file
--- /dev/null
+<placeholder taskText="type here" hint="Test hint">here</placeholder> will be added one placeholder
\ No newline at end of file
--- /dev/null
+<selection>here</selection> will be added one placeholder
\ No newline at end of file
--- /dev/null
+type <placeholder>h<selection>ere</selection></placeholder>
\ No newline at end of file
--- /dev/null
+def f():
+ pass
+ <placeholder taskText="type here" hint="Test hint">type here</placeholder>
\ No newline at end of file
--- /dev/null
+def f():
+ pass
+ <caret>
\ No newline at end of file
--- /dev/null
+no placeholders
\ No newline at end of file
--- /dev/null
+print("<placeholder>veryverylongtextveryverylongtextveryverylongtextveryverylongtext</placeholder> + <placeholder>test</placeholder> = 2")
\ No newline at end of file
--- /dev/null
+print("<placeholder taskText="veryverylongtextveryverylongtextveryverylongtextveryverylongtext">1</placeholder> + <placeholder taskText="test">1</placeholder> = 2")
\ No newline at end of file
--- /dev/null
+<placeholder>type here</placeholder>
\ No newline at end of file
--- /dev/null
+<placeholder taskText="type here">two</placeholder>
\ No newline at end of file
--- /dev/null
+non task file
\ No newline at end of file
--- /dev/null
+test task file with <placeholder>placeholder</placeholder>
\ No newline at end of file
--- /dev/null
+package com.jetbrains.edu.coursecreator;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.command.WriteCommandAction;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.EditorFactory;
+import com.intellij.openapi.editor.markup.MarkupModel;
+import com.intellij.openapi.editor.markup.RangeHighlighter;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.testFramework.EditorTestUtil;
+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 org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.junit.ComparisonFailure;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public abstract class CCTestCase extends CodeInsightFixtureTestCase {
+ private static final Logger LOG = Logger.getInstance(CCTestCase.class);
+
+ @Nullable
+ public static RangeHighlighter getHighlighter(MarkupModel model, AnswerPlaceholder placeholder) {
+ for (RangeHighlighter highlighter : model.getAllHighlighters()) {
+ int endOffset = placeholder.getOffset() + placeholder.getRealLength();
+ if (highlighter.getStartOffset() == placeholder.getOffset() && highlighter.getEndOffset() == endOffset) {
+ return highlighter;
+ }
+ }
+ return null;
+ }
+
+ protected static void checkHighlighters(TaskFile taskFile, MarkupModel markupModel) {
+ for (AnswerPlaceholder answerPlaceholder : taskFile.getAnswerPlaceholders()) {
+ if (getHighlighter(markupModel, answerPlaceholder) == null) {
+ throw new AssertionError("No highlighter for placeholder: " + CCTestsUtil.getPlaceholderPresentation(answerPlaceholder));
+ }
+ }
+ }
+
+ public void checkByFile(TaskFile taskFile, String fileName, boolean useLength) {
+ Pair<Document, List<AnswerPlaceholder>> placeholders = getPlaceholders(fileName, useLength, true);
+ String message = "Placeholders don't match";
+ if (taskFile.getAnswerPlaceholders().size() != placeholders.second.size()) {
+ throw new ComparisonFailure(message,
+ CCTestsUtil.getPlaceholdersPresentation(taskFile.getAnswerPlaceholders()),
+ CCTestsUtil.getPlaceholdersPresentation(placeholders.second));
+ }
+ for (AnswerPlaceholder answerPlaceholder : placeholders.getSecond()) {
+ AnswerPlaceholder placeholder = taskFile.getAnswerPlaceholder(answerPlaceholder.getOffset());
+ if (!CCTestsUtil.comparePlaceholders(placeholder, answerPlaceholder)) {
+ throw new ComparisonFailure(message,
+ CCTestsUtil.getPlaceholdersPresentation(taskFile.getAnswerPlaceholders()),
+ CCTestsUtil.getPlaceholdersPresentation(placeholders.second));
+ }
+ }
+ }
+
+ @Override
+ protected String getBasePath() {
+ return "/community/python/educational-core/course-creator/testData";
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ Course course = new Course();
+ course.setName("test course");
+ course.setCourseDirectory(getProject().getBasePath());
+ StudyTaskManager.getInstance(getProject()).setCourse(course);
+
+ Lesson lesson = new Lesson();
+ lesson.setName("lesson1");
+ Task task = new Task();
+ task.setName("task1");
+ task.setIndex(1);
+ lesson.addTask(task);
+ lesson.setIndex(1);
+ course.getLessons().add(lesson);
+ course.setCourseMode(CCUtils.COURSE_MODE);
+ course.initCourse(false);
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ VirtualFile lesson1 = myFixture.getProject().getBaseDir().createChildDirectory(this, "lesson1");
+ lesson1.createChildDirectory(this, "task1");
+ }
+ catch (IOException e) {
+ //ignore
+ }
+ }
+ });
+ }
+
+ protected VirtualFile copyFileToTask(String name) {
+ return myFixture.copyFileToProject(name, FileUtil.join(getProject().getBasePath(), "lesson1", "task1", name));
+ }
+
+ protected VirtualFile configureByTaskFile(String name) {
+ Task task = StudyTaskManager.getInstance(getProject()).getCourse().getLessons().get(0).getTaskList().get(0);
+ TaskFile taskFile = new TaskFile();
+ taskFile.setTask(task);
+ task.getTaskFiles().put(name, taskFile);
+ VirtualFile file = copyFileToTask(name);
+ myFixture.configureFromExistingVirtualFile(file);
+ Document document = FileDocumentManager.getInstance().getDocument(file);
+ for (AnswerPlaceholder placeholder : getPlaceholders(document, false)) {
+ taskFile.addAnswerPlaceholder(placeholder);
+ }
+ taskFile.sortAnswerPlaceholders();
+ StudyUtils.drawAllWindows(myFixture.getEditor(), taskFile);
+ CCUtils.createResourceFile(file, StudyTaskManager.getInstance(getProject()).getCourse(), file.getParent());
+ return file;
+ }
+
+ private static List<AnswerPlaceholder> getPlaceholders(Document document, boolean useLength) {
+ final List<AnswerPlaceholder> placeholders = new ArrayList<>();
+ new WriteCommandAction(null) {
+ @Override
+ protected void run(@NotNull Result result) {
+ final String openingTagRx = "<placeholder( taskText=\"(.+?)\")?( possibleAnswer=\"(.+?)\")?( hint=\"(.+?)\")?>";
+ final String closingTagRx = "</placeholder>";
+ CharSequence text = document.getCharsSequence();
+ final Matcher openingMatcher = Pattern.compile(openingTagRx).matcher(text);
+ final Matcher closingMatcher = Pattern.compile(closingTagRx).matcher(text);
+ int pos = 0;
+ while (openingMatcher.find(pos)) {
+ AnswerPlaceholder answerPlaceholder = new AnswerPlaceholder();
+ answerPlaceholder.setUseLength(useLength);
+ String taskText = openingMatcher.group(2);
+ if (taskText != null) {
+ answerPlaceholder.setTaskText(taskText);
+ answerPlaceholder.setLength(taskText.length());
+ }
+ String possibleAnswer = openingMatcher.group(4);
+ if (possibleAnswer != null) {
+ answerPlaceholder.setPossibleAnswer(possibleAnswer);
+ }
+ String hint = openingMatcher.group(6);
+ if (hint != null) {
+ answerPlaceholder.setHint(hint);
+ }
+ answerPlaceholder.setOffset(openingMatcher.start());
+ if (!closingMatcher.find(openingMatcher.end())) {
+ LOG.error("No matching closing tag found");
+ }
+ if (useLength) {
+ answerPlaceholder.setLength(closingMatcher.start() - openingMatcher.end());
+ } else {
+ if (possibleAnswer == null) {
+ answerPlaceholder.setPossibleAnswer(document.getText(TextRange.create(openingMatcher.end(), closingMatcher.start())));
+ }
+ }
+ document.deleteString(closingMatcher.start(), closingMatcher.end());
+ document.deleteString(openingMatcher.start(), openingMatcher.end());
+ placeholders.add(answerPlaceholder);
+ pos = answerPlaceholder.getOffset() + answerPlaceholder.getRealLength();
+ }
+ }
+ }.execute();
+ return placeholders;
+ }
+
+ public Pair<Document, List<AnswerPlaceholder>> getPlaceholders(String name) {
+ return getPlaceholders(name, true, false);
+ }
+
+ public Pair<Document, List<AnswerPlaceholder>> getPlaceholders(String name, boolean useLength, boolean removeMarkers) {
+ VirtualFile resultFile = LocalFileSystem.getInstance().findFileByPath(getTestDataPath() + "/" + name);
+ Document document = FileDocumentManager.getInstance().getDocument(resultFile);
+ Document tempDocument = EditorFactory.getInstance().createDocument(document.getCharsSequence());
+ if (removeMarkers) {
+ EditorTestUtil.extractCaretAndSelectionMarkers(tempDocument);
+ }
+ List<AnswerPlaceholder> placeholders = getPlaceholders(tempDocument, useLength);
+ return Pair.create(tempDocument, placeholders);
+ }
+}
+
+
--- /dev/null
+package com.jetbrains.edu.coursecreator;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.intellij.openapi.util.text.StringUtil;
+import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
+
+import java.util.Collection;
+import java.util.List;
+
+public class CCTestsUtil {
+ public static final String BEFORE_POSTFIX = "_before.txt";
+ public static final String AFTER_POSTFIX = "_after.txt";
+
+ private CCTestsUtil() {
+ }
+
+ public static boolean comparePlaceholders(AnswerPlaceholder p1, AnswerPlaceholder p2) {
+ if (p1.getOffset() != p2.getOffset()) return false;
+ if (p1.getRealLength() != p2.getRealLength()) return false;
+ if (p1.getPossibleAnswer() != null ? !p1.getPossibleAnswer().equals(p2.getPossibleAnswer()) : p2.getPossibleAnswer() != null) return false;
+ if (p1.getTaskText() != null ? !p1.getTaskText().equals(p2.getTaskText()) : p2.getTaskText() != null) return false;
+ if (!p1.getHints().equals(p1.getHints())) return false;
+ return true;
+ }
+
+ public static String getPlaceholderPresentation(AnswerPlaceholder placeholder) {
+ return "offset=" + placeholder.getOffset() +
+ " length=" + placeholder.getLength() +
+ " possibleAnswer=" + placeholder.getPossibleAnswer() +
+ " taskText=" + placeholder.getTaskText();
+ }
+
+ public static String getPlaceholdersPresentation(List<AnswerPlaceholder> placeholders) {
+ Collection<String> transformed = Collections2.transform(placeholders, new Function<AnswerPlaceholder, String>() {
+ @Override
+ public String apply(AnswerPlaceholder placeholder) {
+ return getPlaceholderPresentation(placeholder);
+ }
+ });
+ return "[" + StringUtil.join(transformed, ",") + "]";
+ }
+}
--- /dev/null
+package com.jetbrains.edu.coursecreator.actions;
+
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.command.undo.UndoManager;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.jetbrains.edu.coursecreator.CCTestCase;
+import com.jetbrains.edu.coursecreator.CCTestsUtil;
+import com.jetbrains.edu.learning.StudyUtils;
+import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
+import com.jetbrains.edu.learning.courseFormat.TaskFile;
+import com.jetbrains.edu.learning.ui.CCCreateAnswerPlaceholderDialog;
+
+import java.util.Collections;
+import java.util.List;
+
+public class CCAnswerPlaceholderActionTest extends CCTestCase {
+ static class CCTestAction extends CCAddAnswerPlaceholder {
+ @Override
+ protected CCCreateAnswerPlaceholderDialog createDialog(Project project, AnswerPlaceholder answerPlaceholder) {
+ return new CCCreateAnswerPlaceholderDialog(project, answerPlaceholder.getTaskText(), answerPlaceholder.getHints()) {
+ @Override
+ public boolean showAndGet() {
+ return true;
+ }
+
+ @Override
+ public String getTaskText() {
+ return "type here";
+ }
+
+ @Override
+ public List<String> getHints() {
+ return Collections.singletonList("Test hint");
+ }
+ };
+ }
+ }
+
+ public void testPlaceholderWithSelection() {
+ doTest("onePlaceholder");
+ }
+
+ public void testPlaceholderWithoutSelection() {
+ doTest("withoutSelection");
+ }
+
+ public void testPlaceholderIntersection() {
+ configureByTaskFile("placeholderIntersection.txt");
+ Presentation presentation = myFixture.testAction(new CCTestAction());
+ assertTrue(presentation.isVisible() && !presentation.isEnabled());
+ }
+
+ public void testPlaceholderDeleted() {
+ doTest("deletePlaceholder");
+ }
+
+ private void doTest(String name) {
+ VirtualFile virtualFile = configureByTaskFile(name + CCTestsUtil.BEFORE_POSTFIX);
+ myFixture.testAction(new CCTestAction());
+ TaskFile taskFile = StudyUtils.getTaskFile(getProject(), virtualFile);
+ checkByFile(taskFile, name + CCTestsUtil.AFTER_POSTFIX, false);
+ checkHighlighters(taskFile, myFixture.getEditor().getMarkupModel());
+ UndoManager.getInstance(getProject()).undo(FileEditorManager.getInstance(getProject()).getSelectedEditor(virtualFile));
+ checkByFile(taskFile, name + CCTestsUtil.BEFORE_POSTFIX, false);
+ }
+
+ @Override
+ protected String getBasePath() {
+ return super.getBasePath() + "/actions/addPlaceholder";
+ }
+}
--- /dev/null
+package com.jetbrains.edu.coursecreator.actions;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.EditorFactory;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiManager;
+import com.intellij.testFramework.MapDataContext;
+import com.intellij.testFramework.TestActionEvent;
+import com.jetbrains.edu.coursecreator.CCTestCase;
+import com.jetbrains.edu.coursecreator.CCTestsUtil;
+import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
+
+import java.util.List;
+
+public class CCShowPreviewTest extends CCTestCase {
+
+ public void testPreviewUnavailable() {
+ VirtualFile file = configureByTaskFile("noplaceholders.txt");
+ CCShowPreview action = new CCShowPreview();
+ TestActionEvent e = getActionEvent(action, PsiManager.getInstance(getProject()).findFile(file));
+ action.beforeActionPerformedUpdate(e);
+ assertTrue(e.getPresentation().isEnabled() && e.getPresentation().isVisible());
+ try {
+ action.actionPerformed(e);
+ assertTrue("No message shown", false);
+ } catch (RuntimeException ex) {
+ assertEquals(CCShowPreview.NO_PREVIEW_MESSAGE, ex.getMessage());
+ }
+ }
+
+ public void testOnePlaceholder() {
+ doTest("test");
+ }
+
+ public void testSeveralPlaceholders() {
+ doTest("several");
+ }
+
+ private void doTest(String name) {
+ VirtualFile file = configureByTaskFile(name + CCTestsUtil.BEFORE_POSTFIX);
+ CCShowPreview action = new CCShowPreview();
+ TestActionEvent e = getActionEvent(action,PsiManager.getInstance(getProject()).findFile(file));
+ action.beforeActionPerformedUpdate(e);
+ assertTrue(e.getPresentation().isEnabled() && e.getPresentation().isVisible());
+ action.actionPerformed(e);
+ Editor editor = EditorFactory.getInstance().getAllEditors()[1];
+ Pair<Document, List<AnswerPlaceholder>> pair = getPlaceholders(name + CCTestsUtil.AFTER_POSTFIX);
+ assertEquals("Files don't match", editor.getDocument().getText(), pair.getFirst().getText());
+ for (AnswerPlaceholder placeholder : pair.getSecond()) {
+ assertNotNull("No highlighter for placeholder:" + CCTestsUtil.getPlaceholderPresentation(placeholder), getHighlighter(editor.getMarkupModel(), placeholder));
+ }
+ }
+
+ @Override
+ protected String getBasePath() {
+ return super.getBasePath() + "/actions/preview";
+ }
+
+ TestActionEvent getActionEvent(AnAction action, PsiFile psiFile) {
+ MapDataContext context = new MapDataContext();
+ context.put(CommonDataKeys.PSI_FILE, psiFile);
+ context.put(CommonDataKeys.PROJECT, getProject());
+ context.put(LangDataKeys.MODULE, myFixture.getModule());
+ return new TestActionEvent(context, action);
+ }
+}
--- /dev/null
+package com.jetbrains.edu.coursecreator.actions;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.command.undo.UndoManager;
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.testFramework.MapDataContext;
+import com.intellij.testFramework.TestActionEvent;
+import com.jetbrains.edu.coursecreator.CCTestCase;
+import com.jetbrains.edu.learning.StudyUtils;
+import com.jetbrains.edu.learning.courseFormat.TaskFile;
+import org.jetbrains.annotations.NotNull;
+
+public class CCTaskFileActionTest extends CCTestCase {
+ public void testHideTaskFile() {
+ VirtualFile virtualFile = configureByTaskFile("taskFile.txt");
+ launchAction(virtualFile, new CCHideFromStudent());
+ assertNull(StudyUtils.getTaskFile(getProject(), virtualFile));
+ UndoManager.getInstance(getProject()).undo(FileEditorManager.getInstance(getProject()).getSelectedEditor(virtualFile));
+ TaskFile taskFile = StudyUtils.getTaskFile(getProject(), virtualFile);
+ assertNotNull(taskFile);
+ checkHighlighters(taskFile, myFixture.getEditor().getMarkupModel());
+ }
+
+ public void testAddTaskFile() {
+ VirtualFile virtualFile = copyFileToTask("nonTaskFile.txt");
+ myFixture.configureFromExistingVirtualFile(virtualFile);
+ launchAction(virtualFile, new CCAddAsTaskFile());
+ TaskFile taskFile = StudyUtils.getTaskFile(getProject(), virtualFile);
+ assertNotNull(taskFile);
+ FileEditor fileEditor = FileEditorManager.getInstance(getProject()).getSelectedEditor(virtualFile);
+ UndoManager.getInstance(getProject()).undo(fileEditor);
+ assertNull(StudyUtils.getTaskFile(getProject(), virtualFile));
+ }
+
+ private void launchAction(VirtualFile virtualFile, AnAction action) {
+ TestActionEvent e = getActionEvent(virtualFile, action);
+ action.beforeActionPerformedUpdate(e);
+ assertTrue(e.getPresentation().isEnabled() && e.getPresentation().isVisible());
+ action.actionPerformed(e);
+ }
+
+ @NotNull
+ private TestActionEvent getActionEvent(VirtualFile virtualFile, AnAction action) {
+ MapDataContext context = new MapDataContext();
+ context.put(CommonDataKeys.VIRTUAL_FILE, virtualFile);
+ context.put(CommonDataKeys.PROJECT, getProject());
+ return new TestActionEvent(context, action);
+ }
+
+ @Override
+ protected String getBasePath() {
+ return super.getBasePath() + "/actions/taskFileActions";
+ }
+}
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.ValidationInfo;
import com.intellij.openapi.util.text.StringUtil;
-import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class CCCreateAnswerPlaceholderDialog extends DialogWrapper {
- private static final String ourTitle = "Add Answer Placeholder";
- private final AnswerPlaceholder myAnswerPlaceholder;
+ private static final String TITLE = "Add Answer Placeholder";
private final CCCreateAnswerPlaceholderPanel myPanel;
private final Project myProject;
}
public CCCreateAnswerPlaceholderDialog(@NotNull final Project project,
- @NotNull final AnswerPlaceholder answerPlaceholder) {
+ String placeholderText,
+ List<String> hints) {
super(project, true);
- myAnswerPlaceholder = answerPlaceholder;
myProject = project;
- myPanel = new CCCreateAnswerPlaceholderPanel(answerPlaceholder);
- myPanel.showAnswerPlaceholderText(StringUtil.notNullize(answerPlaceholder.getTaskText()));
-
- setTitle(ourTitle);
+ myPanel = new CCCreateAnswerPlaceholderPanel(placeholderText, hints);
+ setTitle(TITLE);
init();
initValidation();
}
- @Override
- protected void doOKAction() {
- String answerPlaceholderText = myPanel.getAnswerPlaceholderText();
- myAnswerPlaceholder.setTaskText(StringUtil.notNullize(answerPlaceholderText));
- myAnswerPlaceholder.setLength(StringUtil.notNullize(answerPlaceholderText).length());
+ public String getTaskText() {
+ return StringUtil.notNullize(myPanel.getAnswerPlaceholderText());
+ }
+
+ public List<String> getHints() {
final List<String> hints = myPanel.getHints();
- if (hints.size() == 1 && hints.get(0).isEmpty()) {
- myAnswerPlaceholder.setHints(Collections.emptyList());
- }
- else {
- myAnswerPlaceholder.setHints(hints);
- }
- super.doOKAction();
+ return hints.size() == 1 && hints.get(0).isEmpty() ? Collections.emptyList() : hints;
}
@Nullable
import com.intellij.ui.components.JBLabel;
import com.intellij.uiDesigner.core.GridLayoutManager;
import com.intellij.util.ui.UIUtil;
-import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.List;
public class CCCreateAnswerPlaceholderPanel {
- private static final String ourFirstHintText = "Type here to add hint";
+ private static final String NEXT_HINT = "Next Hint";
+ private static final String PREVIOUS_HINT = "Previous Hint";
+ private static final String ADD_HINT = "Add Hint";
+ private static final String REMOVE_HINT = "Remove Hint";
+ private static final String HINT_PLACEHOLDER = "Type here to add hint";
private JPanel myPanel;
private JTextArea myHintTextArea;
private JBLabel myHintLabel;
private JPanel actionsPanel;
private JTextArea myPlaceholderTextArea;
- private List<String> myHints = new ArrayList<String>() {
- };
+ private List<String> myHints = new ArrayList<>();
private int myShownHintNumber = 0;
- public CCCreateAnswerPlaceholderPanel(@NotNull final AnswerPlaceholder answerPlaceholder) {
- if (answerPlaceholder.getHints().isEmpty()) {
- myHints.add(ourFirstHintText);
+ public CCCreateAnswerPlaceholderPanel(String placeholderText, List<String> hints) {
+ if (hints.isEmpty()) {
+ myHints.add(HINT_PLACEHOLDER);
}
else {
- myHints.addAll(answerPlaceholder.getHints());
+ myHints.addAll(hints);
}
myPlaceholderTextArea.setBorder(BorderFactory.createLineBorder(JBColor.border()));
actionsPanel.add(createHintToolbarComponent(), BorderLayout.WEST);
showHint(myHints.get(myShownHintNumber));
+ myPlaceholderTextArea.setText(placeholderText);
}
@NotNull
return new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
- if (myHintTextArea.getText().equals(ourFirstHintText)) {
+ if (myHintTextArea.getText().equals(HINT_PLACEHOLDER)) {
myHintTextArea.setForeground(UIUtil.getActiveTextColor());
myHintTextArea.setText("");
}
public void focusLost(FocusEvent e) {
if (myShownHintNumber == 0 && myHintTextArea.getText().isEmpty()) {
myHintTextArea.setForeground(UIUtil.getInactiveTextColor());
- myHintTextArea.setText(ourFirstHintText);
+ myHintTextArea.setText(HINT_PLACEHOLDER);
}
}
};
}
}
- public void showAnswerPlaceholderText(String answerPlaceholderText) {
- myPlaceholderTextArea.setText(answerPlaceholderText);
- }
-
public void showHint(String hintText) {
- if (myHints.get(myShownHintNumber).equals(ourFirstHintText)) {
+ if (myHints.get(myShownHintNumber).equals(HINT_PLACEHOLDER)) {
myHintTextArea.setForeground(UIUtil.getInactiveTextColor());
}
else {
public List<String> getHints() {
final String hintText = myHintTextArea.getText();
- if (myShownHintNumber == 0 && hintText.equals(ourFirstHintText)) {
+ if (myShownHintNumber == 0 && hintText.equals(HINT_PLACEHOLDER)) {
myHints.set(myShownHintNumber, "");
}
else {
private class ShowNext extends AnAction {
public ShowNext() {
- super("Next Hint", "Next Hint", AllIcons.Actions.Forward);
+ super(NEXT_HINT, NEXT_HINT, AllIcons.Actions.Forward);
}
@Override
private class ShowPrevious extends AnAction {
public ShowPrevious() {
- super("Previous Hint", "Previous Hint", AllIcons.Actions.Back);
+ super(PREVIOUS_HINT, PREVIOUS_HINT, AllIcons.Actions.Back);
}
@Override
private class AddHint extends AnAction {
public AddHint() {
- super("Add Hint", "Add Hint", AllIcons.General.Add);
+ super(ADD_HINT, ADD_HINT, AllIcons.General.Add);
}
@Override
private class RemoveHint extends AnAction {
public RemoveHint() {
- super("Remove Hint", "Remove Hint", AllIcons.General.Remove);
+ super(REMOVE_HINT, REMOVE_HINT, AllIcons.General.Remove);
}
@Override
private inner class EditHint : AnAction("Edit Hint", "Edit Hint", AllIcons.Modules.Edit) {
override fun actionPerformed(e: AnActionEvent?) {
- val dialog = CCCreateAnswerPlaceholderDialog(e!!.project!!, myPlaceholder!!)
+ val dialog = CCCreateAnswerPlaceholderDialog(e!!.project!!, myPlaceholder!!.taskText, myPlaceholder.hints)
dialog.show()
}