moved course creation plugin to pycharm educational

This commit is contained in:
Ekaterina Tuzova
2014-08-29 12:13:02 +04:00
37 changed files with 2739 additions and 0 deletions
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PLUGIN_MODULE" version="4">
<component name="DevKit.ModuleBuildProperties" url="file://$MODULE_DIR$/META-INF/plugin.xml" />
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/pluginResources" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
@@ -0,0 +1,62 @@
<idea-plugin version="2">
<id>org.jetbrains.plugins.coursecreator</id>
<name>Cource Creator for PyCharm Education</name>
<version>1.0</version>
<description><![CDATA[
Plugin allows you to create new course for PyCharm Education Edition.
]]></description>
<change-notes><![CDATA[
]]>
</change-notes>
<!-- please see http://confluence.jetbrains.com/display/IDEADEV/Build+Number+Ranges for description -->
<idea-version since-build="138"/>
<!-- please see http://confluence.jetbrains.com/display/IDEADEV/Plugin+Compatibility+with+IntelliJ+Platform+Products
on how to target different products -->
<!-- uncomment to enable plugin in all products
<depends>com.intellij.modules.lang</depends>
-->
<depends>com.intellij.modules.python</depends>
<extensions defaultExtensionNs="com.intellij">
<directoryProjectGenerator implementation="org.jetbrains.plugins.coursecreator.CCProjectGenerator"/>
<projectService serviceImplementation="org.jetbrains.plugins.coursecreator.CCProjectService"/>
<codeInsight.lineMarkerProvider language="Python"
implementationClass="org.jetbrains.plugins.coursecreator.highlighting.CCTaskLineMarkerProvider"/>
<treeStructureProvider implementation="org.jetbrains.plugins.coursecreator.projectView.CCTreeStructureProvider"/>
</extensions>
<application-components>
<!-- Add your application components here -->
</application-components>
<project-components>
<!-- Add your project components here -->
<component>
<implementation-class>org.jetbrains.plugins.coursecreator.CCProjectComponent</implementation-class>
</component>
</project-components>
<actions>
<action id="CreateLesson" class="org.jetbrains.plugins.coursecreator.actions.CreateLesson">
<add-to-group group-id="NewGroup" anchor="before" relative-to-action="NewFile"/>
</action>
<action id="CreateTaskFile" class="org.jetbrains.plugins.coursecreator.actions.CreateTaskFile">
<add-to-group group-id="NewGroup" anchor="before" relative-to-action="NewFile"/>
</action>
<action id="CreateTask" class="org.jetbrains.plugins.coursecreator.actions.CreateTask">
<add-to-group group-id="NewGroup" anchor="before" relative-to-action="NewFile"/>
</action>
<action id="AddTaskWindow" class="org.jetbrains.plugins.coursecreator.actions.AddTaskWindow">
<add-to-group group-id="EditorPopupMenu" anchor="before" relative-to-action="CopyReference"/>
</action>
<action id="PackCourse" class="org.jetbrains.plugins.coursecreator.actions.CreateCourseArchive">
<add-to-group group-id="MainToolBar" anchor="last" />
</action>
</actions>
</idea-plugin>
@@ -0,0 +1,4 @@
<html>
Write your task text here.
<br>
</html>
@@ -0,0 +1 @@
# TODO: type solution here
@@ -0,0 +1,127 @@
import sys
def get_file_text(path):
""" get file text by path"""
file_io = open(path, "r")
text = file_io.read()
file_io.close()
return text
def get_file_output(path):
# TODO: get file output by path
return ""
def test_file_importable():
""" tests there is no obvious syntax errors"""
path = sys.argv[-1]
try:
import_file(path)
except ImportError:
failed("File contains syntax errors")
return
except SyntaxError:
failed("File contains syntax errors")
return
except NameError:
failed("File contains syntax errors")
return
passed()
def import_file(path):
""" returns imported file """
import imp
tmp = imp.load_source('tmp', path)
return tmp
def import_task_file():
""" returns imported file """
path = sys.argv[-1]
return import_file(path)
def test_is_not_empty():
path = sys.argv[-1]
file_text = get_file_text(path)
if len(file_text) > 0:
passed()
else:
failed("The file is empty. Please, reload the task and try again.")
def test_is_initial_text(error_text="You should modify the file"):
path = sys.argv[-1]
text = get_initial_text(path)
file_text = get_file_text(path)
if file_text.strip() == text:
failed(error_text)
else:
passed()
def get_initial_text(path):
course_lib = sys.argv[-2]
import os
# path format is "project_root/lessonX/taskY/file.py"
task_index = path.rfind(os.sep, 0, path.rfind(os.sep))
index = path.rfind(os.sep, 0, task_index)
relative_path = path[index+1:]
initial_file_path = os.path.join(course_lib, relative_path)
return get_file_text(initial_file_path)
def test_text_equals(text, error_text):
path = sys.argv[-1]
file_text = get_file_text(path)
if file_text.strip() == text:
passed()
else:
failed(error_text)
def test_window_text_deleted(error_text="Don't just delete task text"):
windows = get_task_windows()
for window in windows:
if len(window) == 0:
failed(error_text)
return
passed()
def failed(message="Please, reload the task and try again."):
print("#study_plugin FAILED + " + message)
def passed():
print("#study_plugin test OK")
def get_task_windows():
prefix = "#study_plugin_window = "
path = sys.argv[-1]
import os
windows_path = os.path.splitext(path)[0] + "_windows"
windows = []
f = open(windows_path, "r")
window_text = ""
first = True
for line in f.readlines():
if line.startswith(prefix):
if not first:
windows.append(window_text.strip())
else:
first = False
window_text = line[len(prefix):]
else:
window_text += line
if window_text:
windows.append(window_text.strip())
f.close()
return windows
def run_common_tests(error_text="Please, reload file and try again"):
test_file_importable()
test_is_not_empty()
test_is_initial_text(error_text)
test_window_text_deleted(error_text)
@@ -0,0 +1,17 @@
from test_helper import run_common_tests, failed, passed, get_task_windows
def test_task_windows():
windows = get_task_windows()
window = windows[0]
if window != "": # TODO: your condition here
passed()
else:
failed()
if __name__ == '__main__':
run_common_tests()
test_task_windows()
Binary file not shown.

After

Width:  |  Height:  |  Size: 500 B

