added idea support

This commit is contained in:
liana.bakradze
2015-03-23 12:46:08 +03:00
parent ae60819148
commit 0c7fff34bc
18 changed files with 650 additions and 8 deletions
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<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$/resources" type="java-resource" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="interactive-learning-intellij" />
<orderEntry type="module" module-name="lang-api" />
<orderEntry type="module" module-name="python-community-plugin" />
<orderEntry type="module" module-name="course-creator" />
<orderEntry type="module" module-name="course-creator-python" />
<orderEntry type="module" module-name="main" />
<orderEntry type="module" module-name="python-community" />
<orderEntry type="module" module-name="openapi" />
<orderEntry type="module" module-name="interactive-learning-intellij-py" />
<orderEntry type="module" module-name="interactive-learning" />
</component>
</module>
@@ -0,0 +1,38 @@
<!--suppress XmlUnboundNsPrefix -->
<idea-plugin version="2">
<id>com.jetbrains.edu.coursecreator.intellij.python</id>
<name>Plugin for Intellij IDEA for creating python courses</name>
<version>2.0</version>
<description><![CDATA[
Educational plugin core for students.
]]></description>
<change-notes><![CDATA[
]]>
</change-notes>
<depends>com.intellij.modules.lang</depends>
<depends>com.intellij.modules.python</depends>
<depends>com.jetbrains.edu.interactivelearning.intellij</depends>
<depends>com.jetbrains.edu.coursecreator</depends>
<depends>com.jetbrains.edu.coursecreator.python</depends>
<project-components>
</project-components>
<application-components>
</application-components>
<actions>
</actions>
<extensions defaultExtensionNs="com.jetbrains.edu.interactivelearning.intellij">
<EduProjectTemplate implementation="PyCCEduProjectTemplate"/>
</extensions>
</idea-plugin>
@@ -0,0 +1,92 @@
import com.intellij.ide.util.projectWizard.AbstractModuleBuilder;
import com.intellij.ide.util.projectWizard.WizardInputField;
import com.intellij.openapi.module.ModifiableModuleModel;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.ui.ValidationInfo;
import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.edu.coursecreator.PyCCProjectGenerator;
import com.jetbrains.edu.coursecreator.ui.CCNewProjectPanel;
import com.jetbrains.python.facet.PythonSdkComboBox;
import com.jetbrains.python.module.PythonModuleBuilder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
public class PyCCEduProjectTemplate extends EduProjectTemplate {
@NotNull
@Override
public String getName() {
return "Python Course Creation";
}
@Override
public String getDescription() {
return null;
}
@Override
public Icon getIcon() {
return null;
}
@NotNull
@Override
public AbstractModuleBuilder createModuleBuilder() {
return new PythonEduModuleBuilder() ;
}
@Override
public ValidationInfo validateSettings() {
return null;
}
private static class PythonEduModuleBuilder extends PythonModuleBuilder {
@Override
public void setupRootModel(ModifiableRootModel rootModel) throws ConfigurationException {
super.setupRootModel(rootModel);
Sdk sdk = mySdkComboBox.getSelectedSdk();
if (sdk != null) {
rootModel.setSdk(sdk);
}
}
final PythonSdkComboBox mySdkComboBox = new PythonSdkComboBox();
final CCNewProjectPanel panel = new CCNewProjectPanel();
@Override
protected List<WizardInputField> getAdditionalFields() {
List<WizardInputField> wizardInputFields = new ArrayList<WizardInputField>();
wizardInputFields.add(createWizardInputField("Edu.CCProjectSdk", "Python Interpreter:", mySdkComboBox));
wizardInputFields.add(createWizardInputField("Edu.Author", "Author:", panel.getAuthorField()));
wizardInputFields.add(createWizardInputField("Edu.Name", "Name:", panel.getNameField()));
wizardInputFields.add(createWizardInputField("Edu.Description", "Description:", panel.getDescriptionField()));
return wizardInputFields;
}
@Nullable
@Override
public Module commitModule(@NotNull final Project project, @Nullable ModifiableModuleModel model) {
Module module = super.commitModule(project, model);
if (module != null) {
final VirtualFile baseDir = project.getBaseDir();
StartupManager.getInstance(project).runWhenProjectIsInitialized(new Runnable() {
@Override
public void run() {
PyCCProjectGenerator.generateProject(project, baseDir, panel.getName(), panel.getAuthor(), panel.getDescription());
}
});
}
return module;
}
}
}
@@ -58,7 +58,7 @@ public class PyCCProjectGenerator extends PythonProjectGenerator implements Dire
mySettingsPanel.getAuthor(), mySettingsPanel.getDescription());
}
private static void generateProject(@NotNull final Project project, @NotNull final VirtualFile baseDir,
public static void generateProject(@NotNull final Project project, @NotNull final VirtualFile baseDir,
@NotNull final String name, @NotNull final String author,
@NotNull final String description) {
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<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$/resources" type="java-resource" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="util" />
<orderEntry type="module" module-name="lang-api" />
<orderEntry type="module" module-name="python-community-plugin" />
<orderEntry type="module" module-name="interactive-learning" />
<orderEntry type="module" module-name="lang-impl" />
<orderEntry type="module" module-name="interactive-learning-intellij" />
<orderEntry type="module" module-name="main" />
<orderEntry type="module" module-name="python-community" />
<orderEntry type="module" module-name="openapi" />
<orderEntry type="module" module-name="interactive-learning-python" />
</component>
</module>
@@ -0,0 +1,38 @@
<!--suppress XmlUnboundNsPrefix -->
<idea-plugin version="2">
<id>com.jetbrains.edu.interactivelearning.intellij.python</id>
<name>Educational plugin for Intellij Python</name>
<version>2.0</version>
<description><![CDATA[
Educational plugin core for students.
]]></description>
<change-notes><![CDATA[
]]>
</change-notes>
<depends>com.intellij.modules.lang</depends>
<depends>com.intellij.modules.python</depends>
<depends>com.jetbrains.edu.interactivelearning</depends>
<depends>com.jetbrains.python.edu.interactivelearning.python</depends>
<depends>com.jetbrains.edu.interactivelearning.intellij</depends>
<project-components>
</project-components>
<application-components>
</application-components>
<actions>
</actions>
<extensions defaultExtensionNs="com.jetbrains.edu.interactivelearning.intellij">
<EduProjectTemplate implementation="PyEduProjectTemplate"/>
</extensions>
</idea-plugin>
@@ -0,0 +1,190 @@
import sys
def get_file_text(path):
""" Returns file text by path"""
file_io = open(path, "r")
text = file_io.read()
file_io.close()
return text
def get_file_output(encoding="utf-8", path=sys.argv[-1]):
"""
Returns answer file output
:param encoding: to decode output in python3
:param path: path of file to execute
:return: list of strings
"""
import subprocess
proc = subprocess.Popen([sys.executable, path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
return list(map(lambda x: str(x.decode(encoding)), proc.communicate()[0].splitlines()))
def test_file_importable():
""" Tests there is no obvious syntax errors"""
path = sys.argv[-1]
if not path.endswith(".py"):
import os
parent = os.path.abspath(os.path.join(path, os.pardir))
python_files = [f for f in os.listdir(parent) if os.path.isfile(os.path.join(parent, f)) and f.endswith(".py")]
for python_file in python_files:
if python_file == "tests.py": continue
check_importable_path(os.path.join(parent, python_file))
return
check_importable_path(path)
def check_importable_path(path):
""" Checks that file is importable.
Reports failure otherwise.
"""
try:
import_file(path)
except:
failed("The file contains syntax errors", test_file_importable.__name__)
return
passed(test_file_importable.__name__)
def import_file(path):
""" Returns imported file """
if sys.version_info[0] == 2 or sys.version_info[1] < 3:
import imp
return imp.load_source("tmp", path)
elif sys.version_info[0] == 3:
import importlib.machinery
return importlib.machinery.SourceFileLoader("tmp", path).load_module("tmp")
def import_task_file():
""" Returns imported file.
Imports file from which check action was run
"""
path = sys.argv[-1]
return import_file(path)
def test_is_not_empty():
"""
Checks that file 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"):
"""
Checks that file was modified
"""
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):
"""
Returns the initial task text
"""
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):
"""
Checks that answer equals text.
"""
path = sys.argv[-1]
file_text = get_file_text(path)
if file_text.strip() == text:
passed()
else:
failed(error_text)
def test_answer_placeholders_text_deleted(error_text="Don't just delete task text"):
"""
Checks that all answer placeholders are not empty
"""
windows = get_answer_placeholders()
for window in windows:
if len(window) == 0:
failed(error_text)
return
passed()
def failed(message="Please, reload the task and try again.", name=None):
""" Reports failure """
if not name:
name = sys._getframe().f_back.f_code.co_name
print("#educational_plugin " + name + " FAILED + " + message)
def passed(name=None):
""" Reports success """
if not name:
name = sys._getframe().f_back.f_code.co_name
print("#educational_plugin " + name + " test OK")
def get_answer_placeholders():
"""
Returns all answer placeholders text
"""
prefix = "#educational_plugin_window = "
path = sys.argv[-1]
import os
file_name_without_extension = os.path.splitext(path)[0]
windows_path = file_name_without_extension + "_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_is_initial_text()
test_is_not_empty()
test_answer_placeholders_text_deleted()
test_file_importable()
@@ -0,0 +1,112 @@
import com.intellij.ide.fileTemplates.FileTemplate;
import com.intellij.ide.fileTemplates.FileTemplateManager;
import com.intellij.ide.fileTemplates.FileTemplateUtil;
import com.intellij.ide.util.projectWizard.AbstractModuleBuilder;
import com.intellij.ide.util.projectWizard.WizardInputField;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.ModifiableModuleModel;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.ui.ValidationInfo;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiManager;
import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
import com.jetbrains.edu.learning.ui.StudyNewProjectPanel;
import com.jetbrains.python.facet.PythonSdkComboBox;
import com.jetbrains.python.module.PythonModuleBuilder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
public class PyEduProjectTemplate extends EduProjectTemplate {
private static final Logger LOG = Logger.getInstance(PyEduProjectTemplate.class);
@NotNull
@Override
public String getName() {
return "Python";
}
@Nullable
@Override
public String getDescription() {
return null;
}
@Override
public Icon getIcon() {
return null;
}
@NotNull
@Override
public AbstractModuleBuilder createModuleBuilder() {
return new PythonEduModuleBuilder();
}
@Nullable
@Override
public ValidationInfo validateSettings() {
return null;
}
private static class PythonEduModuleBuilder extends PythonModuleBuilder {
final StudyProjectGenerator studyProjectGenerator = new StudyProjectGenerator();
final PythonSdkComboBox mySdkComboBox = new PythonSdkComboBox();
@Override
public void setupRootModel(ModifiableRootModel rootModel) throws ConfigurationException {
super.setupRootModel(rootModel);
Sdk sdk = mySdkComboBox.getSelectedSdk();
if (sdk != null) {
rootModel.setSdk(sdk);
}
}
@Override
protected List<WizardInputField> getAdditionalFields() {
final StudyNewProjectPanel panel = new StudyNewProjectPanel(studyProjectGenerator);
List<WizardInputField> wizardInputFields = new ArrayList<WizardInputField>();
wizardInputFields.add(createWizardInputField("Edu.ProjectSdk", "Python Interpreter:", mySdkComboBox));
wizardInputFields.add(createWizardInputField("Edu.Courses", "Courses:", panel.getCoursesComboBox()));
wizardInputFields.add(createWizardInputField("Edu.InfoPanel", "", panel.getInfoPanel()));
return wizardInputFields;
}
@Nullable
@Override
public Module commitModule(@NotNull final Project project, @Nullable ModifiableModuleModel model) {
Module module = super.commitModule(project, model);
if (module != null) {
final VirtualFile baseDir = project.getBaseDir();
studyProjectGenerator.generateProject(project, baseDir);
final FileTemplate template = FileTemplateManager.getInstance(project).getInternalTemplate("test_helper.py");
StartupManager.getInstance(project).runWhenProjectIsInitialized(new Runnable() {
@Override
public void run() {
final PsiDirectory projectDir = PsiManager.getInstance(project).findDirectory(baseDir);
if (projectDir != null) {
try {
FileTemplateUtil.createFromTemplate(template, "test_helper.py", null, projectDir);
}
catch (Exception e) {
LOG.error("Failed to create test_helper", e);
}
}
}
});
}
return module;
}
}
}
@@ -14,11 +14,6 @@
]]>
</change-notes>
<!--depends>com.intellij.modules.python</depends-->
<!-- please see http://confluence.jetbrains.net/display/IDEADEV/Plugin+Compatibility+with+IntelliJ+Platform+Products
on how to target different products -->
<depends>com.intellij.modules.lang</depends>
<depends>com.intellij.modules.python</depends>
<depends>com.jetbrains.edu.interactivelearning</depends>
@@ -108,4 +108,8 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator imp
public void setSelectedCourse(CourseInfo course) {
myGenerator.setSelectedCourse(course);
}
public StudyProjectGenerator getGenerator() {
return myGenerator;
}
}
@@ -68,4 +68,16 @@ public class CCNewProjectPanel {
}
}
}
public JTextField getAuthorField() {
return myAuthorField;
}
public JTextArea getDescriptionField() {
return myDescription;
}
public JTextField getNameField() {
return myName;
}
}
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<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" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/pluginResources/fileTemplates/internal" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/pluginResources/fileTemplates/j2ee" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="lang-api" />
<orderEntry type="module" module-name="platform-impl" />
</component>
</module>
@@ -0,0 +1,44 @@
<idea-plugin version="2">
<id>com.jetbrains.edu.interactivelearning.intellij</id>
<name>Educational plugin for Intellij</name>
<version>2.0</version>
<description><![CDATA[
Educational plugin core for students.
]]></description>
<change-notes><![CDATA[
]]>
</change-notes>
<depends>com.intellij.modules.python</depends>
<depends>com.jetbrains.edu.interactivelearning</depends>
<depends>com.jetbrains.python.edu.interactivelearning.python</depends>
<!-- please see http://confluence.jetbrains.net/display/IDEADEV/Plugin+Compatibility+with+IntelliJ+Platform+Products
on how to target different products -->
<depends>com.intellij.modules.lang</depends>
<project-components>
</project-components>
<application-components>
</application-components>
<actions>
</actions>
<extensions defaultExtensionNs="com.intellij">
<projectTemplatesFactory implementation="EduProjectTemplateFactory"/>
</extensions>
<extensionPoints>
<extensionPoint name="EduProjectTemplate" interface="EduProjectTemplate"/>
</extensionPoints>
</idea-plugin>
@@ -0,0 +1,28 @@
import com.intellij.ide.util.projectWizard.WizardInputField;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.platform.ProjectTemplate;
import javax.swing.*;
public abstract class EduProjectTemplate implements ProjectTemplate {
public static final ExtensionPointName<EduProjectTemplate> EP_NAME = ExtensionPointName.create("com.jetbrains.edu.interactivelearning.intellij.EduProjectTemplate");
public static WizardInputField createWizardInputField(String id, final String label, final JComponent component) {
return new WizardInputField(id, id) {
@Override
public String getLabel() {
return label;
}
@Override
public JComponent getComponent() {
return component;
}
@Override
public String getValue() {
return null;
}
};
}
}
@@ -0,0 +1,20 @@
import com.intellij.ide.util.projectWizard.WizardContext;
import com.intellij.platform.ProjectTemplate;
import com.intellij.platform.ProjectTemplatesFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class EduProjectTemplateFactory extends ProjectTemplatesFactory {
@NotNull
@Override
public String[] getGroups() {
return new String[] {"Edu"};
}
@NotNull
@Override
public ProjectTemplate[] createTemplates(@Nullable String group, WizardContext context) {
return EduProjectTemplate.EP_NAME.getExtensions();
}
}
@@ -33,7 +33,7 @@ public class StudyTreeStructureProvider implements TreeStructureProvider, DumbAw
if (project != null) {
if (node.getValue() instanceof PsiDirectory) {
final PsiDirectory nodeValue = (PsiDirectory)node.getValue();
if (!nodeValue.getName().contains(EduNames.USER_TESTS)) {
if (!nodeValue.getName().contains(EduNames.USER_TESTS) && !nodeValue.getName().equals(".idea")) {
StudyDirectoryNode newNode = new StudyDirectoryNode(project, nodeValue, settings);
nodes.add(newNode);
}
@@ -8,7 +8,7 @@
<properties/>
<border type="none"/>
<children>
<grid id="54488" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="54488" binding="myInfoPanel" layout-manager="GridLayoutManager" row-count="2" 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="1" column="1" row-span="1" col-span="2" vsize-policy="2" hsize-policy="4" anchor="0" fill="3" indent="0" use-parent-layout="false">
@@ -26,6 +26,7 @@ public class StudyNewProjectPanel{
private JLabel myAuthorLabel;
private JLabel myDescriptionLabel;
private JLabel myLabel;
private JPanel myInfoPanel;
private final StudyProjectGenerator myGenerator;
private static final String CONNECTION_ERROR = "<html>Failed to download courses.<br>Check your Internet connection.</html>";
private static final String INVALID_COURSE = "Selected course is invalid";
@@ -133,4 +134,11 @@ public class StudyNewProjectPanel{
}
}
public JComboBox getCoursesComboBox() {
return myCoursesComboBox;
}
public JPanel getInfoPanel() {
return myInfoPanel;
}
}