@@ -0,0 +1,80 @@
package org.jetbrains.plugins.coursecreator;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.event.EditorFactoryEvent;
import com.intellij.openapi.editor.event.EditorFactoryListener;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.coursecreator.format.*;
public class CCEditorFactoryListener implements EditorFactoryListener {
@Override
public void editorCreated(@NotNull EditorFactoryEvent event) {
Editor editor = event.getEditor();
Project project = editor.getProject();
if (project == null) {
return;
}
VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(editor.getDocument());
if (virtualFile == null) {
return;
}
Course course = CCProjectService.getInstance(project).getCourse();
if (course == null) {
return;
}
final VirtualFile taskDir = virtualFile.getParent();
if (taskDir == null || !taskDir.getName().contains("task")) {
return;
}
final VirtualFile lessonDir = taskDir.getParent();
if (lessonDir == null) return;
final Lesson lesson = course.getLesson(lessonDir.getName());
final Task task = lesson.getTask(taskDir.getName());
final TaskFile taskFile = task.getTaskFile(virtualFile.getName());
TaskFileModificationListener listener = new TaskFileModificationListener(taskFile);
CCProjectService.addDocumentListener(editor.getDocument(), listener);
editor.getDocument().addDocumentListener(listener);
CCProjectService.drawTaskWindows(virtualFile, editor, course);
}
@Override
public void editorReleased(@NotNull EditorFactoryEvent event) {
Editor editor = event.getEditor();
Document document = editor.getDocument();
StudyDocumentListener listener = CCProjectService.getListener(document);
if (listener != null) {
document.removeDocumentListener(listener);
CCProjectService.removeListener(document);
}
editor.getMarkupModel().removeAllHighlighters();
editor.getSelectionModel().removeSelection();
}
private class TaskFileModificationListener extends StudyDocumentListener {
private final TaskFile myTaskFile;
public TaskFileModificationListener(TaskFile taskFile) {
super(taskFile);
myTaskFile = taskFile;
}
@Override
protected void updateTaskWindowLength(CharSequence fragment, TaskWindow taskWindow, int change) {
int newLength = taskWindow.getReplacementLength() + change;
taskWindow.setReplacementLength(newLength <= 0 ? 0 : newLength);
if (fragment.equals("\n")) {
taskWindow.setReplacementLength(taskWindow.getLength() + 1);
}
}
@Override
protected boolean needModify() {
return myTaskFile.isTrackChanges();
}
}
}
@@ -0,0 +1,58 @@
package org.jetbrains.plugins.coursecreator;
import com.intellij.openapi.components.ProjectComponent;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.event.EditorFactoryEvent;
import com.intellij.openapi.editor.impl.EditorFactoryImpl;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.coursecreator.format.Course;
public class CCProjectComponent implements ProjectComponent {
private final Project myProject;
public CCProjectComponent(Project project) {
myProject = project;
}
public void initComponent() {
}
public void disposeComponent() {
}
@NotNull
public String getComponentName() {
return "CCProjectComponent";
}
public void projectOpened() {
StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new Runnable() {
@Override
public void run() {
Course course = CCProjectService.getInstance(myProject).getCourse();
if (course != null) {
EditorFactory.getInstance().addEditorFactoryListener(new CCEditorFactoryListener(), myProject);
VirtualFile[] files = FileEditorManager.getInstance(myProject).getOpenFiles();
for (VirtualFile file : files) {
FileEditor fileEditor = FileEditorManager.getInstance(myProject).getSelectedEditor(file);
if (fileEditor instanceof PsiAwareTextEditorImpl) {
Editor editor = ((PsiAwareTextEditorImpl)fileEditor).getEditor();
new CCEditorFactoryListener().editorCreated(new EditorFactoryEvent(new EditorFactoryImpl(ProjectManager.getInstance()), editor ));
}
}
}
}
});
}
public void projectClosed() {
}
}
@@ -0,0 +1,101 @@
package org.jetbrains.plugins.coursecreator;
import com.intellij.facet.ui.FacetEditorValidator;
import com.intellij.facet.ui.FacetValidatorsManager;
import com.intellij.facet.ui.ValidationResult;
import com.intellij.ide.fileTemplates.FileTemplate;
import com.intellij.ide.fileTemplates.FileTemplateManager;
import com.intellij.ide.fileTemplates.FileTemplateUtil;
import com.intellij.ide.util.DirectoryUtil;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.platform.DirectoryProjectGenerator;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiManager;
import com.jetbrains.python.newProject.PythonProjectGenerator;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.coursecreator.format.Course;
import org.jetbrains.plugins.coursecreator.ui.CCNewProjectPanel;
import javax.swing.*;
public class CCProjectGenerator extends PythonProjectGenerator implements DirectoryProjectGenerator {
private CCNewProjectPanel mySettingsPanel;
@Nls
@NotNull
@Override
public String getName() {
return "Course creation";
}
@Nullable
@Override
public Object showGenerationSettings(VirtualFile baseDir) throws ProcessCanceledException {
return null;
}
@Nullable
@Override
public Icon getLogo() {
return null;
}
@Override
public void generateProject(@NotNull final Project project, @NotNull final VirtualFile baseDir,
@Nullable Object settings, @NotNull Module module) {
final CCProjectService service = CCProjectService.getInstance(project);
final Course course = new Course(mySettingsPanel.getName(), mySettingsPanel.getAuthor(), mySettingsPanel.getDescription());
service.setCourse(course);
final PsiDirectory projectDir = PsiManager.getInstance(project).findDirectory(baseDir);
if (projectDir == null) return;
new WriteCommandAction.Simple(project) {
@Override
protected void run() throws Throwable {
final FileTemplate template = FileTemplateManager.getInstance().getInternalTemplate("test_helper");
try {
FileTemplateUtil.createFromTemplate(template, "test_helper.py", null, projectDir);
}
catch (Exception ignored) {
}
DirectoryUtil.createSubdirectories("hints", projectDir, "\\/");
}
}.execute();
}
@NotNull
@Override
public ValidationResult validate(@NotNull String s) {
String message = "";
message = mySettingsPanel.getDescription().equals("") ? "Enter description" : message;
message = mySettingsPanel.getAuthor().equals("") ? "Enter author name" : message;
message = mySettingsPanel.getName().equals("") ? "Enter course name" : message;
return message.equals("")? ValidationResult.OK : new ValidationResult(message) ;
}
@Nullable
@Override
public JPanel extendBasePanel() throws ProcessCanceledException {
mySettingsPanel = new CCNewProjectPanel();
mySettingsPanel.registerValidators(new FacetValidatorsManager() {
public void registerValidator(FacetEditorValidator validator, JComponent... componentsToWatch) {
throw new UnsupportedOperationException();
}
public void validate() {
fireStateChanged();
}
});
return mySettingsPanel.getMainPanel();
}
}
@@ -0,0 +1,138 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.coursecreator;
import com.intellij.ide.projectView.ProjectView;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.util.xmlb.XmlSerializer;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.coursecreator.format.*;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@State(name = "CCProjectService",
storages = {
@Storage(file = "$PROJECT_CONFIG_DIR$/course_service.xml")
}
)
public class CCProjectService implements PersistentStateComponent<Element> {
private static final Logger LOG = Logger.getInstance(CCProjectService.class.getName());
public Course myCourse;
public static final String COURSE_ELEMENT = "course";
private static final Map<Document, StudyDocumentListener> myDocumentListeners = new HashMap<Document, StudyDocumentListener>();
public void setCourse(@NotNull final Course course) {
myCourse = course;
}
public Course getCourse() {
return myCourse;
}
@Override
public Element getState() {
final Element el = new Element("CCProjectService");
if (myCourse != null) {
Element courseElement = new Element(COURSE_ELEMENT);
XmlSerializer.serializeInto(myCourse, courseElement);
el.addContent(courseElement);
}
return el;
}
@Override
public void loadState(Element el) {
myCourse = XmlSerializer.deserialize(el.getChild(COURSE_ELEMENT), Course.class);
}
public static CCProjectService getInstance(@NotNull Project project) {
return ServiceManager.getService(project, CCProjectService.class);
}
public static void deleteProjectFile(File file, @NotNull final Project project) {
if (!file.delete()) {
LOG.info("Failed to delete file " + file.getPath());
}
VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
ProjectView.getInstance(project).refresh();
}
public static void drawTaskWindows(@NotNull final VirtualFile virtualFile, @NotNull final Editor editor, @NotNull final Course course) {
VirtualFile taskDir = virtualFile.getParent();
if (taskDir == null) {
return;
}
String taskDirName = taskDir.getName();
if (!taskDirName.contains("task")) {
return;
}
VirtualFile lessonDir = taskDir.getParent();
if (lessonDir == null) {
return;
}
String lessonDirName = lessonDir.getName();
if (!lessonDirName.contains("lesson")) {
return;
}
Lesson lesson = course.getLessonsMap().get(lessonDirName);
if (lesson == null) {
return;
}
Task task = lesson.getTask(taskDirName);
if (task == null) {
return;
}
TaskFile taskFile = task.getTaskFile(virtualFile.getName());
if (taskFile == null) {
return;
}
List<TaskWindow> taskWindows = taskFile.getTaskWindows();
for (TaskWindow taskWindow : taskWindows) {
taskWindow.drawHighlighter(editor);
}
}
public static void addDocumentListener(Document document, StudyDocumentListener listener) {
myDocumentListeners.put(document, listener);
}
public static StudyDocumentListener getListener(Document document) {
return myDocumentListeners.get(document);
}
public static void removeListener(Document document) {
myDocumentListeners.remove(document);
}
public static boolean indexIsValid(int index, List<TaskWindow> collection) {
int size = collection.size();
return index >= 0 && index < size;
}
}
@@ -0,0 +1,71 @@
package org.jetbrains.plugins.coursecreator;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.editor.event.DocumentAdapter;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.impl.event.DocumentEventImpl;
import org.jetbrains.plugins.coursecreator.format.TaskFile;
import org.jetbrains.plugins.coursecreator.format.TaskWindow;
/**
* author: liana
* data: 7/16/14.
* Listens changes in study files and updates
* coordinates of all the windows in current task file
*/
public abstract class StudyDocumentListener extends DocumentAdapter {
private final TaskFile myTaskFile;
private int oldLine;
private int oldLineStartOffset;
private TaskWindow myTaskWindow;
public StudyDocumentListener(TaskFile taskFile) {
myTaskFile = taskFile;
}
//remembering old end before document change because of problems
// with fragments containing "\n"
@Override
public void beforeDocumentChange(DocumentEvent e) {
int offset = e.getOffset();
int oldEnd = offset + e.getOldLength();
Document document = e.getDocument();
oldLine = document.getLineNumber(oldEnd);
oldLineStartOffset = document.getLineStartOffset(oldLine);
int line = document.getLineNumber(offset);
int offsetInLine = offset - document.getLineStartOffset(line);
LogicalPosition pos = new LogicalPosition(line, offsetInLine);
myTaskWindow = myTaskFile.getTaskWindow(document, pos);
}
@Override
public void documentChanged(DocumentEvent e) {
if (e instanceof DocumentEventImpl) {
if (!needModify()) {
return;
}
DocumentEventImpl event = (DocumentEventImpl)e;
Document document = e.getDocument();
int offset = e.getOffset();
int change = event.getNewLength() - event.getOldLength();
if (myTaskWindow != null) {
updateTaskWindowLength(e.getNewFragment(), myTaskWindow, change);
}
int newEnd = offset + event.getNewLength();
int newLine = document.getLineNumber(newEnd);
int lineChange = newLine - oldLine;
myTaskFile.incrementLines(oldLine + 1, lineChange);
int newEndOffsetInLine = offset + e.getNewLength() - document.getLineStartOffset(newLine);
int oldEndOffsetInLine = offset + e.getOldLength() - oldLineStartOffset;
myTaskFile.updateLine(lineChange, oldLine, newEndOffsetInLine, oldEndOffsetInLine);
}
}
protected abstract void updateTaskWindowLength(CharSequence fragment, TaskWindow taskWindow, int change);
protected abstract boolean needModify();
}
@@ -0,0 +1,105 @@
package org.jetbrains.plugins.coursecreator.actions;
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import org.jetbrains.plugins.coursecreator.CCProjectService;
import org.jetbrains.plugins.coursecreator.format.*;
import org.jetbrains.plugins.coursecreator.ui.CreateTaskWindowDialog;
public class AddTaskWindow extends DumbAwareAction {
public AddTaskWindow() {
super("Add task window","Add task window", null);
}
@Override
public void actionPerformed(AnActionEvent e) {
final Project project = e.getData(CommonDataKeys.PROJECT);
if (project == null) {
return;
}
final PsiFile file = CommonDataKeys.PSI_FILE.getData(e.getDataContext());
if (file == null) return;
final Editor editor = CommonDataKeys.EDITOR.getData(e.getDataContext());
if (editor == null) return;
final SelectionModel model = editor.getSelectionModel();
final Document document = PsiDocumentManager.getInstance(project).getDocument(file);
if (document == null) return;
final int start = model.getSelectionStart();
final int end = model.getSelectionEnd();
final int lineNumber = document.getLineNumber(start);
final int length = end - start;
int realStart = start - document.getLineStartOffset(lineNumber);
final CCProjectService service = CCProjectService.getInstance(project);
final Course course = service.getCourse();
final PsiDirectory taskDir = file.getContainingDirectory();
final PsiDirectory lessonDir = taskDir.getParent();
if (lessonDir == null) return;
final Lesson lesson = course.getLesson(lessonDir.getName());
final Task task = lesson.getTask(taskDir.getName());
final TaskFile taskFile = task.getTaskFile(file.getName());
final TaskWindow taskWindow = new TaskWindow(lineNumber, realStart, length, model.getSelectedText());
CreateTaskWindowDialog dlg = new CreateTaskWindowDialog(project, taskWindow, lesson.getIndex(), task.getIndex(), file.getVirtualFile().getNameWithoutExtension(), taskFile.getTaskWindows().size() + 1);
dlg.show();
if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) {
return;
}
int index = taskFile.getTaskWindows().size() + 1;
taskFile.addTaskWindow(taskWindow, index);
taskWindow.drawHighlighter(editor);
DaemonCodeAnalyzerImpl.getInstance(project).restart(file);
}
@Override
public void update(AnActionEvent event) {
final Presentation presentation = event.getPresentation();
final Project project = event.getData(CommonDataKeys.PROJECT);
if (project == null) {
presentation.setVisible(false);
presentation.setEnabled(false);
return;
}
final Editor editor = CommonDataKeys.EDITOR.getData(event.getDataContext());
final PsiFile file = CommonDataKeys.PSI_FILE.getData(event.getDataContext());
if (editor == null || file == null) {
presentation.setVisible(false);
presentation.setEnabled(false);
return;
}
if (!editor.getSelectionModel().hasSelection()) {
presentation.setVisible(false);
presentation.setEnabled(false);
return;
}
final CCProjectService service = CCProjectService.getInstance(project);
final Course course = service.getCourse();
final PsiDirectory taskDir = file.getContainingDirectory();
final PsiDirectory lessonDir = taskDir.getParent();
if (lessonDir == null) return;
final Lesson lesson = course.getLesson(lessonDir.getName());
final Task task = lesson.getTask(taskDir.getName());
if (task == null) {
presentation.setVisible(false);
presentation.setEnabled(false);
return;
}
presentation.setVisible(true);
presentation.setEnabled(true);
}
}
@@ -0,0 +1,198 @@
package org.jetbrains.plugins.coursecreator.actions;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.intellij.icons.AllIcons;
import com.intellij.ide.projectView.ProjectView;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.util.io.ZipUtil;
import org.jetbrains.plugins.coursecreator.CCProjectService;
import org.jetbrains.plugins.coursecreator.StudyDocumentListener;
import org.jetbrains.plugins.coursecreator.format.*;
import org.jetbrains.plugins.coursecreator.ui.CreateCourseArchiveDialog;
import java.io.*;
import java.util.*;
import java.util.zip.ZipOutputStream;
public class CreateCourseArchive extends DumbAwareAction {
private static final Logger LOG = Logger.getInstance(CreateCourseArchive.class.getName());
String myZipName;
String myLocationDir;
public void setZipName(String zipName) {
myZipName = zipName;
}
public void setLocationDir(String locationDir) {
myLocationDir = locationDir;
}
public CreateCourseArchive() {
super("Generate course archive", "Generate course archive", AllIcons.FileTypes.Archive);
}
@Override
public void actionPerformed(AnActionEvent e) {
final Project project = e.getData(CommonDataKeys.PROJECT);
if (project == null) {
return;
}
final CCProjectService service = CCProjectService.getInstance(project);
final Course course = service.getCourse();
if (course == null) return;
CreateCourseArchiveDialog dlg = new CreateCourseArchiveDialog(project, this);
dlg.show();
if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) {
return;
}
final VirtualFile baseDir = project.getBaseDir();
final Map<String, Lesson> lessons = course.getLessonsMap();
//List<FileEditor> editorList = new ArrayList<FileEditor>();
Map<VirtualFile, TaskFile> taskFiles = new HashMap<VirtualFile, TaskFile>();
for (Map.Entry<String, Lesson> lesson : lessons.entrySet()) {
final VirtualFile lessonDir = baseDir.findChild(lesson.getKey());
if (lessonDir == null) continue;
for (Map.Entry<String, Task> task : lesson.getValue().myTasksMap.entrySet()) {
final VirtualFile taskDir = lessonDir.findChild(task.getKey());
if (taskDir == null) continue;
for (Map.Entry<String, TaskFile> entry : task.getValue().task_files.entrySet()) {
final VirtualFile file = taskDir.findChild(entry.getKey());
if (file == null) continue;
final Document document = FileDocumentManager.getInstance().getDocument(file);
if (document == null) continue;
final TaskFile taskFile = entry.getValue();
document.addDocumentListener(new InsertionListener(taskFile));
taskFiles.put(file, taskFile);
taskFile.setTrackChanges(false);
Collections.sort(taskFile.getTaskWindows());
for (int i = taskFile.getTaskWindows().size() - 1; i >=0 ; i--) {
final TaskWindow taskWindow = taskFile.getTaskWindows().get(i);
final String taskText = taskWindow.getTaskText();
final int lineStartOffset = document.getLineStartOffset(taskWindow.line);
final int offset = lineStartOffset + taskWindow.start;
CommandProcessor.getInstance().executeCommand(project, new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
document.replaceString(offset, offset + taskWindow.getReplacementLength(), taskText);
FileDocumentManager.getInstance().saveDocument(document);
}
});
}
}, "x", "qwe");
}
}
}
}
generateJson(project);
try {
File zipFile = new File(myLocationDir, myZipName + ".zip");
ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));
for (Map.Entry<String, Lesson> entry : lessons.entrySet()) {
final VirtualFile lessonDir = baseDir.findChild(entry.getKey());
if (lessonDir == null) continue;
ZipUtil.addFileOrDirRecursively(zos, null, new File(lessonDir.getPath()), lessonDir.getName(), null, null);
}
ZipUtil.addFileOrDirRecursively(zos, null, new File(baseDir.getPath(), "hints"), "hints", null, null);
ZipUtil.addFileOrDirRecursively(zos, null, new File(baseDir.getPath(), "course.json"), "course.json", null, null);
ZipUtil.addFileOrDirRecursively(zos, null, new File(baseDir.getPath(), "test_helper.py"), "test_helper.py", null, null);
zos.close();
Messages.showInfoMessage("Course archive was saved to " + zipFile.getPath(), "Course Archive Was Created Successfully");
}
catch (IOException e1) {
LOG.error(e1);
}
for (Map.Entry<VirtualFile, TaskFile> entry: taskFiles.entrySet()) {
TaskFile value = entry.getValue();
final Document document = FileDocumentManager.getInstance().getDocument(entry.getKey());
if (document == null) {
continue;
}
for (final TaskWindow taskWindow : value.getTaskWindows()){
final int lineStartOffset = document.getLineStartOffset(taskWindow.line);
final int offset = lineStartOffset + taskWindow.start;
CommandProcessor.getInstance().executeCommand(project, new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
document.replaceString(offset, offset + taskWindow.length, taskWindow.getPossibleAnswer());
FileDocumentManager.getInstance().saveDocument(document);
}
});
}
}, "x", "qwe");
}
value.setTrackChanges(true);
}
VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
ProjectView.getInstance(project).refresh();
}
private void generateJson(Project project) {
final CCProjectService service = CCProjectService.getInstance(project);
final Course course = service.getCourse();
final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create();
final String json = gson.toJson(course);
final File courseJson = new File(project.getBasePath(), "course.json");
FileWriter writer = null;
try {
writer = new FileWriter(courseJson);
writer.write(json);
}
catch (IOException e) {
Messages.showErrorDialog(e.getMessage(), "Failed to Generate Json");
LOG.info(e);
}
catch (Exception e) {
Messages.showErrorDialog(e.getMessage(), "Failed to Generate Json");
LOG.info(e);
}
finally {
try {
if (writer != null) {
writer.close();
}
}
catch (IOException e1) {
//close silently
}
}
}
private class InsertionListener extends StudyDocumentListener {
public InsertionListener(TaskFile taskFile) {
super(taskFile);
}
@Override
protected void updateTaskWindowLength(CharSequence fragment, TaskWindow taskWindow, int change) {
//we don't need to update task window length
}
@Override
protected boolean needModify() {
return true;
}
}
}
@@ -0,0 +1,89 @@
package org.jetbrains.plugins.coursecreator.actions;
import com.intellij.ide.IdeView;
import com.intellij.ide.util.DirectoryChooserUtil;
import com.intellij.ide.util.DirectoryUtil;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.psi.PsiDirectory;
import com.intellij.util.PlatformIcons;
import org.jetbrains.plugins.coursecreator.CCProjectService;
import org.jetbrains.plugins.coursecreator.format.Course;
import org.jetbrains.plugins.coursecreator.format.Lesson;
public class CreateLesson extends DumbAwareAction {
public CreateLesson() {
super("Lesson", "Create new Lesson", PlatformIcons.DIRECTORY_CLOSED_ICON);
}
@Override
public void actionPerformed(AnActionEvent e) {
final IdeView view = e.getData(LangDataKeys.IDE_VIEW);
final Project project = e.getData(CommonDataKeys.PROJECT);
if (view == null || project == null) {
return;
}
final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view);
if (directory == null) return;
final CCProjectService service = CCProjectService.getInstance(project);
final Course course = service.getCourse();
final int size = course.getLessons().size();
final String lessonName = Messages.showInputDialog("Name:", "Lesson Name", null, "lesson" + (size+1), null);
if (lessonName == null) return;
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
final PsiDirectory lessonDirectory = DirectoryUtil.createSubdirectories("lesson" + (size+1), directory, "\\/");
if (lessonDirectory != null) {
view.selectElement(lessonDirectory);
final Lesson lesson = new Lesson(lessonName);
lesson.setIndex(size + 1);
course.addLesson(lesson, lessonDirectory);
}
}
});
}
@Override
public void update(AnActionEvent event) {
final Presentation presentation = event.getPresentation();
final Project project = event.getData(CommonDataKeys.PROJECT);
if (project == null) {
presentation.setVisible(false);
presentation.setEnabled(false);
return;
}
final IdeView view = event.getData(LangDataKeys.IDE_VIEW);
if (view == null) {
presentation.setVisible(false);
presentation.setEnabled(false);
return;
}
final PsiDirectory[] directories = view.getDirectories();
if (directories.length == 0) {
presentation.setVisible(false);
presentation.setEnabled(false);
return;
}
final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view);
if (directory != null && !project.getBaseDir().equals(directory.getVirtualFile())) {
presentation.setVisible(false);
presentation.setEnabled(false);
return;
}
presentation.setVisible(true);
presentation.setEnabled(true);
}
}
@@ -0,0 +1,122 @@
package org.jetbrains.plugins.coursecreator.actions;
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.util.DirectoryChooserUtil;
import com.intellij.ide.util.DirectoryUtil;
import com.intellij.ide.util.EditorHelper;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.util.PlatformIcons;
import org.jetbrains.plugins.coursecreator.CCProjectService;
import org.jetbrains.plugins.coursecreator.format.Course;
import org.jetbrains.plugins.coursecreator.format.Lesson;
import org.jetbrains.plugins.coursecreator.format.Task;
public class CreateTask extends DumbAwareAction {
public CreateTask() {
super("Task", "Create new Task", PlatformIcons.DIRECTORY_CLOSED_ICON);
}
@Override
public void actionPerformed(AnActionEvent e) {
final IdeView view = e.getData(LangDataKeys.IDE_VIEW);
final Project project = e.getData(CommonDataKeys.PROJECT);
if (view == null || project == null) {
return;
}
final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view);
if (directory == null) return;
final CCProjectService service = CCProjectService.getInstance(project);
final Course course = service.getCourse();
final Lesson lesson = course.getLesson(directory.getName());
final int size = lesson.getTasklist().size();
final String taskName = Messages.showInputDialog("Name:", "Task Name", null, "task" + (size + 1), null);
if (taskName == null) return;
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
final PsiDirectory taskDirectory = DirectoryUtil.createSubdirectories("task" + (size + 1), directory, "\\/");
if (taskDirectory != null) {
final FileTemplate template = FileTemplateManager.getInstance().getInternalTemplate("task.html");
final FileTemplate testsTemplate = FileTemplateManager.getInstance().getInternalTemplate("tests");
final FileTemplate taskTemplate = FileTemplateManager.getInstance().getInternalTemplate("task.py");
try {
final PsiElement taskFile = FileTemplateUtil.createFromTemplate(template, "task.html", null, taskDirectory);
final PsiElement testsFile = FileTemplateUtil.createFromTemplate(testsTemplate, "tests.py", null, taskDirectory);
final PsiElement taskPyFile = FileTemplateUtil.createFromTemplate(taskTemplate, "file1" + ".py", null, taskDirectory);
final Task task = new Task(taskName);
task.addTaskFile(taskPyFile.getContainingFile().getName(), size + 1);
task.setIndex(size + 1);
lesson.addTask(task, taskDirectory);
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
EditorHelper.openInEditor(testsFile, false);
EditorHelper.openInEditor(taskPyFile, false);
view.selectElement(taskFile);
}
});
}
catch (Exception ignored) {
}
}
}
});
}
@Override
public void update(AnActionEvent event) {
final Presentation presentation = event.getPresentation();
final Project project = event.getData(CommonDataKeys.PROJECT);
if (project == null) {
presentation.setVisible(false);
presentation.setEnabled(false);
return;
}
final IdeView view = event.getData(LangDataKeys.IDE_VIEW);
if (view == null) {
presentation.setVisible(false);
presentation.setEnabled(false);
return;
}
final PsiDirectory[] directories = view.getDirectories();
if (directories.length == 0) {
presentation.setVisible(false);
presentation.setEnabled(false);
return;
}
final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view);
final CCProjectService service = CCProjectService.getInstance(project);
final Course course = service.getCourse();
if (course != null && directory != null && course.getLesson(directory.getName()) == null) {
presentation.setVisible(false);
presentation.setEnabled(false);
return;
}
presentation.setVisible(true);
presentation.setEnabled(true);
}
}
@@ -0,0 +1,110 @@
package org.jetbrains.plugins.coursecreator.actions;
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.util.DirectoryChooserUtil;
import com.intellij.ide.util.EditorHelper;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import icons.PythonPsiApiIcons;
import org.jetbrains.plugins.coursecreator.CCProjectService;
import org.jetbrains.plugins.coursecreator.format.Course;
import org.jetbrains.plugins.coursecreator.format.Lesson;
import org.jetbrains.plugins.coursecreator.format.Task;
public class CreateTaskFile extends DumbAwareAction {
public CreateTaskFile() {
super("Task File", "Create new Task File", PythonPsiApiIcons.PythonFile);
}
@Override
public void actionPerformed(AnActionEvent e) {
final IdeView view = e.getData(LangDataKeys.IDE_VIEW);
final Project project = e.getData(CommonDataKeys.PROJECT);
if (view == null || project == null) {
return;
}
final PsiDirectory taskDir = DirectoryChooserUtil.getOrChooseDirectory(view);
if (taskDir == null) return;
PsiDirectory lessonDir = taskDir.getParent();
if (lessonDir == null) {
return;
}
final CCProjectService service = CCProjectService.getInstance(project);
final Course course = service.getCourse();
final Lesson lesson = course.getLesson(lessonDir.getName());
final Task task = lesson.getTask(taskDir.getName());
final int index = task.getTaskFiles().size() + 1;
String generatedName = "file" + index;
final String taskFileName = Messages.showInputDialog("Name:", "Task File Name", null, generatedName, null);
if (taskFileName == null) return;
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
final FileTemplate taskTemplate = FileTemplateManager.getInstance().getInternalTemplate("task.py");
try {
final PsiElement taskPyFile = FileTemplateUtil.createFromTemplate(taskTemplate, taskFileName + ".py", null, taskDir);
task.addTaskFile(taskPyFile.getContainingFile().getName(), index);
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
EditorHelper.openInEditor(taskPyFile, false);
view.selectElement(taskPyFile);
}
});
}
catch (Exception ignored) {
}
}
});
}
@Override
public void update(AnActionEvent event) {
final Presentation presentation = event.getPresentation();
final Project project = event.getData(CommonDataKeys.PROJECT);
if (project == null) {
presentation.setVisible(false);
presentation.setEnabled(false);
return;
}
final IdeView view = event.getData(LangDataKeys.IDE_VIEW);
if (view == null) {
presentation.setVisible(false);
presentation.setEnabled(false);
return;
}
final PsiDirectory[] directories = view.getDirectories();
if (directories.length == 0) {
presentation.setVisible(false);
presentation.setEnabled(false);
return;
}
final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view);
final CCProjectService service = CCProjectService.getInstance(project);
final Course course = service.getCourse();
if (course != null && directory != null && !directory.getName().contains("task")) {
presentation.setVisible(false);
presentation.setEnabled(false);
return;
}
presentation.setVisible(true);
presentation.setEnabled(true);
}
}
@@ -0,0 +1,62 @@
package org.jetbrains.plugins.coursecreator.actions;
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.coursecreator.CCProjectService;
import org.jetbrains.plugins.coursecreator.format.*;
import java.util.List;
@SuppressWarnings("ComponentNotRegistered")
public class DeleteTaskWindow extends DumbAwareAction {
@NotNull
private final TaskWindow myTaskWindow;
public DeleteTaskWindow(@NotNull final TaskWindow taskWindow) {
super("Delete task window","Delete task window", null);
myTaskWindow = taskWindow;
}
@Override
public void actionPerformed(AnActionEvent e) {
final Project project = e.getData(PlatformDataKeys.PROJECT);
if (project == null) return;
final PsiFile file = CommonDataKeys.PSI_FILE.getData(e.getDataContext());
if (file == null) return;
final Editor editor = CommonDataKeys.EDITOR.getData(e.getDataContext());
if (editor == null) {
return;
}
final Document document = PsiDocumentManager.getInstance(project).getDocument(file);
if (document == null) return;
final CCProjectService service = CCProjectService.getInstance(project);
final Course course = service.getCourse();
final PsiDirectory taskDir = file.getContainingDirectory();
final PsiDirectory lessonDir = taskDir.getParent();
if (lessonDir == null) return;
final Lesson lesson = course.getLesson(lessonDir.getName());
final Task task = lesson.getTask(taskDir.getName());
final TaskFile taskFile = task.getTaskFile(file.getName());
final List<TaskWindow> taskWindows = taskFile.getTaskWindows();
if (taskWindows.contains(myTaskWindow)) {
myTaskWindow.removeResources(project);
taskWindows.remove(myTaskWindow);
editor.getMarkupModel().removeAllHighlighters();
CCProjectService.drawTaskWindows(file.getVirtualFile(), editor, course);
DaemonCodeAnalyzerImpl.getInstance(project).restart(file);
}
}
}
@@ -0,0 +1,44 @@
package org.jetbrains.plugins.coursecreator.actions;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.coursecreator.CCProjectService;
import org.jetbrains.plugins.coursecreator.format.*;
import org.jetbrains.plugins.coursecreator.ui.CreateTaskWindowDialog;
@SuppressWarnings("ComponentNotRegistered")
public class ShowTaskWindowText extends DumbAwareAction {
@NotNull
private final TaskWindow myTaskWindow;
public ShowTaskWindowText(@NotNull final TaskWindow taskWindow) {
super("Add task window","Add task window", null);
myTaskWindow = taskWindow;
}
@Override
public void actionPerformed(AnActionEvent e) {
final Project project = e.getData(PlatformDataKeys.PROJECT);
if (project == null) return;
final PsiFile file = CommonDataKeys.PSI_FILE.getData(e.getDataContext());
if (file == null) return;
final CCProjectService service = CCProjectService.getInstance(project);
final Course course = service.getCourse();
final PsiDirectory taskDir = file.getContainingDirectory();
final PsiDirectory lessonDir = taskDir.getParent();
if (lessonDir == null) return;
final Lesson lesson = course.getLesson(lessonDir.getName());
final Task task = lesson.getTask(taskDir.getName());
final TaskFile taskFile = task.getTaskFile(file.getName());
//TODO: copy task window and return if modification canceled
CreateTaskWindowDialog dlg = new CreateTaskWindowDialog(project, myTaskWindow, lesson.getIndex(), task.getIndex(), file.getVirtualFile().getNameWithoutExtension(), taskFile.getTaskWindows().size() + 1);
dlg.show();
}
}
@@ -0,0 +1,55 @@
package org.jetbrains.plugins.coursecreator.format;
import com.google.gson.annotations.Expose;
import com.intellij.psi.PsiDirectory;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Course {
@Expose public List<Lesson> lessons = new ArrayList<Lesson>();
@Expose public String description;
@Expose public String name;
@Expose public String author;
public Map<String, Lesson> myLessonsMap = new HashMap<String, Lesson>();
public Map<String, Lesson> getLessonsMap() {
return myLessonsMap;
}
public Lesson getLesson(@NotNull final String name) {
return myLessonsMap.get(name);
}
public Course() {
}
public Course(@NotNull final String name, @NotNull final String author, @NotNull final String description) {
this.description = description;
this.name = name;
this.author = author;
}
public List<Lesson> getLessons() {
return lessons;
}
public void addLesson(@NotNull final Lesson lesson, @NotNull final PsiDirectory directory) {
lessons.add(lesson);
myLessonsMap.put(directory.getName(), lesson);
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
}
@@ -0,0 +1,45 @@
package org.jetbrains.plugins.coursecreator.format;
import com.google.gson.annotations.Expose;
import com.intellij.psi.PsiDirectory;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Lesson {
@Expose public String name;
@Expose public List<Task> task_list = new ArrayList<Task>();
public int myIndex;
public Map<String, Task> myTasksMap = new HashMap<String, Task>();
public Lesson() {}
public Lesson(@NotNull final String name) {
this.name = name;
}
public void addTask(@NotNull final Task task, PsiDirectory taskDirectory) {
myTasksMap.put(taskDirectory.getName(), task);
task_list.add(task);
}
public Task getTask(@NotNull final String name) {
return myTasksMap.get(name);
}
public List<Task> getTasklist() {
return task_list;
}
public void setIndex(int index) {
myIndex = index;
}
public int getIndex() {
return myIndex;
}
}
@@ -0,0 +1,41 @@
package org.jetbrains.plugins.coursecreator.format;
import com.google.gson.annotations.Expose;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
public class Task {
@Expose public String name;
@Expose public Map<String, TaskFile> task_files = new HashMap<String, TaskFile>();
public int myIndex;
public Task() {}
public Task(@NotNull final String name) {
this.name = name;
}
public int getIndex() {
return myIndex;
}
public void addTaskFile(@NotNull final String name, int index) {
TaskFile taskFile = new TaskFile();
taskFile.setIndex(index);
task_files.put(name, taskFile);
}
public TaskFile getTaskFile(@NotNull final String name) {
return task_files.get(name);
}
public void setIndex(int index) {
myIndex = index;
}
public Map<String, TaskFile> getTaskFiles() {
return task_files;
}
}
@@ -0,0 +1,111 @@
package org.jetbrains.plugins.coursecreator.format;
import com.google.gson.annotations.Expose;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.LogicalPosition;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.coursecreator.CCProjectService;
import java.util.ArrayList;
import java.util.List;
public class TaskFile {
@Expose public List<TaskWindow> task_windows = new ArrayList<TaskWindow>();
public int myIndex;
public boolean myTrackChanges = true;
public boolean isTrackChanges() {
return myTrackChanges;
}
public void setTrackChanges(boolean trackChanges) {
myTrackChanges = trackChanges;
}
public TaskFile() {}
public void addTaskWindow(@NotNull final TaskWindow taskWindow, int index) {
taskWindow.setIndex(index);
task_windows.add(taskWindow);
}
public List<TaskWindow> getTaskWindows() {
return task_windows;
}
public void setIndex(int index) {
myIndex = index;
}
/**
* @param pos position in editor
* @return task window located in specified position or null if there is no task window in this position
*/
@Nullable
public TaskWindow getTaskWindow(@NotNull final Document document, @NotNull final LogicalPosition pos) {
int line = pos.line;
if (line >= document.getLineCount()) {
return null;
}
int column = pos.column;
int offset = document.getLineStartOffset(line) + column;
for (TaskWindow tw : task_windows) {
if (tw.getLine() <= line) {
int twStartOffset = tw.getRealStartOffset(document);
final int length = tw.getReplacementLength() > 0 ? tw.getReplacementLength() : 0;
int twEndOffset = twStartOffset + length;
if (twStartOffset <= offset && offset <= twEndOffset) {
return tw;
}
}
}
return null;
}
/**
* Updates task window lines
*
* @param startLine lines greater than this line and including this line will be updated
* @param change change to be added to line numbers
*/
public void incrementLines(int startLine, int change) {
for (TaskWindow taskTaskWindow : task_windows) {
if (taskTaskWindow.getLine() >= startLine) {
taskTaskWindow.setLine(taskTaskWindow.getLine() + change);
}
}
}
/**
* Updates windows in specific line
*
* @param lineChange change in line number
* @param line line to be updated
* @param newEndOffsetInLine distance from line start to end of inserted fragment
* @param oldEndOffsetInLine distance from line start to end of changed fragment
*/
public void updateLine(int lineChange, int line, int newEndOffsetInLine, int oldEndOffsetInLine) {
for (TaskWindow w : task_windows) {
if ((w.getLine() == line) && (w.getStart() >= oldEndOffsetInLine)) {
int distance = w.getStart() - oldEndOffsetInLine;
boolean coveredByPrevTW = false;
int prevIndex = w.getIndex() - 1;
if (CCProjectService.indexIsValid(prevIndex, task_windows)) {
TaskWindow prevTW = task_windows.get(prevIndex);
if (prevTW.getLine() == line) {
int endOffset = prevTW.getStart() + prevTW.getLength();
if (endOffset >= newEndOffsetInLine) {
coveredByPrevTW = true;
}
}
}
if (lineChange != 0 || newEndOffsetInLine <= w.getStart() || coveredByPrevTW) {
w.setStart(distance + newEndOffsetInLine);
w.setLine(line + lineChange);
}
}
}
}
}
@@ -0,0 +1,133 @@
package org.jetbrains.plugins.coursecreator.format;
import com.google.gson.annotations.Expose;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.markup.HighlighterLayer;
import com.intellij.openapi.editor.markup.HighlighterTargetArea;
import com.intellij.openapi.editor.markup.RangeHighlighter;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.coursecreator.CCProjectService;
import java.io.File;
public class TaskWindow implements Comparable{
@Expose public int line;
@Expose public int start;
@Expose public String hint;
@Expose public String possible_answer;
@Expose public int length;
public String myTaskText;
public int myReplacementLength;
public int myIndex;
public TaskWindow() {}
public TaskWindow(int line, int start, int length, String selectedText) {
this.line = line;
this.start = start;
myReplacementLength = length;
this.possible_answer = selectedText;
}
public void setTaskText(@NotNull final String taskText) {
myTaskText = taskText;
length = myTaskText.length();
}
public String getTaskText() {
return myTaskText;
}
public int getReplacementLength() {
return myReplacementLength;
}
public void setHint(String hint) {
this.hint = hint;
}
public String getHintName() {
return hint;
}
public void removeResources(@NotNull final Project project) {
if (hint != null) {
VirtualFile hints = project.getBaseDir().findChild("hints");
if (hints == null) {
return;
}
File hintFile = new File(hints.getPath(), hint);
CCProjectService.deleteProjectFile(hintFile, project);
}
}
public void drawHighlighter(@NotNull final Editor editor) {
int startOffset = editor.getDocument().getLineStartOffset(line) + start;
int endOffset = startOffset + myReplacementLength;
TextAttributes defaultTestAttributes =
EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.LIVE_TEMPLATE_ATTRIBUTES);
RangeHighlighter highlighter =
editor.getMarkupModel().addRangeHighlighter(startOffset, endOffset, HighlighterLayer.LAST + 1, defaultTestAttributes,
HighlighterTargetArea.EXACT_RANGE);
highlighter.setGreedyToLeft(true);
highlighter.setGreedyToRight(true);
}
public int getIndex() {
return myIndex;
}
public void setIndex(int index) {
myIndex = index;
}
public void setReplacementLength(int replacementLength) {
myReplacementLength = replacementLength;
}
public int getLine() {
return line;
}
public int getRealStartOffset(Document document) {
return document.getLineStartOffset(line) + start;
}
public void setLine(int line) {
this.line = line;
}
public int getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
@Override
public int compareTo(Object o) {
TaskWindow taskWindow = (TaskWindow)o;
int lineDiff = line - taskWindow.line;
if (lineDiff == 0) {
return start - taskWindow.start;
}
return lineDiff;
}
public String getPossibleAnswer() {
return possible_answer;
}
public int getLength() {
return length;
}
}
@@ -0,0 +1,74 @@
package org.jetbrains.plugins.coursecreator.highlighting;
import com.intellij.codeHighlighting.Pass;
import com.intellij.codeInsight.daemon.LineMarkerInfo;
import com.intellij.codeInsight.daemon.LineMarkerProvider;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.coursecreator.CCProjectService;
import org.jetbrains.plugins.coursecreator.format.*;
import java.util.Collection;
import java.util.List;
public class CCTaskLineMarkerProvider implements LineMarkerProvider {
private static final Logger LOG = Logger.getInstance(CCTaskLineMarkerProvider.class.getName());
@Nullable
@Override
public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) {
return null;
}
@Override
public void collectSlowLineMarkers(@NotNull List<PsiElement> elements, @NotNull final Collection<LineMarkerInfo> result) {
for (PsiElement element : elements) {
if (element instanceof PsiFile) {
final Project project = element.getProject();
final Course course = CCProjectService.getInstance(project).getCourse();
final String taskFileName = ((PsiFile) element).getName();
final PsiDirectory taskDir = ((PsiFile) element).getParent();
if (taskDir == null) continue;
final String taskDirName = taskDir.getName();
final PsiDirectory lessonDir = taskDir.getParentDirectory();
if (lessonDir == null) continue;
final String lessonDirName = lessonDir.getName();
final Lesson lesson = course.getLesson(lessonDirName);
if (lesson == null) continue;
final Task task = lesson.getTask(taskDirName);
final TaskFile taskFile = task.getTaskFile(taskFileName);
if (taskFile == null) continue;
final Document document = PsiDocumentManager.getInstance(project).getDocument((PsiFile) element);
if (document == null) continue;
for (final TaskWindow taskWindow : taskFile.getTaskWindows()) {
if (taskWindow.line > document.getLineCount()) continue;
final int lineStartOffset = document.getLineStartOffset(taskWindow.line);
final int offset = lineStartOffset + taskWindow.start;
if (offset > document.getTextLength()) continue;
final TextRange textRange = TextRange.create(offset, offset + taskWindow.getReplacementLength());
@SuppressWarnings("unchecked")
final LineMarkerInfo info = new LineMarkerInfo(element, textRange,
IconLoader.getIcon("/icons/gutter.png"), Pass.UPDATE_OVERRIDEN_MARKERS,
null, null, GutterIconRenderer.Alignment.CENTER) {
@Nullable
@Override
public GutterIconRenderer createGutterRenderer() {
return new TaskTextGutter(taskWindow, this);
}
};
result.add(info);
}
}
}
}
}
@@ -0,0 +1,60 @@
package org.jetbrains.plugins.coursecreator.highlighting;
import com.intellij.codeInsight.daemon.LineMarkerInfo;
import com.intellij.openapi.actionSystem.ActionGroup;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.util.IconLoader;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.coursecreator.actions.DeleteTaskWindow;
import org.jetbrains.plugins.coursecreator.actions.ShowTaskWindowText;
import org.jetbrains.plugins.coursecreator.format.TaskWindow;
import javax.swing.*;
public class TaskTextGutter extends LineMarkerInfo.LineMarkerGutterIconRenderer {
@NotNull
private final TaskWindow myTaskWindow;
public TaskTextGutter(@NotNull final TaskWindow taskWindow, LineMarkerInfo lineMarkerInfo) {
super(lineMarkerInfo);
myTaskWindow = taskWindow;
}
@NotNull
@Override
public Icon getIcon() {
return IconLoader.getIcon("/icons/gutter.png");
}
@Override
public boolean equals(Object o) {
return this == o || o instanceof TaskTextGutter
&& myTaskWindow.getTaskText().equals(((TaskTextGutter) o).getTaskWindow().getTaskText());
}
@NotNull
public TaskWindow getTaskWindow() {
return myTaskWindow;
}
@Override
public int hashCode() {
return myTaskWindow.hashCode();
}
@Nullable
@Override
public AnAction getClickAction() {
return new ShowTaskWindowText(myTaskWindow);
}
@Nullable
@Override
public ActionGroup getPopupMenuActions() {
DefaultActionGroup group = new DefaultActionGroup();
group.add(new DeleteTaskWindow(myTaskWindow));
return group;
}
}
@@ -0,0 +1,63 @@
package org.jetbrains.plugins.coursecreator.projectView;
import com.intellij.ide.projectView.PresentationData;
import com.intellij.ide.projectView.ViewSettings;
import com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiDirectory;
import com.intellij.ui.SimpleTextAttributes;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.coursecreator.CCProjectService;
import org.jetbrains.plugins.coursecreator.format.Course;
import org.jetbrains.plugins.coursecreator.format.Lesson;
import org.jetbrains.plugins.coursecreator.format.Task;
public class CCDirectoryNode extends PsiDirectoryNode {
private final PsiDirectory myValue;
private final Project myProject;
public CCDirectoryNode(@NotNull final Project project,
PsiDirectory value,
ViewSettings viewSettings) {
super(project, value, viewSettings);
myValue = value;
myProject = project;
}
@Override
protected void updateImpl(PresentationData data) {
String valueName = myValue.getName();
final Course course = CCProjectService.getInstance(myProject).getCourse();
if (course == null) return;
if (myProject.getBaseDir().equals(myValue.getVirtualFile())) {
data.clearText();
data.addText(valueName, SimpleTextAttributes.REGULAR_ATTRIBUTES);
data.addText(" (" + course.getName() + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES);
return;
}
final Lesson lesson = course.getLesson(valueName);
if (lesson != null) {
data.clearText();
data.addText(valueName, SimpleTextAttributes.REGULAR_ATTRIBUTES);
data.addText(" (" + lesson.name + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES);
return;
}
else {
final PsiDirectory parentDir = myValue.getParentDirectory();
if (parentDir != null) {
final Lesson parentLesson = course.getLesson(parentDir.getName());
if (parentLesson != null) {
final Task task = parentLesson.getTask(valueName);
if (task != null) {
data.clearText();
data.addText(valueName, SimpleTextAttributes.REGULAR_ATTRIBUTES);
data.addText(" (" + task.name + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES);
return;
}
}
}
}
data.setPresentableText(valueName);
}
}
@@ -0,0 +1,55 @@
package org.jetbrains.plugins.coursecreator.projectView;
import com.intellij.ide.projectView.TreeStructureProvider;
import com.intellij.ide.projectView.ViewSettings;
import com.intellij.ide.util.treeView.AbstractTreeNode;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiDirectory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.coursecreator.CCProjectService;
import java.util.ArrayList;
import java.util.Collection;
public class CCTreeStructureProvider implements TreeStructureProvider, DumbAware {
@NotNull
@Override
public Collection<AbstractTreeNode> modify(@NotNull AbstractTreeNode parent,
@NotNull Collection<AbstractTreeNode> children,
ViewSettings settings) {
if (!needModify(parent)) {
return children;
}
Collection<AbstractTreeNode> nodes = new ArrayList<AbstractTreeNode>();
for (AbstractTreeNode node : children) {
Project project = node.getProject();
if (project != null) {
if (node.getValue() instanceof PsiDirectory) {
PsiDirectory directory = (PsiDirectory) node.getValue();
nodes.add(new CCDirectoryNode(project, directory, settings));
} else {
nodes.add(node);
}
}
}
return nodes;
}
private static boolean needModify(@NotNull final AbstractTreeNode parent) {
Project project = parent.getProject();
if (project != null) {
if (CCProjectService.getInstance(project).getCourse() == null) {
return false;
}
}
return true;
}
@Nullable
@Override
public Object getData(Collection<AbstractTreeNode> selected, String dataName) {
return null;
}
}
@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.plugins.coursecreator.ui.CCNewProjectPanel">
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="3" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="fd520" class="javax.swing.JLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
<preferred-size width="95" height="-1"/>
</grid>
</constraints>
<properties>
<text value="Name:"/>
</properties>
</component>
<component id="7e88" class="javax.swing.JTextField" binding="myName">
<constraints>
<grid row="0" column="1" row-span="1" col-span="2" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
<component id="ec56c" class="javax.swing.JLabel">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="9" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Description"/>
</properties>
</component>
<component id="2e2d7" class="javax.swing.JLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
<preferred-size width="95" height="-1"/>
</grid>
</constraints>
<properties>
<text value="Author:"/>
</properties>
</component>
<component id="41fe6" class="javax.swing.JTextField" binding="myAuthorField">
<constraints>
<grid row="1" column="1" row-span="1" col-span="2" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
<grid id="12f06" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="2" column="1" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="line">
<color color="-6709600"/>
</border>
<children>
<component id="389a7" class="javax.swing.JTextArea" binding="myDescription">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
<preferred-size width="150" height="50"/>
</grid>
</constraints>
<properties/>
</component>
</children>
</grid>
</children>
</grid>
</form>
@@ -0,0 +1,59 @@
package org.jetbrains.plugins.coursecreator.ui;
import com.intellij.facet.ui.FacetValidatorsManager;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.DocumentAdapter;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
public class CCNewProjectPanel {
private JPanel myPanel;
private JTextArea myDescription;
private JTextField myName;
private JTextField myAuthorField;
private FacetValidatorsManager myValidationManager;
public CCNewProjectPanel() {
final String userName = System.getProperty("user.name");
if (userName != null) {
myAuthorField.setText(userName);
}
myName.getDocument().addDocumentListener(new MyValidator());
myDescription.getDocument().addDocumentListener(new MyValidator());
myAuthorField.getDocument().addDocumentListener(new MyValidator());
}
public JPanel getMainPanel() {
return myPanel;
}
@NotNull
public String getName() {
return StringUtil.notNullize(myName.getText());
}
@NotNull
public String getDescription() {
return StringUtil.notNullize(myDescription.getText());
}
@NotNull
public String getAuthor() {
return StringUtil.notNullize(myAuthorField.getText());
}
public void registerValidators(FacetValidatorsManager manager) {
myValidationManager = manager;
}
private class MyValidator extends DocumentAdapter {
@Override
protected void textChanged(DocumentEvent e) {
myValidationManager.validate();
}
}
}
@@ -0,0 +1,40 @@
package org.jetbrains.plugins.coursecreator.ui;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.coursecreator.actions.CreateCourseArchive;
import javax.swing.*;
public class CreateCourseArchiveDialog extends DialogWrapper {
private CreateCourseArchivePanel myPanel;
private CreateCourseArchive myAction;
public CreateCourseArchiveDialog(@NotNull final Project project, CreateCourseArchive action) {
super(project);
setTitle("Create Course Archive");
myPanel = new CreateCourseArchivePanel(project, this);
myAction = action;
init();
}
@Nullable
@Override
protected JComponent createCenterPanel() {
return myPanel;
}
public void enableOKAction(boolean isEnabled) {
myOKAction.setEnabled(isEnabled);
}
@Override
protected void doOKAction() {
myAction.setZipName(myPanel.getZipName());
myAction.setLocationDir(myPanel.getLocationPath());
super.doOKAction();
}
}
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.plugins.coursecreator.ui.CreateCourseArchivePanel">
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<grid id="a3b77" layout-manager="GridLayoutManager" row-count="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="0" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="786af" class="javax.swing.JLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Name:"/>
</properties>
</component>
<component id="160bb" class="javax.swing.JTextField" binding="myNameField" default-binding="true">
<constraints>
<grid row="0" column="1" row-span="2" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
<component id="628ab" class="javax.swing.JLabel">
<constraints>
<grid row="1" column="0" row-span="2" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Location:"/>
</properties>
</component>
<component id="aab8" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myLocationField">
<constraints>
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<grid id="29be7" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="3" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="4fa15" class="javax.swing.JLabel" binding="myErrorIcon">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value=""/>
</properties>
</component>
<component id="4bdcf" class="javax.swing.JLabel" binding="myErrorLabel">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value=""/>
</properties>
</component>
</children>
</grid>
</children>
</grid>
</children>
</grid>
</form>
@@ -0,0 +1,65 @@
package org.jetbrains.plugins.coursecreator.ui;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
public class CreateCourseArchivePanel extends JPanel {
private JPanel myPanel;
private JTextField myNameField;
private TextFieldWithBrowseButton myLocationField;
private JLabel myErrorIcon;
private JLabel myErrorLabel;
private CreateCourseArchiveDialog myDlg;
public CreateCourseArchivePanel(@NotNull final Project project, CreateCourseArchiveDialog dlg) {
setLayout(new BorderLayout());
add(myPanel, BorderLayout.CENTER);
myErrorIcon.setIcon(AllIcons.Actions.Lightning);
setState(false);
myDlg = dlg;
myNameField.setText("course");
myLocationField.setText(project.getBasePath());
FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
myLocationField.addBrowseFolderListener("Choose location folder", null, project, descriptor);
myLocationField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String location = myLocationField.getText();
File file = new File(location);
if (!file.exists() || !file.isDirectory()) {
myDlg.enableOKAction(false);
setError("Invalid location");
}
myDlg.enableOKAction(true);
}
});
}
private void setState(boolean isVisible) {
myErrorIcon.setVisible(isVisible);
myErrorLabel.setVisible(isVisible);
}
private void setError(String message) {
myErrorLabel.setText(message);
setState(true);
}
public String getZipName() {
return myNameField.getText();
}
public String getLocationPath() {
return myLocationField.getText();
}
}
@@ -0,0 +1,153 @@
package org.jetbrains.plugins.coursecreator.ui;
import com.intellij.ide.projectView.ProjectView;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.ValidationInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.coursecreator.CCProjectService;
import org.jetbrains.plugins.coursecreator.format.TaskWindow;
import javax.swing.*;
import java.io.*;
public class CreateTaskWindowDialog extends DialogWrapper {
public static final String TITLE = "New Task Window";
private static final Logger LOG = Logger.getInstance(CreateTaskWindowDialog.class.getName());
private final TaskWindow myTaskWindow;
private final CreateTaskWindowPanel myPanel;
private final Project myProject;
public Project getProject() {
return myProject;
}
public CreateTaskWindowDialog(@NotNull final Project project, @NotNull final TaskWindow taskWindow, int lessonIndex,
int taskIndex, String taskFileName, int taskWindowIndex) {
super(project, true);
setTitle(TITLE);
myTaskWindow = taskWindow;
myPanel = new CreateTaskWindowPanel(this);
String generatedHintName = "lesson" + lessonIndex + "task" + taskIndex + taskFileName + "_" + taskWindowIndex;
myPanel.setGeneratedHintName(generatedHintName);
if (taskWindow.getHintName() != null) {
setHintText(project, taskWindow);
}
myProject = project;
String taskWindowTaskText = taskWindow.getTaskText();
myPanel.setTaskWindowText(taskWindowTaskText != null ? taskWindowTaskText : "");
String hintName = taskWindow.getHintName();
myPanel.setHintName(hintName != null ? hintName : "");
init();
initValidation();
}
private void setHintText(Project project, TaskWindow taskWindow) {
VirtualFile hints = project.getBaseDir().findChild("hints");
if (hints != null) {
File file = new File(hints.getPath(), taskWindow.getHintName());
StringBuilder hintText = new StringBuilder();
if (file.exists()) {
try {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
String line;
while ((line = bufferedReader.readLine()) != null) {
hintText.append(line).append("\n");
}
myPanel.doClick();
//myPanel.enableHint(true);
myPanel.setHintText(hintText.toString());
}
catch (FileNotFoundException e) {
LOG.error("created hint was not found", e);
}
catch (IOException e) {
LOG.error(e);
}
}
}
}
@Override
protected void doOKAction() {
String taskWindowText = myPanel.getTaskWindowText();
myTaskWindow.setTaskText(StringUtil.notNullize(taskWindowText));
if (myPanel.createHint()) {
String hintName = myPanel.getHintName();
myTaskWindow.setHint(hintName);
String hintText = myPanel.getHintText();
createHint(hintName, hintText);
}
super.doOKAction();
}
private void createHint(String hintName, String hintText) {
VirtualFile hintsDir = myProject.getBaseDir().findChild("hints");
if (hintsDir != null) {
File hintFile = new File(hintsDir.getPath(), hintName);
PrintWriter printWriter = null;
try {
printWriter = new PrintWriter(hintFile);
printWriter.print(hintText);
}
catch (FileNotFoundException e) {
//TODO:show error in UI
return;
}
finally {
if (printWriter != null) {
printWriter.close();
}
}
}
VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
ProjectView.getInstance(myProject).refresh();
}
public void deleteHint() {
VirtualFile hintsDir = myProject.getBaseDir().findChild("hints");
if (hintsDir != null) {
String hintName = myTaskWindow.getHintName();
if (hintName == null) {
return;
}
File hintFile = new File(hintsDir.getPath(), hintName);
if (hintFile.exists()) {
CCProjectService.deleteProjectFile(hintFile, myProject);
myTaskWindow.setHint(null);
myPanel.resetHint();
}
}
}
@Nullable
@Override
protected JComponent createCenterPanel() {
return myPanel;
}
@Nullable
@Override
public ValidationInfo doValidate() {
String name = myPanel.getHintName();
VirtualFile hintsDir = myProject.getBaseDir().findChild("hints");
if (hintsDir == null) {
return null;
}
VirtualFile child = hintsDir.findChild(name);
if (child == null) {
return null;
}
return myTaskWindow.getHintName() != null ? null : new ValidationInfo("Hint file with such filename already exists");
}
public void validateInput() {
super.initValidation();
}
}
@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.plugins.coursecreator.ui.CreateTaskWindowPanel">
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="4" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="aaa28" class="javax.swing.JLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="b712"/>
<text value="Text:"/>
</properties>
</component>
<component id="d2e2f" class="javax.swing.JLabel" binding="myHintNameLabel">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<enabled value="true"/>
<labelFor value="ddeb1"/>
<text value="Hint name:"/>
</properties>
</component>
<component id="ddeb1" class="javax.swing.JTextField" binding="myHintName">
<constraints>
<grid row="2" column="1" row-span="1" col-span="3" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties>
<text value=""/>
</properties>
</component>
<component id="d322a" class="javax.swing.JLabel" binding="myHintTextLabel">
<constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="d0efc"/>
<text value="Hint text:"/>
</properties>
</component>
<grid id="51a63" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="3" column="1" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="line">
<color color="-6709600"/>
</border>
<children>
<component id="d0efc" class="javax.swing.JTextArea" binding="myHintText">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
<preferred-size width="300" height="100"/>
</grid>
</constraints>
<properties/>
</component>
</children>
</grid>
<grid id="cbc70" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="0" column="1" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
<minimum-size width="150" height="-1"/>
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
<border type="line">
<color color="-6709600"/>
</border>
<children>
<component id="b712" class="javax.swing.JTextArea" binding="myTaskWindowText">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
<preferred-size width="300" height="100"/>
</grid>
</constraints>
<properties/>
</component>
</children>
</grid>
<component id="f86b4" class="javax.swing.JCheckBox" binding="myCreateHintCheckBox" default-binding="true">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Create hint"/>
</properties>
</component>
</children>
</grid>
</form>
@@ -0,0 +1,96 @@
package org.jetbrains.plugins.coursecreator.ui;
import com.intellij.ui.DocumentAdapter;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
public class CreateTaskWindowPanel extends JPanel {
private final CreateTaskWindowDialog myDialog;
private JPanel myPanel;
private JTextArea myTaskWindowText;
private JTextField myHintName;
private JTextArea myHintText;
private JCheckBox myCreateHintCheckBox;
private JLabel myHintNameLabel;
private JLabel myHintTextLabel;
private String myGeneratedHintName = "";
public CreateTaskWindowPanel(CreateTaskWindowDialog dialog) {
super(new BorderLayout());
add(myPanel, BorderLayout.CENTER);
myDialog = dialog;
enableHint(false);
myCreateHintCheckBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
int state = e.getStateChange();
// 1 for checked
enableHint(state == 1);
if (state == 2) {
myDialog.deleteHint();
}
}
});
myHintName.getDocument().addDocumentListener(new DocumentAdapter() {
@Override
protected void textChanged(DocumentEvent e) {
myDialog.validateInput();
}
});
}
public void enableHint(boolean isEnable) {
myHintName.setEnabled(isEnable);
myHintText.setEnabled(isEnable);
myHintNameLabel.setEnabled(isEnable);
myHintTextLabel.setEnabled(isEnable);
myHintName.setText(myGeneratedHintName);
}
public void setTaskWindowText(String taskWindowText) {
myTaskWindowText.setText(taskWindowText);
}
public void setHintName(String hintName) {
myHintName.setText(hintName);
}
public void setHintText(String hintText) {
myHintText.setText(hintText);
}
public String getTaskWindowText() {
return myTaskWindowText.getText();
}
public String getHintName() {
return myHintName.getText();
}
public String getHintText() {
return myHintText.getText();
}
public boolean createHint() {
return myHintName.isEnabled();
}
public void doClick() {
myCreateHintCheckBox.doClick();
}
public void resetHint() {
myHintName.setText("");
myHintText.setText("");
}
public void setGeneratedHintName(String generatedHintName) {
myGeneratedHintName = generatedHintName;
}
}
@@ -0,0 +1,28 @@
package com.intellij.openapi.application;
import com.intellij.util.ThreeState;
// see com.intellij.openapi.application.ConfigImportHelper.getConfigImportSettings
@SuppressWarnings("UnusedDeclaration")
public class PyCharmEduConfigImportSettings extends ConfigImportSettings {
@Override
protected String getProductName(ThreeState full) {
return "WebStorm";
}
@Override
public String getCustomPathsSelector() {
return "WebIDE10";
}
@Override
public String getExecutableName() {
return "webstorm";
}
@Override
public String[] getMainJarNames() {
return new String[] { "webide", "webstorm"};
}
}