mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Moved ipython notebook to pycharm community
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
intellij-ipnb
|
||||
=============
|
||||
|
||||
IPython notebook support in IntelliJ
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PLUGIN_MODULE" version="4">
|
||||
<component name="DevKit.ModuleBuildProperties" url="file://$MODULE_DIR$/resources/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$/resources" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/testSrc" isTestSource="true" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module-library">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$MODULE_DIR$/lib/java_websocket.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
</component>
|
||||
</module>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
There are 4 possible cell types:
|
||||
- Markdown
|
||||
- Heading
|
||||
- Raw
|
||||
- Code
|
||||
|
||||
Markdown cell type consists of:
|
||||
- metadata
|
||||
- Source <- List of Strings
|
||||
|
||||
Heading cell type consists of:
|
||||
- metadata
|
||||
- Source <- List of Strings
|
||||
- level <- int
|
||||
|
||||
Raw cell type consists of:
|
||||
- metadata
|
||||
|
||||
Code cell type consists of:
|
||||
- metadata
|
||||
- collapsed <- Boolean
|
||||
- input <- List of Strings
|
||||
- language <- String
|
||||
- prompt_number <- int
|
||||
- outputs <- List of Outputs
|
||||
|
||||
Output of Code cell consists of:
|
||||
- metadata
|
||||
- output_type <- String
|
||||
- text <- String
|
||||
- one of [png, latex, stream, ..] <- String
|
||||
- prompt_number <- int *present only with latex field
|
||||
|
||||
|
||||
Cells are packed into worksheets.
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
pyzmq
|
||||
@@ -0,0 +1,11 @@
|
||||
"""Tests of interactions with IPython kernel via 0MQ."""
|
||||
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
import zmq
|
||||
|
||||
|
||||
class ZMQVersionTest(TestCase):
|
||||
def test_version_available(self):
|
||||
self.assertIsNotNone(zmq.pyzmq_version())
|
||||
@@ -0,0 +1,43 @@
|
||||
<idea-plugin version="2" xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
<name>IPython Notebook</name>
|
||||
<id>org.jetbrains.plugins.ipnb</id>
|
||||
<version>0.1</version>
|
||||
<vendor>JetBrains</vendor>
|
||||
<description>IPython Notebook support</description>
|
||||
<depends>com.intellij.modules.platform</depends>
|
||||
<depends optional="true">Pythonid</depends>
|
||||
<depends optional="true">com.intellij.modules.python</depends>
|
||||
|
||||
<project-components>
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.plugins.ipnb.configuration.IpnbConnectionManager</implementation-class>
|
||||
</component>
|
||||
</project-components>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<fileEditorProvider implementation="org.jetbrains.plugins.ipnb.editor.IpnbEditorProvider"/>
|
||||
<fileTypeFactory implementation="org.jetbrains.plugins.ipnb.IpnbFileTypeFactory"/>
|
||||
|
||||
<errorHandler implementation="com.intellij.diagnostic.ITNReporter"/>
|
||||
|
||||
<projectConfigurable groupId="tools" instance="org.jetbrains.plugins.ipnb.configuration.IpnbConfigurable"
|
||||
id="org.jetbrains.plugins.ipnb.configuration.IpnbConfigurable" displayName="IPython Notebook"
|
||||
nonDefaultProject="true"/>
|
||||
<projectService serviceInterface="org.jetbrains.plugins.ipnb.configuration.IpnbSettings"
|
||||
serviceImplementation="org.jetbrains.plugins.ipnb.configuration.IpnbSettings"/>
|
||||
<lang.parserDefinition language="IpnbPython" implementationClass="org.jetbrains.plugins.ipnb.psi.IpnbPyParserDefinition"/>
|
||||
</extensions>
|
||||
<extensions defaultExtensionNs="Pythonid">
|
||||
<dialectsTokenSetContributor implementation="org.jetbrains.plugins.ipnb.psi.IpnbPyTokenSetContributor"/>
|
||||
</extensions>
|
||||
<actions>
|
||||
<action class="org.jetbrains.plugins.ipnb.editor.actions.IpnbRunCellAction" id="IpnbRunCellAction" text="Run cell">
|
||||
<keyboard-shortcut keymap="$default" first-keystroke="ctrl ENTER"/>
|
||||
</action>
|
||||
<action class="org.jetbrains.plugins.ipnb.editor.actions.IpnbSaveAction" id="IpnbSaveAction" text="Save and Checkpoint">
|
||||
</action>
|
||||
<action class="org.jetbrains.plugins.ipnb.editor.actions.IpnbAddCellAction" id="IpnbAddCellAction" text="Insert Cell Below"/>
|
||||
<action class="org.jetbrains.plugins.ipnb.editor.actions.IpnbCutCellAction" id="IpnbCutCellAction" text="Cut Cell"/>
|
||||
<action class="org.jetbrains.plugins.ipnb.editor.actions.IpnbCopyCellAction" id="IpnbCopyCellAction" text="Copy Cell"/>
|
||||
<action class="org.jetbrains.plugins.ipnb.editor.actions.IpnbPasteCellAction" id="IpnbPasteCellAction" text="Paste Cell Below"/>
|
||||
</actions>
|
||||
</idea-plugin>
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.plugins.ipnb;
|
||||
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.vfs.CharsetToolkit;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.PlatformIcons;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class IpnbFileType implements FileType {
|
||||
|
||||
public static final IpnbFileType INSTANCE = new IpnbFileType();
|
||||
|
||||
@NonNls
|
||||
public static final String DEFAULT_EXTENSION = "ipynb";
|
||||
|
||||
@NotNull
|
||||
public String getName() {
|
||||
return "IPNB";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getDescription() {
|
||||
return "IPython Notebook";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getDefaultExtension() {
|
||||
return DEFAULT_EXTENSION;
|
||||
}
|
||||
|
||||
public Icon getIcon() {
|
||||
return PlatformIcons.UI_FORM_ICON;
|
||||
} //TODO
|
||||
|
||||
public boolean isBinary() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isReadOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getCharset(@NotNull VirtualFile file, @NotNull final byte[] content) {
|
||||
return CharsetToolkit.UTF8;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.plugins.ipnb;
|
||||
|
||||
import com.intellij.openapi.fileTypes.FileTypeConsumer;
|
||||
import com.intellij.openapi.fileTypes.FileTypeFactory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author traff
|
||||
*/
|
||||
public class IpnbFileTypeFactory extends FileTypeFactory {
|
||||
|
||||
public void createFileTypes(@NotNull FileTypeConsumer consumer) {
|
||||
consumer.consume(IpnbFileType.INSTANCE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
package org.jetbrains.plugins.ipnb;
|
||||
|
||||
import com.intellij.ide.BrowserUtil;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.text.MarkdownUtil;
|
||||
import com.petebevin.markdown.MarkdownProcessor;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.concurrent.Worker;
|
||||
import javafx.embed.swing.JFXPanel;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.web.WebEngine;
|
||||
import javafx.scene.web.WebView;
|
||||
import netscape.javascript.JSException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.editor.IpnbEditorUtil;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class IpnbUtils {
|
||||
private static final Logger LOG = Logger.getInstance(IpnbUtils.class);
|
||||
private static MarkdownProcessor ourMarkdownProcessor = new MarkdownProcessor();
|
||||
|
||||
private static final String ourPrefix = "<html><head><script type=\"text/x-mathjax-config\">\n" +
|
||||
" MathJax.Hub.Config({\n" +
|
||||
" tex2jax: {\n" +
|
||||
" inlineMath: [ ['$','$'], [\"\\\\(\",\"\\\\)\"] ],\n" +
|
||||
" displayMath: [ ['$$','$$'], [\"\\\\[\",\"\\\\]\"] ],\n" +
|
||||
" processEscapes: true,\n" +
|
||||
" processEnvironments: true\n" +
|
||||
" },\n" +
|
||||
" displayAlign: 'center',\n" +
|
||||
" \"HTML-CSS\": {\n" +
|
||||
" styles: {'.MathJax_Display': {\"margin\": 0}},\n" +
|
||||
" webFont: null,\n" +
|
||||
" preferredFont: null,\n" +
|
||||
" linebreaks: { automatic: true }\n" +
|
||||
" }\n" +
|
||||
" });\n" +
|
||||
"</script><script type=\"text/javascript\"\n" +
|
||||
" src=\"http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML\">\n" +
|
||||
"</script></head><body style='width: " + IpnbEditorUtil.PANEL_WIDTH + "px'><div id=\"mydiv\">";
|
||||
|
||||
public static String markdown2Html(@NotNull final String description) {
|
||||
// TODO: add links to the dependant notebook pages (see: index.ipynb)
|
||||
//TODO: relative picture links (see: index.ipynb in IPython.kernel) We should use absolute file:/// path
|
||||
final List<String> lines = ContainerUtil.newArrayList(description.split("\n|\r|\r\n"));
|
||||
final List<String> processedLines = new ArrayList<String>();
|
||||
boolean isInCode = false;
|
||||
for (String line : lines) {
|
||||
String processedLine = line;
|
||||
if (line.startsWith(" ")) {
|
||||
processedLine = line.substring(1);
|
||||
}
|
||||
|
||||
if (processedLine.contains("```")) isInCode = !isInCode;
|
||||
if (isInCode) {
|
||||
processedLine = processedLine
|
||||
.replace("&", "&");
|
||||
}
|
||||
else {
|
||||
processedLine = processedLine
|
||||
.replaceAll("([\\w])_([\\w])", "$1&underline;$2");
|
||||
}
|
||||
processedLines.add(processedLine);
|
||||
}
|
||||
MarkdownUtil.replaceHeaders(processedLines);
|
||||
MarkdownUtil.removeImages(processedLines);
|
||||
MarkdownUtil.generateLists(processedLines);
|
||||
MarkdownUtil.replaceCodeBlock(processedLines);
|
||||
final String[] lineArray = ArrayUtil.toStringArray(processedLines);
|
||||
final String normalizedMarkdown = StringUtil.join(lineArray, "\n");
|
||||
String html = ourMarkdownProcessor.markdown(normalizedMarkdown);
|
||||
html = html
|
||||
.replace("<pre><code>", "<pre>").replace("</code></pre>", "</pre>")
|
||||
.replace("<em>", "<i>").replace("</em>", "</i>")
|
||||
.replace("<strong>", "<b>").replace("</strong>", "</b>")
|
||||
.replace("&underline;", "_")
|
||||
.trim();
|
||||
return html;
|
||||
}
|
||||
|
||||
public static void addLatexToPanel(@NotNull final String source, @NotNull final JPanel panel) {
|
||||
final StringBuilder result = convertToHtml(source);
|
||||
addToPanel(result.toString(), panel);
|
||||
|
||||
}
|
||||
|
||||
private static StringBuilder convertToHtml(@NotNull final String source) {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
StringBuilder markdown = new StringBuilder();
|
||||
boolean inCode = false;
|
||||
int inMultiStringCode = 0;
|
||||
boolean inEnd = false;
|
||||
boolean escaped = false;
|
||||
boolean backQuoted = false;
|
||||
|
||||
for (int i = 0; i != source.length(); ++i) {
|
||||
final char charAt = source.charAt(i);
|
||||
|
||||
if (charAt == '`') {
|
||||
backQuoted = !backQuoted;
|
||||
if (source.length() > i + 2 && source.charAt(i + 1) == '`' && source.charAt(i + 2) == '`') {
|
||||
markdown.append(escaped ? "</pre>" : "<pre>");
|
||||
escaped = !escaped;
|
||||
i = i + 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!escaped && !backQuoted) {
|
||||
if (charAt == '$' && source.length() > i + 1 && source.charAt(i+1) != '$') {
|
||||
inCode = !inCode;
|
||||
}
|
||||
|
||||
if (charAt == '\\' && source.substring(i).startsWith("\\begin")) {
|
||||
inMultiStringCode += 1;
|
||||
}
|
||||
if (charAt == '\\' && source.substring(i).startsWith("\\end")) {
|
||||
inEnd = true;
|
||||
}
|
||||
}
|
||||
|
||||
final boolean doubleDollar = charAt == '$' && ((source.length() > i + 1 && source.charAt(i + 1) == '$')
|
||||
|| (i >= 1 && source.charAt(i - 1) == '$'));
|
||||
if (inCode || inMultiStringCode != 0 || (!backQuoted && doubleDollar)) {
|
||||
if (markdown.length() != 0) {
|
||||
result.append(markdown2Html(markdown.toString()));
|
||||
markdown = new StringBuilder();
|
||||
}
|
||||
|
||||
result.append(charAt);
|
||||
}
|
||||
else {
|
||||
markdown.append(charAt);
|
||||
}
|
||||
|
||||
if (inEnd && charAt == '}') {
|
||||
inMultiStringCode -= 1;
|
||||
}
|
||||
|
||||
}
|
||||
if (markdown.length() != 0) {
|
||||
result.append(markdown2Html(markdown.toString()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void addToPanel(@NotNull final String source, @NotNull final JPanel panel) {
|
||||
Platform.setImplicitExit(false);
|
||||
|
||||
final String text = ourPrefix + source + "</div></body></html>";
|
||||
|
||||
final JFXPanel javafxPanel = new JFXPanel();
|
||||
javafxPanel.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
final MouseEvent parentEvent = SwingUtilities.convertMouseEvent(javafxPanel, e, panel);
|
||||
panel.dispatchEvent(parentEvent);
|
||||
}
|
||||
});
|
||||
Platform.runLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final BorderPane borderPane = new BorderPane();
|
||||
final WebView webComponent = new WebView();
|
||||
webComponent.setPrefWidth(IpnbEditorUtil.PANEL_WIDTH + 100);
|
||||
webComponent.setPrefHeight(5);
|
||||
final WebEngine engine = webComponent.getEngine();
|
||||
engine.locationProperty().addListener(new ChangeListener<String>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends String> value, String newValue, String t1) {
|
||||
try {
|
||||
final URI address = new URI(value.getValue());
|
||||
BrowserUtil.browse(address);
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
LOG.warn(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
engine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Worker.State> arg0, Worker.State oldState, Worker.State newState) {
|
||||
if (newState == Worker.State.SUCCEEDED) {
|
||||
adjustHeight(webComponent, javafxPanel, panel);
|
||||
}
|
||||
}
|
||||
});
|
||||
engine.loadContent(text);
|
||||
borderPane.setCenter(webComponent);
|
||||
final Scene scene = new Scene(borderPane);
|
||||
javafxPanel.setScene(scene);
|
||||
}
|
||||
});
|
||||
panel.add(javafxPanel);
|
||||
}
|
||||
|
||||
private static void adjustHeight(final WebView webComponent, final JFXPanel javafxPanel, final JPanel panel) {
|
||||
Platform.runLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Object result = webComponent.getEngine().executeScript("document.getElementById(\"mydiv\").offsetHeight");
|
||||
if (result instanceof Integer) {
|
||||
final double value = ((Integer)result + 150);
|
||||
|
||||
javafxPanel.setPreferredSize(new Dimension((int)webComponent.getPrefWidth(), (int)value));
|
||||
panel.revalidate();
|
||||
panel.repaint();
|
||||
}
|
||||
}
|
||||
catch (JSException e) {
|
||||
LOG.warn(e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.plugins.ipnb.configuration.IpnbConfigurable">
|
||||
<grid id="27dc6" binding="myMainPanel" 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>
|
||||
<xy x="20" y="20" width="657" height="302"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<grid id="6ed7f" 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="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="1a2fb" class="com.intellij.ui.components.JBLabel">
|
||||
<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="IPython Notebook URL:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="5a5b" class="com.intellij.ui.components.JBTextField" binding="myFieldUrl">
|
||||
<constraints>
|
||||
<grid row="0" 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>
|
||||
</children>
|
||||
</grid>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
@@ -0,0 +1,69 @@
|
||||
package org.jetbrains.plugins.ipnb.configuration;
|
||||
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.options.SearchableConfigurable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.ui.components.JBTextField;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class IpnbConfigurable implements SearchableConfigurable {
|
||||
private JPanel myMainPanel;
|
||||
private JBTextField myFieldUrl;
|
||||
@NotNull private final Project myProject;
|
||||
|
||||
public IpnbConfigurable(@NotNull Project project) {
|
||||
myProject = project;
|
||||
myFieldUrl.setText(IpnbSettings.getInstance(myProject).getURL());
|
||||
}
|
||||
|
||||
@Nls
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "IPython Notebook";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpTopic() {
|
||||
return "reference-ipnb";
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent createComponent() {
|
||||
return myMainPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModified() {
|
||||
final String oldUrl = IpnbSettings.getInstance(myProject).getURL();
|
||||
final String url = myFieldUrl.getText();
|
||||
return !url.equals(oldUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() throws ConfigurationException {
|
||||
IpnbSettings.getInstance(myProject).setURL(myFieldUrl.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disposeUIResources() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getId() {
|
||||
return "IpnbConfigurable";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Runnable enableSearch(String option) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
package org.jetbrains.plugins.ipnb.configuration;
|
||||
|
||||
import com.intellij.openapi.components.ProjectComponent;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.ui.popup.Balloon;
|
||||
import com.intellij.openapi.ui.popup.BalloonBuilder;
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.ipnb.editor.IpnbFileEditor;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.code.IpnbCodePanel;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.output.IpnbOutputCell;
|
||||
import org.jetbrains.plugins.ipnb.protocol.IpnbConnection;
|
||||
import org.jetbrains.plugins.ipnb.protocol.IpnbConnectionListenerBase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public final class IpnbConnectionManager implements ProjectComponent {
|
||||
private static final Logger LOG = Logger.getInstance(IpnbConnectionManager.class);
|
||||
private final Project myProject;
|
||||
private Map<String, IpnbConnection> myKernels = new HashMap<String, IpnbConnection>();
|
||||
private Map<String, IpnbCodePanel> myUpdateMap = new HashMap<String, IpnbCodePanel>();
|
||||
|
||||
public IpnbConnectionManager(final Project project) {
|
||||
myProject = project;
|
||||
}
|
||||
|
||||
public static IpnbConnectionManager getInstance(Project project) {
|
||||
return project.getComponent(IpnbConnectionManager.class);
|
||||
}
|
||||
|
||||
public void executeCell(@NotNull final IpnbCodePanel codePanel) {
|
||||
final IpnbFileEditor fileEditor = codePanel.getFileEditor();
|
||||
final String path = fileEditor.getVirtualFile().getPath();
|
||||
if (!myKernels.containsKey(path)) {
|
||||
try {
|
||||
final String url = IpnbSettings.getInstance(myProject).getURL();
|
||||
if (StringUtil.isEmptyOrSpaces(url)) {
|
||||
BalloonBuilder balloonBuilder = JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(
|
||||
"Please, specify IPython Notebook URL in Settings->IPython Notebook", null, MessageType.WARNING.getPopupBackground(), null);
|
||||
final Balloon balloon = balloonBuilder.createBalloon();
|
||||
balloon.showInCenterOf(fileEditor.getRunCellButton());
|
||||
return;
|
||||
}
|
||||
final IpnbConnection connection = new IpnbConnection(new URI(url), new IpnbConnectionListenerBase() {
|
||||
@Override
|
||||
public void onOpen(@NotNull IpnbConnection connection) {
|
||||
final String messageId = connection.execute(codePanel.getCell().getSourceAsString());
|
||||
myUpdateMap.put(messageId, codePanel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOutput(@NotNull IpnbConnection connection,
|
||||
@NotNull String parentMessageId,
|
||||
@NotNull List<IpnbOutputCell> outputs,
|
||||
@Nullable Integer execCount) {
|
||||
if (!myUpdateMap.containsKey(parentMessageId)) return;
|
||||
final IpnbCodePanel cell = myUpdateMap.remove(parentMessageId);
|
||||
cell.getCell().setPromptNumber(execCount);
|
||||
cell.updatePanel(outputs);
|
||||
}
|
||||
});
|
||||
myKernels.put(path, connection);
|
||||
}
|
||||
catch (IOException e) {
|
||||
BalloonBuilder balloonBuilder = JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(
|
||||
"Please, check that IPython Notebook is running", null, MessageType.WARNING.getPopupBackground(), null);
|
||||
final Balloon balloon = balloonBuilder.createBalloon();
|
||||
balloon.showInCenterOf(fileEditor.getRunCellButton());
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
BalloonBuilder balloonBuilder = JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(
|
||||
"Please, check IPython Notebook URL in Settings->IPython Notebook", null, MessageType.WARNING.getPopupBackground(), null);
|
||||
final Balloon balloon = balloonBuilder.createBalloon();
|
||||
balloon.showInCenterOf(fileEditor.getRunCellButton());
|
||||
}
|
||||
}
|
||||
else {
|
||||
final IpnbConnection connection = myKernels.get(path);
|
||||
if (connection != null) {
|
||||
final String messageId = connection.execute(codePanel.getCell().getSourceAsString());
|
||||
myUpdateMap.put(messageId, codePanel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void projectOpened() {}
|
||||
|
||||
|
||||
public void projectClosed() {
|
||||
shutdownKernels();
|
||||
}
|
||||
|
||||
private void shutdownKernels() {
|
||||
for (IpnbConnection connection : myKernels.values()) {
|
||||
connection.shutdown();
|
||||
try {
|
||||
connection.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
myKernels.clear();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getComponentName() {
|
||||
return "IpnbConnectionManager";
|
||||
}
|
||||
|
||||
public void initComponent() {
|
||||
}
|
||||
|
||||
public void disposeComponent() {
|
||||
shutdownKernels();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.jetbrains.plugins.ipnb.configuration;
|
||||
|
||||
import com.intellij.openapi.components.*;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.util.xmlb.XmlSerializerUtil;
|
||||
import com.intellij.util.xmlb.annotations.Transient;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@State(name = "IpnbSettings",
|
||||
storages = {@Storage(file = StoragePathMacros.PROJECT_FILE)}
|
||||
)
|
||||
public class IpnbSettings implements PersistentStateComponent<IpnbSettings> {
|
||||
public String URL = "";
|
||||
|
||||
public static IpnbSettings getInstance(@NotNull Project project) {
|
||||
return ServiceManager.getService(project, IpnbSettings.class);
|
||||
}
|
||||
|
||||
public void setURL(@NotNull final String url) {
|
||||
URL = url;
|
||||
}
|
||||
|
||||
@Transient
|
||||
public String getURL() {
|
||||
return URL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IpnbSettings getState() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadState(IpnbSettings state) {
|
||||
XmlSerializerUtil.copyBean(state, this);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package org.jetbrains.plugins.ipnb.editor;
|
||||
|
||||
import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorPolicy;
|
||||
import com.intellij.openapi.fileEditor.FileEditorProvider;
|
||||
import com.intellij.openapi.fileEditor.FileEditorState;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.IpnbFileType;
|
||||
|
||||
/**
|
||||
* @author traff
|
||||
*/
|
||||
public class IpnbEditorProvider implements FileEditorProvider, DumbAware {
|
||||
@NonNls private static final String SELECTED_CELL = "selected";
|
||||
@NonNls private static final String ID = "id";
|
||||
@NonNls private static final String TOP = "top";
|
||||
|
||||
@Override
|
||||
public boolean accept(@NotNull Project project, @NotNull VirtualFile file) {
|
||||
return file.getFileType() == IpnbFileType.INSTANCE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public FileEditor createEditor(@NotNull Project project, @NotNull VirtualFile file) {
|
||||
return new IpnbFileEditor(project, file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disposeEditor(@NotNull FileEditor editor) {
|
||||
Disposer.dispose(editor);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public FileEditorState readState(@NotNull Element sourceElement, @NotNull Project project, @NotNull VirtualFile file) {
|
||||
final IpnbEditorState state = new IpnbEditorState(-1, 0, 0);
|
||||
final Element child = sourceElement.getChild(SELECTED_CELL);
|
||||
state.setSelectedIndex(child == null ? 0 : Integer.parseInt(child.getAttributeValue(ID)));
|
||||
state.setSelectedTop(child == null ? 0 : Integer.parseInt(child.getAttributeValue(TOP)));
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeState(@NotNull FileEditorState state, @NotNull Project project, @NotNull Element targetElement) {
|
||||
IpnbEditorState editorState = (IpnbEditorState)state;
|
||||
final int id = editorState.getSelectedIndex();
|
||||
final int location = editorState.getSelectedTop();
|
||||
final Element element = new Element(SELECTED_CELL);
|
||||
element.setAttribute(ID, String.valueOf(id));
|
||||
element.setAttribute(TOP, String.valueOf(location));
|
||||
targetElement.addContent(element);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getEditorTypeId() {
|
||||
return "ipnb-editor";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public FileEditorPolicy getPolicy() {
|
||||
return FileEditorPolicy.PLACE_BEFORE_DEFAULT_EDITOR;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2000-2009 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.ipnb.editor;
|
||||
|
||||
import com.intellij.openapi.fileEditor.FileEditorState;
|
||||
import com.intellij.openapi.fileEditor.FileEditorStateLevel;
|
||||
|
||||
final class IpnbEditorState implements FileEditorState{
|
||||
private final transient long myDocumentModificationStamp; // should not be serialized
|
||||
private int mySelectedIndex;
|
||||
private int mySelectedTop;
|
||||
|
||||
public IpnbEditorState(final long modificationStamp, int selectedComponentIndex, int top) {
|
||||
myDocumentModificationStamp = modificationStamp;
|
||||
mySelectedIndex = selectedComponentIndex;
|
||||
mySelectedTop = top;
|
||||
}
|
||||
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof IpnbEditorState)) return false;
|
||||
|
||||
final IpnbEditorState state = (IpnbEditorState)o;
|
||||
|
||||
return myDocumentModificationStamp == state.myDocumentModificationStamp && mySelectedIndex == state.mySelectedIndex &&
|
||||
mySelectedTop == state.mySelectedTop;
|
||||
}
|
||||
|
||||
public int getSelectedTop() {
|
||||
return mySelectedTop;
|
||||
}
|
||||
|
||||
public void setSelectedTop(int selectedTop) {
|
||||
mySelectedTop = selectedTop;
|
||||
}
|
||||
|
||||
public void setSelectedIndex(int selectedIndex) {
|
||||
mySelectedIndex = selectedIndex;
|
||||
}
|
||||
|
||||
public int getSelectedIndex() {
|
||||
return mySelectedIndex;
|
||||
}
|
||||
|
||||
public int hashCode(){
|
||||
return (int)(myDocumentModificationStamp ^ (myDocumentModificationStamp >>> 32));
|
||||
}
|
||||
|
||||
public boolean canBeMergedWith(FileEditorState otherState, FileEditorStateLevel level) {
|
||||
return otherState instanceof IpnbEditorState;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.plugins.ipnb.editor;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.execution.impl.ConsoleViewUtil;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.ui.Gray;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.jetbrains.python.PythonFileType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.code.IpnbCodeSourcePanel;
|
||||
import org.jetbrains.plugins.ipnb.psi.IpnbPyFragment;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseWheelListener;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author traff
|
||||
*/
|
||||
public class IpnbEditorUtil {
|
||||
public enum PromptType { In, Out, None }
|
||||
|
||||
public static Dimension PROMPT_SIZE = new Dimension(80, 30);
|
||||
public static int PANEL_WIDTH = 900;
|
||||
|
||||
public static Editor createPythonCodeEditor(@NotNull final Project project, @NotNull final IpnbCodeSourcePanel codeSourcePanel) {
|
||||
final EditorFactory editorFactory = EditorFactory.getInstance();
|
||||
assert editorFactory != null;
|
||||
final Document document = createPythonCodeDocument(project, codeSourcePanel);
|
||||
assert document != null;
|
||||
EditorEx editor = (EditorEx)editorFactory.createEditor(document, project, PythonFileType.INSTANCE, false);
|
||||
|
||||
setupEditor(editor);
|
||||
return editor;
|
||||
}
|
||||
|
||||
private static void setupEditor(@NotNull final EditorEx editor) {
|
||||
if (!UIUtil.isUnderDarcula())
|
||||
editor.setBackgroundColor(Gray._247);
|
||||
noScrolling(editor);
|
||||
editor.getScrollPane().setBorder(null);
|
||||
ConsoleViewUtil.setupConsoleEditor(editor, false, false);
|
||||
}
|
||||
|
||||
public static Editor createPlainCodeEditor(@NotNull final Project project, @NotNull final String text) {
|
||||
final EditorFactory editorFactory = EditorFactory.getInstance();
|
||||
assert editorFactory != null;
|
||||
final Document document = editorFactory.createDocument(text);
|
||||
EditorEx editor = (EditorEx)editorFactory.createEditor(document, project);
|
||||
setupEditor(editor);
|
||||
return editor;
|
||||
}
|
||||
|
||||
private static void noScrolling(EditorEx editor) {
|
||||
editor.getScrollPane().setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
|
||||
editor.getScrollPane().setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
||||
editor.getScrollPane().setWheelScrollingEnabled(false);
|
||||
|
||||
List<MouseWheelListener> listeners = Lists.newArrayList(editor.getScrollPane().getMouseWheelListeners());
|
||||
for (MouseWheelListener l : listeners) {
|
||||
editor.getScrollPane().removeMouseWheelListener(l);
|
||||
}
|
||||
}
|
||||
|
||||
public static Document createPythonCodeDocument(@NotNull final Project project, @NotNull IpnbCodeSourcePanel codeSourcePanel) {
|
||||
final String text = codeSourcePanel.getCell().getSourceAsString().trim();
|
||||
final IpnbPyFragment fragment = new IpnbPyFragment(project, text, true, codeSourcePanel);
|
||||
|
||||
return PsiDocumentManager.getInstance(project).getDocument(fragment);
|
||||
}
|
||||
|
||||
public static JComponent createPromptComponent(Integer promptNumber, @NotNull final PromptType type) {
|
||||
final String promptText = prompt(promptNumber, type);
|
||||
JLabel promptLabel = new JLabel(promptText);
|
||||
promptLabel.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
promptLabel.setPreferredSize(PROMPT_SIZE);
|
||||
final Font font = promptLabel.getFont();
|
||||
assert font != null;
|
||||
promptLabel.setFont(font.deriveFont(Font.BOLD));
|
||||
final JBColor darkRed = new JBColor(new Color(210, 30, 50), new Color(210, 30, 50));
|
||||
promptLabel.setForeground(type == PromptType.In ? JBColor.BLUE : darkRed);
|
||||
promptLabel.setBackground(getBackground());
|
||||
return promptLabel;
|
||||
}
|
||||
|
||||
protected static String prompt(Integer promptNumber, @NotNull final PromptType type) {
|
||||
if (type == PromptType.In)
|
||||
return promptNumber == null ? type + " [ ]:" : promptNumber > 0 ? String.format(type + " [%d]:", promptNumber) : type + " [*]:";
|
||||
else if (type == PromptType.Out)
|
||||
return promptNumber == null ? type + "[ ]:" : promptNumber > 0 ? String.format(type + "[%d]:", promptNumber) : type + "[*]:";
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
public static Color getBackground() {
|
||||
return EditorColorsManager.getInstance().getGlobalScheme().getDefaultBackground();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,447 @@
|
||||
package org.jetbrains.plugins.ipnb.editor;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.AppTopics;
|
||||
import com.intellij.codeHighlighting.BackgroundEditorHighlighter;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.ide.structureView.StructureViewBuilder;
|
||||
import com.intellij.openapi.actionSystem.ActionManager;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.*;
|
||||
import com.intellij.openapi.fileEditor.impl.FileEditorProviderManagerImpl;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.ComboBox;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.UserDataHolderBase;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.pom.Navigatable;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.ui.ScrollPaneFactory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.ipnb.editor.actions.*;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.*;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.code.IpnbCodePanel;
|
||||
import org.jetbrains.plugins.ipnb.format.IpnbParser;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.IpnbCell;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.IpnbCodeCell;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.IpnbHeadingCell;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.IpnbMarkdownCell;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.output.IpnbOutputCell;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.MatteBorder;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author traff
|
||||
*/
|
||||
public class IpnbFileEditor extends UserDataHolderBase implements FileEditor, TextEditor {
|
||||
private final Project myProject;
|
||||
private final VirtualFile myFile;
|
||||
|
||||
private final String myName;
|
||||
|
||||
private final JComponent myEditorPanel;
|
||||
|
||||
private final TextEditor myEditor;
|
||||
private final IpnbFilePanel myIpnbFilePanel;
|
||||
private ComboBox myCellTypeCombo;
|
||||
private static final String codeCellType = "Code";
|
||||
private static final String markdownCellType = "Markdown";
|
||||
private static final String headingCellType = "Heading ";
|
||||
private static final String rawNBCellType = "Raw NBConvert";
|
||||
private final static String[] ourCellTypes = new String[]{codeCellType, markdownCellType, /*rawNBCellType, */headingCellType + "1",
|
||||
headingCellType + "2", headingCellType + "3", headingCellType + "4", headingCellType + "5", headingCellType + "6"};
|
||||
private JButton myRunCellButton;
|
||||
private final JScrollPane myScrollPane;
|
||||
|
||||
|
||||
public IpnbFileEditor(Project project, final VirtualFile vFile) {
|
||||
myProject = project;
|
||||
myProject.getMessageBus().connect(this).subscribe(AppTopics.FILE_DOCUMENT_SYNC, new FileDocumentManagerAdapter() {
|
||||
@Override
|
||||
public void beforeAllDocumentsSaving() {
|
||||
final IpnbFilePanel filePanel = myIpnbFilePanel;
|
||||
if (filePanel != null) {
|
||||
IpnbParser.saveIpnbFile(filePanel);
|
||||
vFile.refresh(false, false);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
myProject.getMessageBus().connect(this).subscribe(FileEditorManagerListener.Before.FILE_EDITOR_MANAGER, new FileEditorManagerListener.Before.Adapter() {
|
||||
@Override
|
||||
public void beforeFileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
|
||||
IpnbParser.saveIpnbFile(myIpnbFilePanel);
|
||||
file.refresh(false, false);
|
||||
}
|
||||
});
|
||||
myFile = vFile;
|
||||
|
||||
myName = vFile.getName();
|
||||
|
||||
myEditor = createEditor(project, vFile);
|
||||
|
||||
myEditorPanel = new JPanel(new BorderLayout());
|
||||
myEditorPanel.setBackground(IpnbEditorUtil.getBackground());
|
||||
|
||||
myIpnbFilePanel = createIpnbEditorPanel(myProject, vFile);
|
||||
|
||||
final JPanel controlPanel = createControlPanel();
|
||||
myEditorPanel.add(controlPanel, BorderLayout.NORTH);
|
||||
myScrollPane = ScrollPaneFactory.createScrollPane(myIpnbFilePanel);
|
||||
myScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
|
||||
myEditorPanel.add(myScrollPane, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
public JScrollPane getScrollPane() {
|
||||
return myScrollPane;
|
||||
}
|
||||
|
||||
private JPanel createControlPanel() {
|
||||
final JPanel controlPanel = new JPanel();
|
||||
controlPanel.setBackground(IpnbEditorUtil.getBackground());
|
||||
addSaveButton(controlPanel);
|
||||
addAddButton(controlPanel);
|
||||
addCutButton(controlPanel);
|
||||
addCopyButton(controlPanel);
|
||||
addPasteButton(controlPanel);
|
||||
addRunButton(controlPanel);
|
||||
|
||||
myCellTypeCombo = new ComboBox(ourCellTypes);
|
||||
|
||||
myCellTypeCombo.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
final Object selectedItem = myCellTypeCombo.getSelectedItem();
|
||||
final IpnbEditablePanel selectedCell = myIpnbFilePanel.getSelectedCell();
|
||||
if (selectedCell != null && selectedItem instanceof String) {
|
||||
updateCellType((String)selectedItem, selectedCell);
|
||||
}
|
||||
}
|
||||
});
|
||||
final IpnbPanel selectedCell = myIpnbFilePanel.getSelectedCell();
|
||||
updateCellTypeCombo(selectedCell);
|
||||
controlPanel.add(myCellTypeCombo);
|
||||
final MatteBorder border = BorderFactory.createMatteBorder(0, 0, 1, 0, JBColor.GRAY);
|
||||
controlPanel.setBorder(border);
|
||||
return controlPanel;
|
||||
}
|
||||
|
||||
private void addRunButton(@NotNull final JPanel controlPanel) {
|
||||
myRunCellButton = new JButton();
|
||||
myRunCellButton.setBackground(IpnbEditorUtil.getBackground());
|
||||
myRunCellButton.setPreferredSize(new Dimension(30, 30));
|
||||
myRunCellButton.setIcon(AllIcons.General.Run);
|
||||
myRunCellButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
final IpnbRunCellAction action = (IpnbRunCellAction)ActionManager.getInstance().getAction("IpnbRunCellAction");
|
||||
action.runCell(myIpnbFilePanel);
|
||||
}
|
||||
});
|
||||
controlPanel.add(myRunCellButton);
|
||||
}
|
||||
|
||||
private void addSaveButton(@NotNull final JPanel controlPanel) {
|
||||
addButton(controlPanel, new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
final IpnbSaveAction action = (IpnbSaveAction)ActionManager.getInstance().getAction("IpnbSaveAction");
|
||||
action.saveAndCheckpoint(IpnbFileEditor.this);
|
||||
}
|
||||
}, AllIcons.Actions.Menu_saveall);
|
||||
}
|
||||
|
||||
private void addCutButton(@NotNull final JPanel controlPanel) {
|
||||
addButton(controlPanel, new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
final IpnbCutCellAction action = (IpnbCutCellAction)ActionManager.getInstance().getAction("IpnbCutCellAction");
|
||||
action.cutCell(myIpnbFilePanel);
|
||||
}
|
||||
}, AllIcons.Actions.Menu_cut);
|
||||
}
|
||||
|
||||
private void addCopyButton(@NotNull final JPanel controlPanel) {
|
||||
addButton(controlPanel, new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
final IpnbCopyCellAction action = (IpnbCopyCellAction)ActionManager.getInstance().getAction("IpnbCopyCellAction");
|
||||
action.copyCell(myIpnbFilePanel);
|
||||
}
|
||||
}, AllIcons.Actions.Copy);
|
||||
}
|
||||
|
||||
private void addPasteButton(@NotNull final JPanel controlPanel) {
|
||||
addButton(controlPanel, new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
final IpnbPasteCellAction action = (IpnbPasteCellAction)ActionManager.getInstance().getAction("IpnbPasteCellAction");
|
||||
action.pasteCell(myIpnbFilePanel);
|
||||
}
|
||||
}, AllIcons.Actions.Menu_paste);
|
||||
}
|
||||
|
||||
private void addButton(@NotNull final JPanel controlPanel, @NotNull final ActionListener listener, @NotNull final Icon icon) {
|
||||
final JButton button = new JButton();
|
||||
button.setBackground(IpnbEditorUtil.getBackground());
|
||||
button.setPreferredSize(new Dimension(30, 30));
|
||||
button.setIcon(icon);
|
||||
button.addActionListener(listener);
|
||||
controlPanel.add(button);
|
||||
}
|
||||
|
||||
private void addAddButton(@NotNull final JPanel controlPanel) {
|
||||
addButton(controlPanel, new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
final IpnbAddCellAction action = (IpnbAddCellAction)ActionManager.getInstance().getAction("IpnbAddCellAction");
|
||||
action.addCell(myIpnbFilePanel);
|
||||
}
|
||||
}, AllIcons.General.Add);
|
||||
}
|
||||
|
||||
public JButton getRunCellButton() {
|
||||
return myRunCellButton;
|
||||
}
|
||||
|
||||
private void updateCellType(@NotNull final String selectedItem, @NotNull final IpnbEditablePanel selectedCell) {
|
||||
if (selectedCell instanceof IpnbHeadingPanel) {
|
||||
final IpnbHeadingCell cell = ((IpnbHeadingPanel)selectedCell).getCell();
|
||||
if (selectedItem.startsWith(headingCellType)) {
|
||||
final char c = selectedItem.charAt(selectedItem.length() - 1);
|
||||
final int level = Character.getNumericValue(c);
|
||||
if (level != cell.getLevel()) {
|
||||
cell.setLevel(level);
|
||||
selectedCell.updateCellView();
|
||||
}
|
||||
}
|
||||
else if (selectedItem.equals(markdownCellType)) {
|
||||
final List<IpnbCell> cells = myIpnbFilePanel.getIpnbFile().getCells();
|
||||
final int index = cells.indexOf(((IpnbHeadingPanel)selectedCell).getCell());
|
||||
final IpnbMarkdownCell markdownCell = new IpnbMarkdownCell(cell.getSource());
|
||||
if (index >= 0)
|
||||
cells.set(index, markdownCell);
|
||||
myIpnbFilePanel.replaceComponent(selectedCell, markdownCell);
|
||||
}
|
||||
else if (selectedItem.equals(codeCellType)) {
|
||||
final List<IpnbCell> cells = myIpnbFilePanel.getIpnbFile().getCells();
|
||||
final int index = cells.indexOf(((IpnbHeadingPanel)selectedCell).getCell());
|
||||
final IpnbCodeCell codeCell = new IpnbCodeCell("python", cell.getSource(), null, Lists.<IpnbOutputCell>newArrayList());
|
||||
if (index >= 0)
|
||||
cells.set(index, codeCell);
|
||||
|
||||
myIpnbFilePanel.replaceComponent(selectedCell, codeCell);
|
||||
}
|
||||
}
|
||||
else if (selectedCell instanceof IpnbMarkdownPanel) {
|
||||
final IpnbMarkdownCell cell = ((IpnbMarkdownPanel)selectedCell).getCell();
|
||||
if (selectedItem.startsWith(headingCellType)) {
|
||||
final char c = selectedItem.charAt(selectedItem.length() - 1);
|
||||
final int level = Character.getNumericValue(c);
|
||||
final List<IpnbCell> cells = myIpnbFilePanel.getIpnbFile().getCells();
|
||||
final int index = cells.indexOf(((IpnbMarkdownPanel)selectedCell).getCell());
|
||||
final IpnbHeadingCell headingCell = new IpnbHeadingCell(cell.getSource(), level);
|
||||
if (index >= 0)
|
||||
cells.set(index, headingCell);
|
||||
|
||||
myIpnbFilePanel.replaceComponent(selectedCell, headingCell);
|
||||
}
|
||||
else if (selectedItem.equals(codeCellType)) {
|
||||
final List<IpnbCell> cells = myIpnbFilePanel.getIpnbFile().getCells();
|
||||
final int index = cells.indexOf(((IpnbMarkdownPanel)selectedCell).getCell());
|
||||
final IpnbCodeCell codeCell = new IpnbCodeCell("python", cell.getSource(), null, Lists.<IpnbOutputCell>newArrayList());
|
||||
if (index >= 0)
|
||||
cells.set(index, codeCell);
|
||||
|
||||
myIpnbFilePanel.replaceComponent(selectedCell, codeCell);
|
||||
}
|
||||
}
|
||||
else if (selectedCell instanceof IpnbCodePanel) {
|
||||
final IpnbCodeCell cell = ((IpnbCodePanel)selectedCell).getCell();
|
||||
if (selectedItem.startsWith(headingCellType)) {
|
||||
final char c = selectedItem.charAt(selectedItem.length() - 1);
|
||||
final int level = Character.getNumericValue(c);
|
||||
final List<IpnbCell> cells = myIpnbFilePanel.getIpnbFile().getCells();
|
||||
final int index = cells.indexOf(((IpnbCodePanel)selectedCell).getCell());
|
||||
final IpnbHeadingCell headingCell = new IpnbHeadingCell(cell.getSource(), level);
|
||||
if (index >= 0)
|
||||
cells.set(index, headingCell);
|
||||
myIpnbFilePanel.replaceComponent(selectedCell, headingCell);
|
||||
}
|
||||
else if(selectedItem.equals(markdownCellType)) {
|
||||
final List<IpnbCell> cells = myIpnbFilePanel.getIpnbFile().getCells();
|
||||
final int index = cells.indexOf(((IpnbCodePanel)selectedCell).getCell());
|
||||
final IpnbMarkdownCell markdownCell = new IpnbMarkdownCell(cell.getSource());
|
||||
if (index >= 0)
|
||||
cells.set(index, markdownCell);
|
||||
|
||||
myIpnbFilePanel.replaceComponent(selectedCell, markdownCell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private IpnbFilePanel createIpnbEditorPanel(Project project, VirtualFile vFile) {
|
||||
return new IpnbFilePanel(project, this, vFile,
|
||||
new CellSelectionListener() {
|
||||
@Override
|
||||
public void selectionChanged(@NotNull IpnbPanel ipnbPanel) {
|
||||
if (myCellTypeCombo == null) return;
|
||||
updateCellTypeCombo(ipnbPanel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateCellTypeCombo(IpnbPanel ipnbPanel) {
|
||||
if (ipnbPanel instanceof IpnbHeadingPanel) {
|
||||
final IpnbHeadingCell cell = ((IpnbHeadingPanel)ipnbPanel).getCell();
|
||||
final int level = cell.getLevel();
|
||||
myCellTypeCombo.setSelectedItem(headingCellType + level);
|
||||
}
|
||||
else if (ipnbPanel instanceof IpnbMarkdownPanel) {
|
||||
myCellTypeCombo.setSelectedItem(markdownCellType);
|
||||
}
|
||||
else if (ipnbPanel instanceof IpnbCodePanel) {
|
||||
myCellTypeCombo.setSelectedItem(codeCellType);
|
||||
}
|
||||
}
|
||||
|
||||
public IpnbFilePanel getIpnbFilePanel() {
|
||||
return myIpnbFilePanel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return myEditorPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getPreferredFocusedComponent() {
|
||||
return myEditorPanel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public FileEditorState getState(@NotNull FileEditorStateLevel level) {
|
||||
final int index = getIpnbFilePanel().getSelectedIndex();
|
||||
final IpnbEditablePanel cell = getIpnbFilePanel().getSelectedCell();
|
||||
final int top = cell != null ? cell.getTop() : 0;
|
||||
return new IpnbEditorState(-1, index, top);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setState(@NotNull FileEditorState state) {
|
||||
final int index = ((IpnbEditorState)state).getSelectedIndex();
|
||||
final int position = ((IpnbEditorState)state).getSelectedTop();
|
||||
myIpnbFilePanel.setInitialPosition(index, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModified() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectNotify() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deselectNotify() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPropertyChangeListener(@NotNull PropertyChangeListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePropertyChangeListener(@NotNull PropertyChangeListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BackgroundEditorHighlighter getBackgroundHighlighter() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileEditorLocation getCurrentLocation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureViewBuilder getStructureViewBuilder() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
Disposer.dispose(myEditor);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Editor getEditor() {
|
||||
return myEditor.getEditor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canNavigateTo(@NotNull Navigatable navigatable) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void navigateTo(@NotNull Navigatable navigatable) {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static TextEditor createEditor(@NotNull Project project, @NotNull VirtualFile vFile) {
|
||||
FileEditorProvider provider = getProvider(project, vFile);
|
||||
|
||||
if (provider != null) {
|
||||
FileEditor editor = provider.createEditor(project, vFile);
|
||||
if (editor instanceof TextEditor) {
|
||||
return (TextEditor)editor;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static FileEditorProvider getProvider(Project project, VirtualFile vFile) {
|
||||
FileEditorProvider[] providers = FileEditorProviderManagerImpl.getInstance().getProviders(project, vFile);
|
||||
for (FileEditorProvider provider : providers) {
|
||||
if (!(provider instanceof IpnbEditorProvider)) {
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract class CellSelectionListener {
|
||||
public abstract void selectionChanged(@NotNull final IpnbPanel ipnbPanel);
|
||||
}
|
||||
|
||||
public VirtualFile getVirtualFile() {
|
||||
return myFile;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.jetbrains.plugins.ipnb.editor.actions;
|
||||
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.editor.IpnbFileEditor;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.IpnbFilePanel;
|
||||
|
||||
public class IpnbAddCellAction extends AnAction {
|
||||
public IpnbAddCellAction() {
|
||||
super(AllIcons.General.Run);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent event) {
|
||||
final DataContext context = event.getDataContext();
|
||||
final FileEditor editor = PlatformDataKeys.FILE_EDITOR.getData(context);
|
||||
if (editor instanceof IpnbFileEditor) {
|
||||
final IpnbFilePanel component = ((IpnbFileEditor)editor).getIpnbFilePanel();
|
||||
addCell(component);
|
||||
}
|
||||
}
|
||||
|
||||
public void addCell(@NotNull final IpnbFilePanel ipnbFilePanel) {
|
||||
ipnbFilePanel.createAndAddCell();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.jetbrains.plugins.ipnb.editor.actions;
|
||||
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.editor.IpnbFileEditor;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.IpnbFilePanel;
|
||||
|
||||
public class IpnbCopyCellAction extends AnAction {
|
||||
public IpnbCopyCellAction() {
|
||||
super(AllIcons.General.Run);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent event) {
|
||||
final DataContext context = event.getDataContext();
|
||||
final FileEditor editor = PlatformDataKeys.FILE_EDITOR.getData(context);
|
||||
if (editor instanceof IpnbFileEditor) {
|
||||
final IpnbFilePanel component = ((IpnbFileEditor)editor).getIpnbFilePanel();
|
||||
copyCell(component);
|
||||
}
|
||||
}
|
||||
|
||||
public void copyCell(@NotNull final IpnbFilePanel ipnbFilePanel) {
|
||||
ipnbFilePanel.copyCell();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.jetbrains.plugins.ipnb.editor.actions;
|
||||
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.editor.IpnbFileEditor;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.IpnbFilePanel;
|
||||
|
||||
public class IpnbCutCellAction extends AnAction {
|
||||
public IpnbCutCellAction() {
|
||||
super(AllIcons.General.Run);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent event) {
|
||||
final DataContext context = event.getDataContext();
|
||||
final FileEditor editor = PlatformDataKeys.FILE_EDITOR.getData(context);
|
||||
if (editor instanceof IpnbFileEditor) {
|
||||
final IpnbFilePanel component = ((IpnbFileEditor)editor).getIpnbFilePanel();
|
||||
cutCell(component);
|
||||
}
|
||||
}
|
||||
|
||||
public void cutCell(@NotNull final IpnbFilePanel ipnbFilePanel) {
|
||||
ipnbFilePanel.cutCell();
|
||||
ipnbFilePanel.revalidate();
|
||||
ipnbFilePanel.repaint();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.jetbrains.plugins.ipnb.editor.actions;
|
||||
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.editor.IpnbFileEditor;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.IpnbFilePanel;
|
||||
|
||||
public class IpnbPasteCellAction extends AnAction {
|
||||
public IpnbPasteCellAction() {
|
||||
super(AllIcons.General.Run);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent event) {
|
||||
final DataContext context = event.getDataContext();
|
||||
final FileEditor editor = PlatformDataKeys.FILE_EDITOR.getData(context);
|
||||
if (editor instanceof IpnbFileEditor) {
|
||||
final IpnbFilePanel component = ((IpnbFileEditor)editor).getIpnbFilePanel();
|
||||
pasteCell(component);
|
||||
}
|
||||
}
|
||||
|
||||
public void pasteCell(@NotNull final IpnbFilePanel ipnbFilePanel) {
|
||||
ipnbFilePanel.pasteCell();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.jetbrains.plugins.ipnb.editor.actions;
|
||||
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.editor.IpnbFileEditor;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.IpnbEditablePanel;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.IpnbFilePanel;
|
||||
|
||||
public class IpnbRunCellAction extends AnAction {
|
||||
public IpnbRunCellAction() {
|
||||
super(AllIcons.General.Run);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent event) {
|
||||
final DataContext context = event.getDataContext();
|
||||
final FileEditor editor = PlatformDataKeys.FILE_EDITOR.getData(context);
|
||||
if (editor instanceof IpnbFileEditor) {
|
||||
final IpnbFilePanel component = ((IpnbFileEditor)editor).getIpnbFilePanel();
|
||||
runCell(component);
|
||||
}
|
||||
}
|
||||
|
||||
public void runCell(@NotNull final IpnbFilePanel ipnbFilePanel) {
|
||||
final IpnbEditablePanel cell = ipnbFilePanel.getSelectedCell();
|
||||
cell.runCell();
|
||||
ipnbFilePanel.selectNext(cell);
|
||||
ipnbFilePanel.revalidate();
|
||||
ipnbFilePanel.repaint();
|
||||
ipnbFilePanel.requestFocus();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.jetbrains.plugins.ipnb.editor.actions;
|
||||
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.editor.IpnbFileEditor;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.IpnbFilePanel;
|
||||
import org.jetbrains.plugins.ipnb.format.IpnbParser;
|
||||
|
||||
public class IpnbSaveAction extends AnAction {
|
||||
public IpnbSaveAction() {
|
||||
super(AllIcons.General.Run);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent event) {
|
||||
final DataContext context = event.getDataContext();
|
||||
final FileEditor editor = PlatformDataKeys.FILE_EDITOR.getData(context);
|
||||
if (editor instanceof IpnbFileEditor) {
|
||||
saveAndCheckpoint((IpnbFileEditor)editor);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveAndCheckpoint(@NotNull final IpnbFileEditor editor) {
|
||||
final IpnbFilePanel filePanel = editor.getIpnbFilePanel();
|
||||
IpnbParser.saveIpnbFile(filePanel);
|
||||
final VirtualFile file = editor.getVirtualFile();
|
||||
file.refresh(false, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
package org.jetbrains.plugins.ipnb.editor.panels;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.ui.Gray;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.IpnbEditableCell;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
public abstract class IpnbEditablePanel<T extends JComponent, K extends IpnbEditableCell> extends IpnbPanel<T, K> {
|
||||
private static final Logger LOG = Logger.getInstance(IpnbEditablePanel.class);
|
||||
private boolean myEditing;
|
||||
protected JTextArea myEditablePanel;
|
||||
public final static String EDITABLE_PANEL = "Editable panel";
|
||||
public final static String VIEW_PANEL = "View panel";
|
||||
|
||||
public IpnbEditablePanel(@NotNull K cell) {
|
||||
super(cell);
|
||||
}
|
||||
|
||||
|
||||
protected void initPanel() {
|
||||
myViewPanel = createViewPanel();
|
||||
myViewPanel.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
final Container parent = getParent();
|
||||
final MouseEvent parentEvent = SwingUtilities.convertMouseEvent(myViewPanel, e, parent);
|
||||
parent.dispatchEvent(parentEvent);
|
||||
if (e.getClickCount() == 2) {
|
||||
switchToEditing();
|
||||
}
|
||||
}
|
||||
});
|
||||
myViewPanel.setName(VIEW_PANEL);
|
||||
add(myViewPanel, VIEW_PANEL);
|
||||
myEditablePanel = createEditablePanel();
|
||||
myEditablePanel.setName(EDITABLE_PANEL);
|
||||
add(myEditablePanel, EDITABLE_PANEL);
|
||||
}
|
||||
|
||||
public void switchToEditing() {
|
||||
setEditing(true);
|
||||
|
||||
final LayoutManager layout = getLayout();
|
||||
if (layout instanceof CardLayout) {
|
||||
((CardLayout)layout).show(this, EDITABLE_PANEL);
|
||||
UIUtil.requestFocus(myEditablePanel);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isModified() {
|
||||
final Component[] components = getComponents();
|
||||
for (Component component : components) {
|
||||
final String name = component.getName();
|
||||
if (component.isVisible() && EDITABLE_PANEL.equals(name)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected String getRawCellText() { return ""; }
|
||||
|
||||
public void runCell() {
|
||||
final LayoutManager layout = getLayout();
|
||||
if (layout instanceof CardLayout) {
|
||||
updateCellSource();
|
||||
updateCellView();
|
||||
((CardLayout)layout).show(this, VIEW_PANEL);
|
||||
setEditing(false);
|
||||
}
|
||||
}
|
||||
|
||||
private JTextArea createEditablePanel() {
|
||||
final JTextArea textArea = new JTextArea(getRawCellText());
|
||||
textArea.setLineWrap(true);
|
||||
textArea.setEditable(true);
|
||||
textArea.setBorder(BorderFactory.createLineBorder(JBColor.lightGray));
|
||||
textArea.setBackground(Gray._247);
|
||||
textArea.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (e.getClickCount() == 1) {
|
||||
setEditing(true);
|
||||
final Container parent = getParent();
|
||||
parent.repaint();
|
||||
if (parent instanceof IpnbFilePanel) {
|
||||
((IpnbFilePanel)parent).setSelectedCell(IpnbEditablePanel.this);
|
||||
textArea.requestFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
textArea.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
|
||||
setEditing(false);
|
||||
final Container parent = getParent();
|
||||
if (parent instanceof IpnbFilePanel) {
|
||||
parent.repaint();
|
||||
UIUtil.requestFocus((IpnbFilePanel)parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return textArea;
|
||||
}
|
||||
|
||||
public boolean contains(int y) {
|
||||
return y>= getTop() && y<=getBottom();
|
||||
}
|
||||
|
||||
public int getTop() {
|
||||
return getY();
|
||||
}
|
||||
|
||||
public int getBottom() {
|
||||
return getTop() + getHeight();
|
||||
}
|
||||
|
||||
public boolean isEditing() {
|
||||
return myEditing;
|
||||
}
|
||||
|
||||
public void setEditing(boolean editing) {
|
||||
myEditing = editing;
|
||||
}
|
||||
|
||||
public void updateCellView() { // TODO: make abstract
|
||||
}
|
||||
|
||||
public void updateCellSource() {
|
||||
final String text = myEditablePanel.getText();
|
||||
myCell.setSource(StringUtil.splitByLinesKeepSeparators(text != null ? text : ""));
|
||||
}
|
||||
|
||||
@SuppressWarnings("CloneDoesntDeclareCloneNotSupportedException")
|
||||
@Override
|
||||
protected Object clone() {
|
||||
try {
|
||||
return super.clone();
|
||||
}
|
||||
catch (CloneNotSupportedException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public K getCell() {
|
||||
return myCell;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.plugins.ipnb.editor.panels;
|
||||
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
|
||||
/**
|
||||
* @author traff
|
||||
*/
|
||||
public interface IpnbEditorPanel {
|
||||
Editor getEditor();
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
package org.jetbrains.plugins.ipnb.editor.panels;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.openapi.actionSystem.DataProvider;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.ipnb.editor.IpnbEditorUtil;
|
||||
import org.jetbrains.plugins.ipnb.editor.IpnbFileEditor;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.code.IpnbCodePanel;
|
||||
import org.jetbrains.plugins.ipnb.format.IpnbFile;
|
||||
import org.jetbrains.plugins.ipnb.format.IpnbParser;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.*;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.output.IpnbOutputCell;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider {
|
||||
|
||||
public static final int INSET_Y = 10;
|
||||
public static final int INSET_X = 5;
|
||||
private IpnbFile myIpnbFile;
|
||||
|
||||
private Project myProject;
|
||||
@NotNull private IpnbFileEditor myParent;
|
||||
@NotNull private final IpnbFileEditor.CellSelectionListener myListener;
|
||||
|
||||
private final List<IpnbEditablePanel> myIpnbPanels = Lists.newArrayList();
|
||||
|
||||
private IpnbEditablePanel mySelectedCell;
|
||||
private IpnbEditablePanel myBufferPanel;
|
||||
private int myIncrement = 10;
|
||||
private int myInitialSelection = 0;
|
||||
private int myInitialPosition = 0;
|
||||
|
||||
public IpnbFilePanel(@NotNull final Project project, @NotNull final IpnbFileEditor parent, @NotNull final VirtualFile vFile,
|
||||
@NotNull final IpnbFileEditor.CellSelectionListener listener) {
|
||||
super(new GridBagLayout());
|
||||
myProject = project;
|
||||
myParent = parent;
|
||||
myListener = listener;
|
||||
setBackground(IpnbEditorUtil.getBackground());
|
||||
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
myIpnbFile = IpnbParser.parseIpnbFile(vFile);
|
||||
layoutFile();
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
updateCellSelection(e);
|
||||
}
|
||||
});
|
||||
setFocusable(true);
|
||||
|
||||
}
|
||||
catch (IOException e) {
|
||||
Messages.showErrorDialog(project, e.getMessage(), "Can't open " + vFile.getPath());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
UIUtil.requestFocus(this);
|
||||
}
|
||||
|
||||
public List<IpnbEditablePanel> getIpnbPanels() {
|
||||
return myIpnbPanels;
|
||||
}
|
||||
|
||||
private void layoutFile() {
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.gridx = 0;
|
||||
c.gridy = 0;
|
||||
c.gridwidth = 2;
|
||||
c.insets = new Insets(INSET_Y, INSET_X, 0, 0);
|
||||
|
||||
final int width = IpnbEditorUtil.PANEL_WIDTH + IpnbEditorUtil.PROMPT_SIZE.width;
|
||||
final JLabel label = new JLabel("<html><body style='width: " + width + "px'></body></html>");
|
||||
add(label, c);
|
||||
|
||||
c.gridy = 1;
|
||||
c.gridwidth = 1;
|
||||
final JPanel panel = new JPanel();
|
||||
panel.setPreferredSize(IpnbEditorUtil.PROMPT_SIZE);
|
||||
panel.setBackground(getBackground());
|
||||
panel.setOpaque(false);
|
||||
add(panel, c);
|
||||
|
||||
final List<IpnbCell> cells = myIpnbFile.getCells();
|
||||
for (IpnbCell cell : cells) {
|
||||
c.gridy = addCellToPanel(cell, c);
|
||||
}
|
||||
|
||||
if (myInitialSelection >= 0 && myIpnbPanels.size() > myInitialSelection) {
|
||||
final IpnbEditablePanel toSelect = myIpnbPanels.get(myInitialSelection);
|
||||
setSelectedCell(toSelect);
|
||||
myParent.getScrollPane().getViewport().setViewPosition(new Point(0, myInitialPosition));
|
||||
}
|
||||
c.weighty = 1;
|
||||
add(createEmptyPanel(), c);
|
||||
}
|
||||
|
||||
private int addCellToPanel(IpnbCell cell, GridBagConstraints c) {
|
||||
IpnbEditablePanel panel;
|
||||
if (cell instanceof IpnbCodeCell) {
|
||||
panel = new IpnbCodePanel(myProject, myParent, (IpnbCodeCell)cell);
|
||||
c.gridwidth = 2;
|
||||
c.gridx = 0;
|
||||
add(panel, c);
|
||||
myIpnbPanels.add(panel);
|
||||
}
|
||||
else if (cell instanceof IpnbMarkdownCell) {
|
||||
panel = new IpnbMarkdownPanel((IpnbMarkdownCell)cell);
|
||||
addComponent(c, panel);
|
||||
}
|
||||
else if (cell instanceof IpnbHeadingCell) {
|
||||
panel = new IpnbHeadingPanel((IpnbHeadingCell)cell);
|
||||
addComponent(c, panel);
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedOperationException(cell.getClass().toString());
|
||||
}
|
||||
return c.gridy + 1;
|
||||
}
|
||||
|
||||
public void createAndAddCell() {
|
||||
removeAll();
|
||||
final IpnbCodeCell cell = new IpnbCodeCell("python", new String[]{""}, null, new ArrayList<IpnbOutputCell>());
|
||||
final IpnbCodePanel codePanel = new IpnbCodePanel(myProject, myParent, cell);
|
||||
|
||||
addCell(cell, codePanel);
|
||||
}
|
||||
|
||||
private void addCell(IpnbEditableCell cell, IpnbEditablePanel panel) {
|
||||
final IpnbEditablePanel selectedCell = getSelectedCell();
|
||||
final int index = myIpnbPanels.indexOf(selectedCell);
|
||||
myIpnbFile.addCell(cell, index+1);
|
||||
myIpnbPanels.add(index + 1, panel);
|
||||
|
||||
final GridBagConstraints c = new GridBagConstraints();
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.gridx = 0;
|
||||
c.gridy = 0;
|
||||
c.gridwidth = 1;
|
||||
c.insets = new Insets(INSET_Y, INSET_X, 0, 0);
|
||||
|
||||
final JPanel promptPanel = new JPanel();
|
||||
promptPanel.setPreferredSize(new Dimension(IpnbEditorUtil.PROMPT_SIZE.width, 1));
|
||||
promptPanel.setBackground(getBackground());
|
||||
promptPanel.setOpaque(false);
|
||||
add(promptPanel, c);
|
||||
|
||||
c.gridy += 1;
|
||||
for (IpnbPanel comp : myIpnbPanels) {
|
||||
c.gridwidth = 1;
|
||||
c.gridx = 1;
|
||||
if (comp instanceof IpnbCodePanel) {
|
||||
c.gridwidth = 2;
|
||||
c.gridx = 0;
|
||||
add(comp, c);
|
||||
}
|
||||
else {
|
||||
add(comp, c);
|
||||
}
|
||||
c.gridy += 1;
|
||||
}
|
||||
c.weighty = 1;
|
||||
add(createEmptyPanel(), c);
|
||||
|
||||
setSelectedCell(panel);
|
||||
requestFocus();
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
|
||||
public void cutCell() {
|
||||
myBufferPanel = getSelectedCell();
|
||||
selectNextOrPrev(myBufferPanel);
|
||||
final int index = myIpnbPanels.indexOf(myBufferPanel);
|
||||
if (index < 0) return;
|
||||
myIpnbPanels.remove(index);
|
||||
myIpnbFile.removeCell(index);
|
||||
|
||||
remove(myBufferPanel);
|
||||
}
|
||||
|
||||
public void copyCell() {
|
||||
myBufferPanel = getSelectedCell();
|
||||
}
|
||||
|
||||
public void pasteCell() {
|
||||
if (myBufferPanel == null) return;
|
||||
removeAll();
|
||||
final IpnbEditablePanel editablePanel = (IpnbEditablePanel)myBufferPanel.clone();
|
||||
addCell(editablePanel.getCell(), editablePanel);
|
||||
}
|
||||
|
||||
public void replaceComponent(@NotNull final IpnbEditablePanel from, @NotNull final IpnbCell cell) {
|
||||
final GridBagConstraints c = ((GridBagLayout)getLayout()).getConstraints(from);
|
||||
final int index = myIpnbPanels.indexOf(from);
|
||||
IpnbEditablePanel panel;
|
||||
if (cell instanceof IpnbCodeCell) {
|
||||
panel = new IpnbCodePanel(myProject, myParent, (IpnbCodeCell)cell);
|
||||
c.gridwidth = 2;
|
||||
c.gridx = 0;
|
||||
add(panel, c);
|
||||
}
|
||||
else if (cell instanceof IpnbMarkdownCell) {
|
||||
panel = new IpnbMarkdownPanel((IpnbMarkdownCell)cell);
|
||||
c.gridwidth = 1;
|
||||
c.gridx = 1;
|
||||
add(panel, c);
|
||||
}
|
||||
else if (cell instanceof IpnbHeadingCell) {
|
||||
panel = new IpnbHeadingPanel((IpnbHeadingCell)cell);
|
||||
c.gridwidth = 1;
|
||||
c.gridx = 1;
|
||||
add(panel, c);
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedOperationException(cell.getClass().toString());
|
||||
}
|
||||
if (index >= 0) {
|
||||
myIpnbPanels.remove(index);
|
||||
myIpnbPanels.add(index, panel);
|
||||
}
|
||||
setSelectedCell(panel);
|
||||
remove(from);
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void addComponent(@NotNull final GridBagConstraints c, @NotNull final IpnbEditablePanel comp) {
|
||||
c.gridwidth = 1;
|
||||
c.gridx = 1;
|
||||
add(comp, c);
|
||||
|
||||
myIpnbPanels.add(comp);
|
||||
}
|
||||
|
||||
private JPanel createEmptyPanel() {
|
||||
JPanel panel = new JPanel();
|
||||
panel.setBackground(IpnbEditorUtil.getBackground());
|
||||
return panel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processKeyEvent(KeyEvent e) {
|
||||
if (mySelectedCell != null && e.getID() == KeyEvent.KEY_PRESSED) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
||||
mySelectedCell.switchToEditing();
|
||||
}
|
||||
int index = myIpnbPanels.indexOf(mySelectedCell);
|
||||
final Rectangle rect = getVisibleRect();
|
||||
|
||||
|
||||
if (e.getKeyCode() == KeyEvent.VK_UP) {
|
||||
selectPrev(mySelectedCell);
|
||||
if (index > 0) {
|
||||
final Rectangle cellBounds = mySelectedCell.getBounds();
|
||||
if (cellBounds.getY() + cellBounds.getHeight() <= rect.getY()) {
|
||||
myIncrement = rect.y - cellBounds.y;
|
||||
getParent().dispatchEvent(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
|
||||
selectNext(mySelectedCell);
|
||||
if (index < myIpnbPanels.size() - 1) {
|
||||
final Rectangle cellBounds = mySelectedCell.getBounds();
|
||||
if (cellBounds.getY() > rect.getY() + rect.getHeight()) {
|
||||
myIncrement = cellBounds.y + cellBounds.height - rect.y - rect.height;
|
||||
getParent().dispatchEvent(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
getParent().dispatchEvent(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void selectPrev(@NotNull IpnbEditablePanel cell) {
|
||||
int index = myIpnbPanels.indexOf(cell);
|
||||
if (index > 0) {
|
||||
setSelectedCell(myIpnbPanels.get(index - 1));
|
||||
}
|
||||
}
|
||||
|
||||
public void selectNext(@NotNull IpnbEditablePanel cell) {
|
||||
int index = myIpnbPanels.indexOf(cell);
|
||||
if (index < myIpnbPanels.size() - 1) {
|
||||
setSelectedCell(myIpnbPanels.get(index + 1));
|
||||
}
|
||||
}
|
||||
|
||||
public void selectNextOrPrev(@NotNull IpnbEditablePanel cell) {
|
||||
int index = myIpnbPanels.indexOf(cell);
|
||||
if (index < myIpnbPanels.size() - 1) {
|
||||
setSelectedCell(myIpnbPanels.get(index + 1));
|
||||
}
|
||||
else if (index > 0) {
|
||||
setSelectedCell(myIpnbPanels.get(index - 1));
|
||||
}
|
||||
else {
|
||||
mySelectedCell = null;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
if (mySelectedCell != null) {
|
||||
g.setColor(mySelectedCell.isEditing() ? JBColor.GREEN : JBColor.GRAY);
|
||||
g.drawRoundRect(100, mySelectedCell.getTop() - 1, getWidth() - 200, mySelectedCell.getHeight() + 2, 5, 5);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCellSelection(MouseEvent e) {
|
||||
if (e.getClickCount() > 0) {
|
||||
IpnbEditablePanel ipnbPanel = getIpnbPanelByClick(e.getPoint());
|
||||
if (ipnbPanel != null) {
|
||||
ipnbPanel.setEditing(false);
|
||||
ipnbPanel.requestFocus();
|
||||
repaint();
|
||||
setSelectedCell(ipnbPanel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setInitialPosition(int index, int position) {
|
||||
myInitialSelection = index;
|
||||
myInitialPosition = position;
|
||||
}
|
||||
|
||||
public void setSelectedCell(@NotNull final IpnbEditablePanel ipnbPanel) {
|
||||
if (ipnbPanel.equals(mySelectedCell)) return;
|
||||
if (mySelectedCell != null)
|
||||
mySelectedCell.setEditing(false);
|
||||
mySelectedCell = ipnbPanel;
|
||||
revalidate();
|
||||
UIUtil.requestFocus(this);
|
||||
repaint();
|
||||
myListener.selectionChanged(ipnbPanel);
|
||||
}
|
||||
|
||||
public IpnbEditablePanel getSelectedCell() {
|
||||
return mySelectedCell;
|
||||
}
|
||||
|
||||
public int getSelectedIndex() {
|
||||
final IpnbEditablePanel selectedCell = getSelectedCell();
|
||||
return myIpnbPanels.indexOf(selectedCell);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private IpnbEditablePanel getIpnbPanelByClick(@NotNull final Point point) {
|
||||
for (IpnbEditablePanel c: myIpnbPanels) {
|
||||
if (c.contains(point.y)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public IpnbFile getIpnbFile() {
|
||||
return myIpnbFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredScrollableViewportSize() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
|
||||
return myIncrement;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getScrollableTracksViewportWidth() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getScrollableTracksViewportHeight() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getData(String dataId) {
|
||||
final IpnbEditablePanel cell = getSelectedCell();
|
||||
if (OpenFileDescriptor.NAVIGATE_IN_EDITOR.is(dataId)) {
|
||||
if (cell instanceof IpnbCodePanel) {
|
||||
return ((IpnbCodePanel)cell).getEditor();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.jetbrains.plugins.ipnb.editor.panels;
|
||||
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.editor.IpnbEditorUtil;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.IpnbHeadingCell;
|
||||
|
||||
public class IpnbHeadingPanel extends IpnbEditablePanel<JBLabel, IpnbHeadingCell> {
|
||||
|
||||
public IpnbHeadingPanel(@NotNull final IpnbHeadingCell cell) {
|
||||
super(cell);
|
||||
initPanel();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getRawCellText() {
|
||||
return myCell.getSourceAsString();
|
||||
}
|
||||
|
||||
private String renderCellText() {
|
||||
return "<html><body style='width: " + IpnbEditorUtil.PANEL_WIDTH + "px'><h" + myCell.getLevel() + ">" + myCell.getSourceAsString() + "</h" + myCell.getLevel() +
|
||||
"></body></html>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCellView() {
|
||||
myViewPanel.setText(renderCellText());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JBLabel createViewPanel() {
|
||||
final JBLabel label = new JBLabel(renderCellText());
|
||||
label.setBackground(IpnbEditorUtil.getBackground());
|
||||
label.setOpaque(true);
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
@SuppressWarnings("CloneDoesntCallSuperClone")
|
||||
@Override
|
||||
protected Object clone() {
|
||||
return new IpnbHeadingPanel((IpnbHeadingCell)myCell.clone());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.jetbrains.plugins.ipnb.editor.panels;
|
||||
|
||||
import com.intellij.openapi.ui.VerticalFlowLayout;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.IpnbUtils;
|
||||
import org.jetbrains.plugins.ipnb.editor.IpnbEditorUtil;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.IpnbMarkdownCell;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class IpnbMarkdownPanel extends IpnbEditablePanel<JPanel, IpnbMarkdownCell> {
|
||||
|
||||
public IpnbMarkdownPanel(@NotNull final IpnbMarkdownCell cell) {
|
||||
super(cell);
|
||||
initPanel();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getRawCellText() {
|
||||
return myCell.getSourceAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JPanel createViewPanel() {
|
||||
final JPanel panel = new JPanel(new VerticalFlowLayout(FlowLayout.LEFT, false, true));
|
||||
updatePanel(panel);
|
||||
panel.setBackground(IpnbEditorUtil.getBackground());
|
||||
panel.setOpaque(true);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void updatePanel(@NotNull final JPanel panel) {
|
||||
panel.removeAll();
|
||||
IpnbUtils.addLatexToPanel(myCell.getSourceAsString(), panel);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void updateCellView() {
|
||||
updatePanel(myViewPanel);
|
||||
}
|
||||
|
||||
@SuppressWarnings("CloneDoesntCallSuperClone")
|
||||
@Override
|
||||
protected Object clone() {
|
||||
return new IpnbMarkdownPanel((IpnbMarkdownCell)myCell.clone());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.jetbrains.plugins.ipnb.editor.panels;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.IpnbCell;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public abstract class IpnbPanel<T extends JComponent, K extends IpnbCell> extends JPanel {
|
||||
protected T myViewPanel;
|
||||
protected K myCell;
|
||||
|
||||
public IpnbPanel(@NotNull final K cell) {
|
||||
super(new CardLayout());
|
||||
myCell = cell;
|
||||
}
|
||||
|
||||
public K getCell() {
|
||||
return myCell;
|
||||
}
|
||||
|
||||
protected abstract T createViewPanel();
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package org.jetbrains.plugins.ipnb.editor.panels.code;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.IpnbPanel;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.output.IpnbOutputCell;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class IpnbCodeOutputPanel<K extends IpnbOutputCell> extends IpnbPanel<JComponent, K> {
|
||||
|
||||
public IpnbCodeOutputPanel(@NotNull final K cell) {
|
||||
super(cell);
|
||||
myViewPanel = createViewPanel();
|
||||
add(myViewPanel);
|
||||
}
|
||||
|
||||
protected JComponent createViewPanel() {
|
||||
JTextArea textArea = new JTextArea(myCell.getSourceAsString());
|
||||
textArea.setEditable(false);
|
||||
return textArea;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package org.jetbrains.plugins.ipnb.editor.panels.code;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.ipnb.configuration.IpnbConnectionManager;
|
||||
import org.jetbrains.plugins.ipnb.editor.IpnbEditorUtil;
|
||||
import org.jetbrains.plugins.ipnb.editor.IpnbFileEditor;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.IpnbEditablePanel;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.IpnbPanel;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.IpnbCodeCell;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.output.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
|
||||
public class IpnbCodePanel extends IpnbEditablePanel<JComponent, IpnbCodeCell> {
|
||||
private final Project myProject;
|
||||
private final Disposable myParent;
|
||||
private IpnbCodeSourcePanel myCodeSourcePanel;
|
||||
private final List<IpnbPanel> myOutputPanels = Lists.newArrayList();
|
||||
|
||||
public IpnbCodePanel(@NotNull final Project project, @Nullable final Disposable parent, @NotNull final IpnbCodeCell cell) {
|
||||
super(cell);
|
||||
myProject = project;
|
||||
myParent = parent;
|
||||
|
||||
myViewPanel = createViewPanel();
|
||||
add(myViewPanel);
|
||||
}
|
||||
|
||||
public IpnbFileEditor getFileEditor() {
|
||||
assert myParent instanceof IpnbFileEditor;
|
||||
return (IpnbFileEditor)myParent;
|
||||
}
|
||||
|
||||
public Editor getEditor() {
|
||||
return myCodeSourcePanel.getEditor();
|
||||
}
|
||||
|
||||
public void addPromptPanel(@NotNull final JComponent parent, Integer promptNumber,
|
||||
@NotNull final IpnbEditorUtil.PromptType promptType,
|
||||
@NotNull final IpnbPanel component, @NotNull final GridBagConstraints c) {
|
||||
c.gridx = 0;
|
||||
c.weightx = 0;
|
||||
c.anchor = GridBagConstraints.NORTHWEST;
|
||||
final JComponent promptComponent = IpnbEditorUtil.createPromptComponent(promptNumber, promptType);
|
||||
c.insets = new Insets(2,2,2,5);
|
||||
parent.add(promptComponent, c);
|
||||
|
||||
c.gridx = 1;
|
||||
c.weightx = 1;
|
||||
c.insets = new Insets(2,2,2,2);
|
||||
c.anchor = GridBagConstraints.CENTER;
|
||||
parent.add(component, c);
|
||||
myOutputPanels.add(component);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createViewPanel() {
|
||||
final JPanel panel = new JPanel(new GridBagLayout());
|
||||
panel.setBackground(IpnbEditorUtil.getBackground());
|
||||
|
||||
final GridBagConstraints c = new GridBagConstraints();
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.gridx = 0;
|
||||
c.gridy = 0;
|
||||
c.gridwidth = 1;
|
||||
|
||||
myCodeSourcePanel = new IpnbCodeSourcePanel(myProject, this, myCell);
|
||||
if (myParent != null)
|
||||
Disposer.register(myParent, new Disposable() {
|
||||
@Override
|
||||
public void dispose() {
|
||||
EditorFactory.getInstance().releaseEditor(myCodeSourcePanel.getEditor());
|
||||
}
|
||||
});
|
||||
addPromptPanel(panel, myCell.getPromptNumber(), IpnbEditorUtil.PromptType.In, myCodeSourcePanel, c);
|
||||
|
||||
c.gridx = 1;
|
||||
|
||||
c.gridy = 0;
|
||||
for (IpnbOutputCell outputCell : myCell.getCellOutputs()) {
|
||||
c.gridy++;
|
||||
addOutputPanel(panel, c, outputCell, true);
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void addOutputPanel(@NotNull final JComponent panel, @NotNull final GridBagConstraints c,
|
||||
@NotNull final IpnbOutputCell outputCell, boolean addPrompt) {
|
||||
final IpnbEditorUtil.PromptType promptType = addPrompt ? IpnbEditorUtil.PromptType.Out : IpnbEditorUtil.PromptType.None;
|
||||
if (outputCell instanceof IpnbImageOutputCell) {
|
||||
addPromptPanel(panel, myCell.getPromptNumber(), promptType,
|
||||
new IpnbImagePanel((IpnbImageOutputCell)outputCell), c);
|
||||
}
|
||||
else if (outputCell instanceof IpnbHtmlOutputCell) {
|
||||
addPromptPanel(panel, myCell.getPromptNumber(), promptType,
|
||||
new IpnbHtmlPanel((IpnbHtmlOutputCell)outputCell), c);
|
||||
}
|
||||
else if (outputCell instanceof IpnbLatexOutputCell) {
|
||||
addPromptPanel(panel, myCell.getPromptNumber(), promptType,
|
||||
new IpnbLatexPanel((IpnbLatexOutputCell)outputCell), c);
|
||||
}
|
||||
else if (outputCell instanceof IpnbErrorOutputCell) {
|
||||
addPromptPanel(panel, myCell.getPromptNumber(), promptType,
|
||||
new IpnbErrorPanel((IpnbErrorOutputCell)outputCell), c);
|
||||
}
|
||||
else if (outputCell instanceof IpnbStreamOutputCell) {
|
||||
addPromptPanel(panel, myCell.getPromptNumber(), IpnbEditorUtil.PromptType.None,
|
||||
new IpnbStreamPanel((IpnbStreamOutputCell)outputCell), c);
|
||||
}
|
||||
else if (outputCell.getSourceAsString() != null) {
|
||||
addPromptPanel(panel, myCell.getPromptNumber(), promptType,
|
||||
new IpnbCodeOutputPanel<IpnbOutputCell>(outputCell), c);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchToEditing() {
|
||||
setEditing(true);
|
||||
getParent().repaint();
|
||||
UIUtil.requestFocus(myCodeSourcePanel.getEditor().getContentComponent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCell() {
|
||||
super.runCell();
|
||||
updateCellSource();
|
||||
myCell.setPromptNumber(-1);
|
||||
updatePanel(myCell.getCellOutputs());
|
||||
final IpnbConnectionManager connectionManager = IpnbConnectionManager.getInstance(myProject);
|
||||
connectionManager.executeCell(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModified() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCellSource() {
|
||||
final Document document = myCodeSourcePanel.getEditor().getDocument();
|
||||
final String text = document.getText();
|
||||
myCell.setSource(StringUtil.splitByLinesKeepSeparators(text));
|
||||
}
|
||||
|
||||
public void updatePanel(@NotNull final List<IpnbOutputCell> outputContent) {
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myCell.removeCellOutputs();
|
||||
|
||||
myViewPanel.removeAll();
|
||||
final GridBagConstraints c = new GridBagConstraints();
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.gridx = 0;
|
||||
c.gridy = 0;
|
||||
c.gridwidth = 1;
|
||||
|
||||
addPromptPanel(myViewPanel, myCell.getPromptNumber(), IpnbEditorUtil.PromptType.In, myCodeSourcePanel, c);
|
||||
|
||||
for (IpnbOutputCell output : outputContent) {
|
||||
myCell.addCellOutput(output);
|
||||
c.gridx = 0;
|
||||
c.gridy += 1;
|
||||
|
||||
addOutputPanel(myViewPanel, c, output, output instanceof IpnbOutOutputCell);
|
||||
}
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings({"CloneDoesntCallSuperClone", "CloneDoesntDeclareCloneNotSupportedException"})
|
||||
@Override
|
||||
protected Object clone() {
|
||||
return new IpnbCodePanel(myProject, myParent, (IpnbCodeCell)myCell.clone());
|
||||
}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.plugins.ipnb.editor.panels.code;
|
||||
|
||||
import com.intellij.openapi.actionSystem.ActionManager;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.VerticalFlowLayout;
|
||||
import com.intellij.ui.Gray;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.editor.IpnbEditorUtil;
|
||||
import org.jetbrains.plugins.ipnb.editor.actions.IpnbRunCellAction;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.IpnbEditorPanel;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.IpnbFilePanel;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.IpnbPanel;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.IpnbCodeCell;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
|
||||
/**
|
||||
* @author traff
|
||||
*/
|
||||
public class IpnbCodeSourcePanel extends IpnbPanel<JComponent, IpnbCodeCell> implements IpnbEditorPanel {
|
||||
private Editor myEditor;
|
||||
@NotNull private final Project myProject;
|
||||
@NotNull private final IpnbCodePanel myParent;
|
||||
@NotNull private final String mySource;
|
||||
|
||||
public IpnbCodeSourcePanel(@NotNull final Project project, @NotNull final IpnbCodePanel parent, @NotNull final IpnbCodeCell cell) {
|
||||
super(cell);
|
||||
myProject = project;
|
||||
myParent = parent;
|
||||
mySource = cell.getSourceAsString();
|
||||
final JComponent panel = createViewPanel();
|
||||
add(panel);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public IpnbCodePanel getIpnbCodePanel() {
|
||||
return myParent;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Editor getEditor() {
|
||||
return myEditor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createViewPanel() {
|
||||
final JPanel panel = new JPanel(new VerticalFlowLayout(FlowLayout.LEFT, true, true));
|
||||
panel.setBackground(UIUtil.isUnderDarcula() ? IpnbEditorUtil.getBackground() : Gray._247);
|
||||
|
||||
if (mySource.startsWith("%")) {
|
||||
myEditor = IpnbEditorUtil.createPlainCodeEditor(myProject, mySource);
|
||||
}
|
||||
else {
|
||||
myEditor = IpnbEditorUtil.createPythonCodeEditor(myProject, this);
|
||||
}
|
||||
|
||||
final JComponent component = myEditor.getComponent();
|
||||
final JComponent contentComponent = myEditor.getContentComponent();
|
||||
contentComponent.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
final int keyCode = e.getKeyCode();
|
||||
final int height = myEditor.getLineHeight() * Math.max(myEditor.getDocument().getLineCount(), 1);
|
||||
component.setPreferredSize(new Dimension(IpnbEditorUtil.PANEL_WIDTH, height));
|
||||
final Container parent = myParent.getParent();
|
||||
if (parent instanceof IpnbFilePanel) {
|
||||
IpnbFilePanel ipnbFilePanel = (IpnbFilePanel)parent;
|
||||
ipnbFilePanel.revalidate();
|
||||
ipnbFilePanel.repaint();
|
||||
if (keyCode == KeyEvent.VK_ESCAPE) {
|
||||
getIpnbCodePanel().setEditing(false);
|
||||
UIUtil.requestFocus(getIpnbCodePanel().getFileEditor().getIpnbFilePanel());
|
||||
}
|
||||
else if (keyCode == KeyEvent.VK_ENTER && InputEvent.CTRL_DOWN_MASK == e.getModifiersEx()) {
|
||||
final IpnbRunCellAction action = (IpnbRunCellAction)ActionManager.getInstance().getAction("IpnbRunCellAction");
|
||||
action.runCell(ipnbFilePanel);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
contentComponent.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (InputEvent.CTRL_DOWN_MASK == e.getModifiersEx()) return;
|
||||
final Container ipnbFilePanel = myParent.getParent();
|
||||
if (ipnbFilePanel instanceof IpnbFilePanel) {
|
||||
((IpnbFilePanel)ipnbFilePanel).setSelectedCell(myParent);
|
||||
myParent.switchToEditing();
|
||||
}
|
||||
UIUtil.requestFocus(contentComponent);
|
||||
}
|
||||
});
|
||||
|
||||
panel.add(component);
|
||||
|
||||
component.setPreferredSize(new Dimension(IpnbEditorUtil.PANEL_WIDTH, component.getPreferredSize().height));
|
||||
setBorder(BorderFactory.createLineBorder(JBColor.lightGray, 1, true));
|
||||
return panel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package org.jetbrains.plugins.ipnb.editor.panels.code;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.ui.JBColor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.editor.IpnbEditorUtil;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.output.IpnbErrorOutputCell;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.AttributeSet;
|
||||
import javax.swing.text.SimpleAttributeSet;
|
||||
import javax.swing.text.StyleConstants;
|
||||
import javax.swing.text.StyleContext;
|
||||
import java.awt.*;
|
||||
|
||||
public class IpnbErrorPanel extends IpnbCodeOutputPanel<IpnbErrorOutputCell> {
|
||||
public IpnbErrorPanel(@NotNull final IpnbErrorOutputCell cell) {
|
||||
super(cell);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createViewPanel() {
|
||||
final String[] text = myCell.getText();
|
||||
if (text == null) return new JLabel();
|
||||
ColorPane ansiColoredPane = new ColorPane();
|
||||
ansiColoredPane.appendANSI(StringUtil.join(text));
|
||||
ansiColoredPane.setBackground(IpnbEditorUtil.getBackground());
|
||||
ansiColoredPane.setEditable(false);
|
||||
return ansiColoredPane;
|
||||
}
|
||||
|
||||
|
||||
public static class ColorPane extends JTextPane {
|
||||
static final Color D_Red = Color.decode("#8B0000");
|
||||
static final Color D_Magenta = JBColor.MAGENTA;
|
||||
static final Color D_Green = Color.decode("#006400");
|
||||
static final Color D_Yellow = Color.decode("#A52A2A");
|
||||
static final Color D_Cyan = Color.decode("#5AB4EB");
|
||||
|
||||
static final Color cReset = JBColor.BLACK;
|
||||
|
||||
static Color currentColor = cReset;
|
||||
String remaining = "";
|
||||
|
||||
public void append(Color color, String s) {
|
||||
StyleContext styleContext = StyleContext.getDefaultStyleContext();
|
||||
AttributeSet attributeSet = styleContext.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, color);
|
||||
int len = getDocument().getLength();
|
||||
setCaretPosition(len);
|
||||
setCharacterAttributes(attributeSet, false);
|
||||
replaceSelection(s);
|
||||
}
|
||||
|
||||
public void appendANSI(String string) {
|
||||
int position = 0;
|
||||
int index;
|
||||
int mIndex;
|
||||
String substring;
|
||||
boolean continueSearch;
|
||||
String addString = remaining + string;
|
||||
remaining = "";
|
||||
|
||||
if (addString.length() > 0) {
|
||||
index = addString.indexOf("\u001B");
|
||||
if (index == -1) {
|
||||
append(currentColor, addString);
|
||||
return;
|
||||
}
|
||||
|
||||
if (index > 0) {
|
||||
substring = addString.substring(0, index);
|
||||
append(currentColor, substring);
|
||||
position = index;
|
||||
}
|
||||
continueSearch = true;
|
||||
while (continueSearch) {
|
||||
mIndex = addString.indexOf("m", position);
|
||||
if (mIndex < 0) {
|
||||
remaining = addString.substring(position, addString.length());
|
||||
continueSearch = false;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
substring = addString.substring(position, mIndex + 1);
|
||||
currentColor = getANSIColor(substring);
|
||||
}
|
||||
position = mIndex + 1;
|
||||
|
||||
index = addString.indexOf("\u001B", position);
|
||||
|
||||
if (index == -1) {
|
||||
substring = addString.substring(position, addString.length());
|
||||
append(currentColor, substring);
|
||||
continueSearch = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
substring = addString.substring(position, index);
|
||||
position = index;
|
||||
append(currentColor, substring);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Color getANSIColor(String ANSIColor) {
|
||||
if (ANSIColor.equals("\u001B[30m") || ANSIColor.equals("\u001B[0;30m") || ANSIColor.equals("\u001B[1;30m")) {
|
||||
return JBColor.BLACK;
|
||||
}
|
||||
else if (ANSIColor.equals("\u001B[31m") || ANSIColor.equals("\u001B[0;31m") || ANSIColor.equals("\u001B[1;31m")) {
|
||||
return D_Red;
|
||||
}
|
||||
else if (ANSIColor.equals("\u001B[32m") || ANSIColor.equals("\u001B[0;32m") || ANSIColor.equals("\u001B[1;32m")) {
|
||||
return D_Green;
|
||||
}
|
||||
else if (ANSIColor.equals("\u001B[33m") || ANSIColor.equals("\u001B[0;33m") || ANSIColor.equals("\u001B[1;33m")) {
|
||||
return D_Yellow;
|
||||
}
|
||||
else if (ANSIColor.equals("\u001B[34m") || ANSIColor.equals("\u001B[0;34m") || ANSIColor.equals("\u001B[1;34m")) {
|
||||
return JBColor.BLUE;
|
||||
}
|
||||
else if (ANSIColor.equals("\u001B[35m") || ANSIColor.equals("\u001B[0;35m") || ANSIColor.equals("\u001B[1;35m")) {
|
||||
return D_Magenta;
|
||||
}
|
||||
else if (ANSIColor.equals("\u001B[36m") || ANSIColor.equals("\u001B[0;36m") || ANSIColor.equals("\u001B[1;36m")) {
|
||||
return D_Cyan;
|
||||
}
|
||||
else if (ANSIColor.equals("\u001B[37m") || ANSIColor.equals("\u001B[0;37m") || ANSIColor.equals("\u001B[1;37m")) {
|
||||
return JBColor.WHITE;
|
||||
}
|
||||
else if (ANSIColor.equals("\u001B[0m")) {
|
||||
return cReset;
|
||||
}
|
||||
else {
|
||||
return JBColor.WHITE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.jetbrains.plugins.ipnb.editor.panels.code;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.embed.swing.JFXPanel;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.web.WebView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.output.IpnbHtmlOutputCell;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class IpnbHtmlPanel extends IpnbCodeOutputPanel<IpnbHtmlOutputCell> {
|
||||
public IpnbHtmlPanel(@NotNull final IpnbHtmlOutputCell cell) {
|
||||
super(cell);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createViewPanel() {
|
||||
Platform.setImplicitExit(false);
|
||||
final JFXPanel javafxPanel = new JFXPanel();
|
||||
final StringBuilder text = new StringBuilder("<html>");
|
||||
for (String html : myCell.getHtmls()) {
|
||||
html = html.replace("\"", "'");
|
||||
text.append(html);
|
||||
}
|
||||
text.append("</html>");
|
||||
Platform.runLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
BorderPane borderPane = new BorderPane();
|
||||
WebView webComponent = new WebView();
|
||||
|
||||
webComponent.getEngine().loadContent(text.toString());
|
||||
|
||||
borderPane.setCenter(webComponent);
|
||||
Scene scene = new Scene(borderPane, 450, 450);
|
||||
javafxPanel.setScene(scene);
|
||||
}
|
||||
});
|
||||
|
||||
return javafxPanel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.jetbrains.plugins.ipnb.editor.panels.code;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.editor.IpnbEditorUtil;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.output.IpnbImageOutputCell;
|
||||
import sun.misc.BASE64Decoder;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class IpnbImagePanel extends IpnbCodeOutputPanel<IpnbImageOutputCell> {
|
||||
private static final Logger LOG = Logger.getInstance(IpnbImagePanel.class);
|
||||
|
||||
public IpnbImagePanel(@NotNull final IpnbImageOutputCell cell) {
|
||||
super(cell);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createViewPanel() {
|
||||
final String png = myCell.getBase64String();
|
||||
|
||||
final JBLabel label = new JBLabel();
|
||||
try {
|
||||
byte[] btDataFile = new BASE64Decoder().decodeBuffer(png);
|
||||
BufferedImage image = ImageIO.read(new ByteArrayInputStream(btDataFile));
|
||||
label.setIcon(new ImageIcon(image));
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error("Couldn't parse image. " + e.getMessage());
|
||||
}
|
||||
|
||||
label.setBackground(IpnbEditorUtil.getBackground());
|
||||
label.setOpaque(true);
|
||||
|
||||
return label;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.jetbrains.plugins.ipnb.editor.panels.code;
|
||||
|
||||
import com.intellij.openapi.ui.VerticalFlowLayout;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.IpnbUtils;
|
||||
import org.jetbrains.plugins.ipnb.editor.IpnbEditorUtil;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.output.IpnbLatexOutputCell;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class IpnbLatexPanel extends IpnbCodeOutputPanel<IpnbLatexOutputCell> {
|
||||
public IpnbLatexPanel(@NotNull final IpnbLatexOutputCell cell) {
|
||||
super(cell);
|
||||
setLayout(new VerticalFlowLayout(FlowLayout.LEFT));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createViewPanel() {
|
||||
final JPanel panel = new JPanel();
|
||||
IpnbUtils.addLatexToPanel(StringUtil.join(myCell.getLatex()), panel);
|
||||
setBackground(IpnbEditorUtil.getBackground());
|
||||
panel.setBackground(IpnbEditorUtil.getBackground());
|
||||
panel.setOpaque(true);
|
||||
setOpaque(true);
|
||||
return panel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.jetbrains.plugins.ipnb.editor.panels.code;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.output.IpnbStreamOutputCell;
|
||||
|
||||
public class IpnbStreamPanel extends IpnbCodeOutputPanel<IpnbStreamOutputCell> {
|
||||
public IpnbStreamPanel(@NotNull final IpnbStreamOutputCell cell) {
|
||||
super(cell);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.jetbrains.plugins.ipnb.format;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.IpnbCell;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class IpnbFile {
|
||||
private final IpnbParser.IpnbFileRaw myRawFile;
|
||||
private final List<IpnbCell> myCells;
|
||||
private final String myPath;
|
||||
|
||||
IpnbFile(IpnbParser.IpnbFileRaw rawFile, List<IpnbCell> cells, String path) {
|
||||
myRawFile = rawFile;
|
||||
myCells = cells;
|
||||
myPath = path;
|
||||
}
|
||||
|
||||
public List<IpnbCell> getCells() {
|
||||
return myCells;
|
||||
}
|
||||
|
||||
public void addCell(@NotNull final IpnbCell cell, int index) {
|
||||
myCells.add(index, cell);
|
||||
}
|
||||
|
||||
public void removeCell(int index) {
|
||||
myCells.remove(index);
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return myPath;
|
||||
}
|
||||
|
||||
public IpnbParser.IpnbFileRaw getRawFile() {
|
||||
return myRawFile;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
package org.jetbrains.plugins.ipnb.format;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.vfs.CharsetToolkit;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.IpnbEditablePanel;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.IpnbFilePanel;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.*;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.output.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class IpnbParser {
|
||||
private static final Logger LOG = Logger.getInstance(IpnbParser.class);
|
||||
private static final Gson gson = initGson();
|
||||
|
||||
@NotNull
|
||||
private static Gson initGson() {
|
||||
final GsonBuilder builder = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping();
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static IpnbFile parseIpnbFile(@NotNull String fileText, String path) throws IOException {
|
||||
IpnbFileRaw rawFile = gson.fromJson(fileText, IpnbFileRaw.class);
|
||||
if (rawFile == null) return new IpnbFile(new IpnbFileRaw(), Lists.<IpnbCell>newArrayList(), path);
|
||||
List<IpnbCell> cells = new ArrayList<IpnbCell>();
|
||||
final IpnbWorksheet[] worksheets = rawFile.worksheets;
|
||||
for (IpnbWorksheet worksheet : worksheets) {
|
||||
final IpnbCellRaw[] rawCells = worksheet.cells;
|
||||
for (IpnbCellRaw rawCell : rawCells) {
|
||||
cells.add(rawCell.createCell());
|
||||
}
|
||||
}
|
||||
return new IpnbFile(rawFile, cells, path);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static IpnbFile parseIpnbFile(@NotNull VirtualFile virtualFile) throws IOException {
|
||||
final String fileText = new String(virtualFile.contentsToByteArray(), CharsetToolkit.UTF8);
|
||||
return parseIpnbFile(fileText, virtualFile.getPath());
|
||||
}
|
||||
|
||||
public static void saveIpnbFile(@NotNull final IpnbFilePanel ipnbPanel) {
|
||||
final IpnbFile ipnbFile = ipnbPanel.getIpnbFile();
|
||||
if (ipnbFile == null) return;
|
||||
for (IpnbEditablePanel panel : ipnbPanel.getIpnbPanels()) {
|
||||
if (panel.isModified()) {
|
||||
panel.updateCellSource();
|
||||
}
|
||||
}
|
||||
|
||||
final IpnbFileRaw fileRaw = ipnbFile.getRawFile();
|
||||
final IpnbWorksheet worksheet = new IpnbWorksheet();
|
||||
final ArrayList<IpnbCellRaw> cellRaws = new ArrayList<IpnbCellRaw>();
|
||||
for (IpnbCell cell: ipnbFile.getCells()) {
|
||||
cellRaws.add(IpnbCellRaw.fromCell(cell));
|
||||
}
|
||||
worksheet.cells = cellRaws.toArray(new IpnbCellRaw[cellRaws.size()]);
|
||||
fileRaw.worksheets = new IpnbWorksheet[]{worksheet};
|
||||
final String json = gson.toJson(fileRaw);
|
||||
final String path = ipnbFile.getPath();
|
||||
final File file = new File(path);
|
||||
FileWriter writer = null;
|
||||
try {
|
||||
writer = new FileWriter(file);
|
||||
writer.write(json);
|
||||
} catch (IOException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
if (writer != null)
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class IpnbFileRaw {
|
||||
Map<String, String> metadata;
|
||||
int nbformat;
|
||||
int nbformat_minor;
|
||||
IpnbWorksheet[] worksheets;
|
||||
}
|
||||
|
||||
private static class IpnbWorksheet {
|
||||
IpnbCellRaw[] cells;
|
||||
}
|
||||
private static class IpnbCellRaw {
|
||||
String cell_type;
|
||||
Integer level;
|
||||
String[] source;
|
||||
String[] input;
|
||||
String language;
|
||||
CellOutputRaw[] outputs;
|
||||
Integer prompt_number;
|
||||
|
||||
public static IpnbCellRaw fromCell(@NotNull final IpnbCell cell) {
|
||||
final IpnbCellRaw raw = new IpnbCellRaw();
|
||||
if (cell instanceof IpnbMarkdownCell) {
|
||||
raw.cell_type = "markdown";
|
||||
raw.source = ((IpnbMarkdownCell)cell).getSource();
|
||||
}
|
||||
else if (cell instanceof IpnbCodeCell) {
|
||||
raw.cell_type = "code";
|
||||
final ArrayList<CellOutputRaw> outputRaws = new ArrayList<CellOutputRaw>();
|
||||
for (IpnbOutputCell outputCell : ((IpnbCodeCell)cell).getCellOutputs()) {
|
||||
outputRaws.add(CellOutputRaw.fromOutput(outputCell));
|
||||
}
|
||||
raw.outputs = outputRaws.toArray(new CellOutputRaw[outputRaws.size()]);
|
||||
raw.language = ((IpnbCodeCell)cell).getLanguage();
|
||||
raw.input = ((IpnbCodeCell)cell).getSource();
|
||||
raw.prompt_number = ((IpnbCodeCell)cell).getPromptNumber();
|
||||
}
|
||||
else if (cell instanceof IpnbRawCell) {
|
||||
raw.cell_type = "raw";
|
||||
}
|
||||
else if (cell instanceof IpnbHeadingCell) {
|
||||
raw.cell_type = "heading";
|
||||
raw.source = ((IpnbHeadingCell)cell).getSource();
|
||||
raw.level = ((IpnbHeadingCell)cell).getLevel();
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
public IpnbCell createCell() {
|
||||
final IpnbCell cell;
|
||||
if (cell_type.equals("markdown")) {
|
||||
cell = new IpnbMarkdownCell(source);
|
||||
}
|
||||
else if (cell_type.equals("code")) {
|
||||
final List<IpnbOutputCell> outputCells = new ArrayList<IpnbOutputCell>();
|
||||
for (CellOutputRaw outputRaw : outputs) {
|
||||
outputCells.add(outputRaw.createOutput());
|
||||
}
|
||||
cell = new IpnbCodeCell(language, input, prompt_number, outputCells);
|
||||
}
|
||||
else if (cell_type.equals("raw")) {
|
||||
cell = new IpnbRawCell();
|
||||
}
|
||||
else if (cell_type.equals("heading")) {
|
||||
cell = new IpnbHeadingCell(source, level);
|
||||
}
|
||||
else {
|
||||
cell = null;
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CellOutputRaw {
|
||||
String ename;
|
||||
String evalue;
|
||||
String output_type;
|
||||
String png;
|
||||
String stream;
|
||||
String jpeg;
|
||||
String[] html;
|
||||
String[] latex;
|
||||
String[] svg;
|
||||
Integer prompt_number;
|
||||
String[] text;
|
||||
String[] traceback;
|
||||
|
||||
|
||||
public static CellOutputRaw fromOutput(@NotNull final IpnbOutputCell outputCell) {
|
||||
final CellOutputRaw raw = new CellOutputRaw();
|
||||
|
||||
if (outputCell instanceof IpnbPngOutputCell) {
|
||||
raw.png = ((IpnbPngOutputCell)outputCell).getBase64String();
|
||||
raw.text = outputCell.getText();
|
||||
//raw.output_type = "display_data";
|
||||
}
|
||||
else if (outputCell instanceof IpnbSvgOutputCell) {
|
||||
raw.svg = ((IpnbSvgOutputCell)outputCell).getSvg();
|
||||
raw.text = outputCell.getText();
|
||||
}
|
||||
else if (outputCell instanceof IpnbJpegOutputCell) {
|
||||
raw.jpeg = ((IpnbJpegOutputCell)outputCell).getBase64String();
|
||||
raw.text = outputCell.getText();
|
||||
}
|
||||
else if (outputCell instanceof IpnbLatexOutputCell) {
|
||||
raw.latex = ((IpnbLatexOutputCell)outputCell).getLatex();
|
||||
raw.prompt_number = outputCell.getPromptNumber();
|
||||
raw.text = outputCell.getText();
|
||||
}
|
||||
else if (outputCell instanceof IpnbStreamOutputCell) {
|
||||
raw.stream = ((IpnbStreamOutputCell)outputCell).getStream();
|
||||
raw.output_type = "stream";
|
||||
raw.text = outputCell.getText();
|
||||
}
|
||||
else if (outputCell instanceof IpnbHtmlOutputCell) {
|
||||
raw.html = ((IpnbHtmlOutputCell)outputCell).getHtmls();
|
||||
raw.text = outputCell.getText();
|
||||
}
|
||||
else if (outputCell instanceof IpnbErrorOutputCell) {
|
||||
raw.output_type = "pyerr";
|
||||
raw.evalue = ((IpnbErrorOutputCell)outputCell).getEvalue();
|
||||
raw.ename = ((IpnbErrorOutputCell)outputCell).getEname();
|
||||
raw.traceback = outputCell.getText();
|
||||
}
|
||||
else if (outputCell instanceof IpnbOutOutputCell) {
|
||||
raw.output_type = "pyout";
|
||||
raw.text = outputCell.getText();
|
||||
raw.prompt_number = outputCell.getPromptNumber();
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
public IpnbOutputCell createOutput() {
|
||||
final IpnbOutputCell outputCell;
|
||||
if (png != null) {
|
||||
outputCell = new IpnbPngOutputCell(png, text, prompt_number);
|
||||
}
|
||||
else if (jpeg != null) {
|
||||
outputCell = new IpnbJpegOutputCell(jpeg, text, prompt_number);
|
||||
}
|
||||
else if (svg != null) {
|
||||
outputCell = new IpnbSvgOutputCell(svg, text, prompt_number);
|
||||
}
|
||||
else if (latex != null) {
|
||||
outputCell = new IpnbLatexOutputCell(latex, prompt_number, text);
|
||||
}
|
||||
else if (stream != null) {
|
||||
outputCell = new IpnbStreamOutputCell(stream, text, prompt_number);
|
||||
}
|
||||
else if (html != null) {
|
||||
outputCell = new IpnbHtmlOutputCell(html, text, prompt_number);
|
||||
}
|
||||
else if ("pyerr".equals(output_type)) {
|
||||
outputCell = new IpnbErrorOutputCell(evalue, ename, traceback, prompt_number);
|
||||
}
|
||||
else if ("pyout".equals(output_type)) {
|
||||
outputCell = new IpnbOutOutputCell(text, prompt_number);
|
||||
}
|
||||
else {
|
||||
outputCell = new IpnbOutputCell(text, prompt_number);
|
||||
}
|
||||
return outputCell;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package org.jetbrains.plugins.ipnb.format.cells;
|
||||
|
||||
public interface IpnbCell {
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.jetbrains.plugins.ipnb.format.cells;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.output.IpnbOutputCell;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class IpnbCodeCell extends IpnbEditableCell {
|
||||
@NotNull private final String myLanguage;
|
||||
@Nullable private Integer myPromptNumber;
|
||||
@NotNull private final List<IpnbOutputCell> myCellOutputs;
|
||||
|
||||
public IpnbCodeCell(@NotNull final String language,
|
||||
@NotNull final String[] input,
|
||||
@Nullable final Integer number,
|
||||
@NotNull final List<IpnbOutputCell> cellOutputs) {
|
||||
super(input);
|
||||
myLanguage = language;
|
||||
myPromptNumber = number;
|
||||
myCellOutputs = cellOutputs;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getLanguage() {
|
||||
return myLanguage;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getPromptNumber() {
|
||||
return myPromptNumber;
|
||||
}
|
||||
|
||||
public void setPromptNumber(@Nullable Integer promptNumber) {
|
||||
myPromptNumber = promptNumber;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<IpnbOutputCell> getCellOutputs() {
|
||||
return myCellOutputs;
|
||||
}
|
||||
|
||||
public void removeCellOutputs() {
|
||||
myCellOutputs.clear();
|
||||
}
|
||||
|
||||
public void addCellOutput(@NotNull final IpnbOutputCell cellOutput) {
|
||||
myCellOutputs.add(cellOutput);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"CloneDoesntCallSuperClone", "CloneDoesntDeclareCloneNotSupportedException"})
|
||||
@Override
|
||||
public Object clone() {
|
||||
return new IpnbCodeCell(myLanguage, Arrays.copyOf(getSource(), getSource().length), myPromptNumber, new ArrayList<IpnbOutputCell>(myCellOutputs));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.jetbrains.plugins.ipnb.format.cells;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public abstract class IpnbEditableCell implements IpnbCell {
|
||||
@NotNull private String[] mySource;
|
||||
|
||||
IpnbEditableCell(@NotNull final String[] source) {
|
||||
mySource = source;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String[] getSource() {
|
||||
return mySource;
|
||||
}
|
||||
|
||||
public void setSource(@NotNull final String[] source) {
|
||||
mySource = source;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getSourceAsString() {
|
||||
return StringUtil.join(mySource);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.jetbrains.plugins.ipnb.format.cells;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class IpnbHeadingCell extends IpnbEditableCell {
|
||||
private int myLevel;
|
||||
|
||||
public IpnbHeadingCell(@NotNull final String[] source, int level) {
|
||||
super(source);
|
||||
myLevel = level;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return myLevel;
|
||||
}
|
||||
|
||||
public void setLevel(int level) {
|
||||
myLevel = level;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"CloneDoesntCallSuperClone", "CloneDoesntDeclareCloneNotSupportedException"})
|
||||
@Override
|
||||
public Object clone() {
|
||||
return new IpnbHeadingCell(getSource(), myLevel);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.jetbrains.plugins.ipnb.format.cells;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class IpnbMarkdownCell extends IpnbEditableCell {
|
||||
public IpnbMarkdownCell(@NotNull final String[] source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"CloneDoesntCallSuperClone", "CloneDoesntDeclareCloneNotSupportedException"})
|
||||
@Override
|
||||
public Object clone() {
|
||||
return new IpnbMarkdownCell(getSource());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package org.jetbrains.plugins.ipnb.format.cells;
|
||||
|
||||
public class IpnbRawCell implements IpnbCell {
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.jetbrains.plugins.ipnb.format.cells.output;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class IpnbErrorOutputCell extends IpnbOutputCell {
|
||||
|
||||
@NotNull private final String myEvalue;
|
||||
@NotNull private final String myEname;
|
||||
|
||||
public IpnbErrorOutputCell(@NotNull final String evalue, @NotNull final String ename, @NotNull final String[] traceback,
|
||||
@Nullable final Integer prompt) {
|
||||
super(traceback, prompt);
|
||||
myEvalue = evalue;
|
||||
myEname = ename;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getEvalue() {
|
||||
return myEvalue;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getEname() {
|
||||
return myEname;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package org.jetbrains.plugins.ipnb.format.cells.output;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class IpnbHtmlOutputCell extends IpnbOutputCell {
|
||||
@NotNull private final String[] myHtml;
|
||||
|
||||
public IpnbHtmlOutputCell(@NotNull final String[] html, String[] text, @Nullable final Integer prompt) {
|
||||
super(text, prompt);
|
||||
myHtml = html;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String[] getHtmls() {
|
||||
return myHtml;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package org.jetbrains.plugins.ipnb.format.cells.output;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class IpnbImageOutputCell extends IpnbOutputCell {
|
||||
@NotNull private final String myBase64String;
|
||||
|
||||
public IpnbImageOutputCell(@NotNull final String base64String, @Nullable final String[] text, @Nullable final Integer prompt) {
|
||||
super(text, prompt);
|
||||
myBase64String = base64String;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getBase64String() {
|
||||
return myBase64String;
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package org.jetbrains.plugins.ipnb.format.cells.output;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class IpnbJpegOutputCell extends IpnbImageOutputCell {
|
||||
|
||||
public IpnbJpegOutputCell(@NotNull final String jpeg, @Nullable final String[] text, @Nullable final Integer prompt) {
|
||||
super(jpeg, text, prompt);
|
||||
}
|
||||
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package org.jetbrains.plugins.ipnb.format.cells.output;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class IpnbLatexOutputCell extends IpnbOutputCell {
|
||||
@NotNull private final String[] myLatex;
|
||||
|
||||
public IpnbLatexOutputCell(@NotNull final String[] latex, Integer promptNumber, @NotNull final String[] text) {
|
||||
super(text, promptNumber);
|
||||
myLatex = latex;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String[] getLatex() {
|
||||
return myLatex;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.jetbrains.plugins.ipnb.format.cells.output;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class IpnbOutOutputCell extends IpnbOutputCell {
|
||||
public IpnbOutOutputCell(@Nullable final String[] text, Integer promptNumber) {
|
||||
super(text, promptNumber);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.jetbrains.plugins.ipnb.format.cells.output;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.IpnbCell;
|
||||
|
||||
public class IpnbOutputCell implements IpnbCell {
|
||||
@Nullable protected final Integer myPromptNumber;
|
||||
@Nullable private final String[] myText;
|
||||
|
||||
public IpnbOutputCell(@Nullable final String[] text, @Nullable final Integer promptNumber) {
|
||||
myText = text;
|
||||
myPromptNumber = promptNumber;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String[] getText() {
|
||||
return myText;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getSourceAsString() {
|
||||
return myText == null ? null : StringUtil.join(myText, "\n");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getPromptNumber() {
|
||||
return myPromptNumber;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.jetbrains.plugins.ipnb.format.cells.output;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class IpnbPngOutputCell extends IpnbImageOutputCell {
|
||||
|
||||
public IpnbPngOutputCell(@NotNull final String png, @Nullable final String[] text, @Nullable final Integer prompt) {
|
||||
super(png, text, prompt);
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package org.jetbrains.plugins.ipnb.format.cells.output;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class IpnbStreamOutputCell extends IpnbOutputCell {
|
||||
@NotNull private final String myStream;
|
||||
|
||||
public IpnbStreamOutputCell(@NotNull final String stream, String[] text, @Nullable final Integer prompt) {
|
||||
super(text, prompt);
|
||||
myStream = stream;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getStream() {
|
||||
return myStream;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.jetbrains.plugins.ipnb.format.cells.output;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class IpnbSvgOutputCell extends IpnbOutputCell {
|
||||
@NotNull private final String[] mySvg;
|
||||
|
||||
public IpnbSvgOutputCell(@NotNull final String[] svg, @Nullable final String[] text, @Nullable final Integer prompt) {
|
||||
super(text, prompt);
|
||||
mySvg = svg;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String[] getSvg() {
|
||||
return mySvg;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,434 @@
|
||||
package org.jetbrains.plugins.ipnb.protocol;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import org.java_websocket.client.WebSocketClient;
|
||||
import org.java_websocket.drafts.Draft;
|
||||
import org.java_websocket.drafts.Draft_17;
|
||||
import org.java_websocket.handshake.ClientHandshakeBuilder;
|
||||
import org.java_websocket.handshake.ServerHandshake;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.output.*;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
public class IpnbConnection {
|
||||
private static final String API_URL = "/api";
|
||||
private static final String KERNELS_URL = API_URL + "/kernels";
|
||||
private static final String HTTP_POST = "POST";
|
||||
public static final String HTTP_DELETE = "DELETE";
|
||||
|
||||
@NotNull private final URI myURI;
|
||||
@NotNull private final String myKernelId;
|
||||
@NotNull private final String mySessionId;
|
||||
@NotNull private final IpnbConnectionListener myListener;
|
||||
@NotNull private final WebSocketClient myShellClient;
|
||||
@NotNull private final WebSocketClient myIOPubClient;
|
||||
@NotNull private final Thread myShellThread;
|
||||
@NotNull private final Thread myIOPubThread;
|
||||
|
||||
private volatile boolean myIsShellOpen = false;
|
||||
private volatile boolean myIsIOPubOpen = false;
|
||||
private volatile boolean myIsOpened = false;
|
||||
|
||||
public IpnbConnection(@NotNull URI uri, @NotNull IpnbConnectionListener listener) throws IOException, URISyntaxException {
|
||||
myURI = uri;
|
||||
myListener = listener;
|
||||
mySessionId = UUID.randomUUID().toString();
|
||||
myKernelId = startKernel();
|
||||
|
||||
final Draft draft = new Draft17WithOrigin();
|
||||
// TODO: Serialize cookies for the authentication message
|
||||
final String authMessage = "identity:foo";
|
||||
|
||||
myShellClient = new WebSocketClient(getShellURI(), draft) {
|
||||
@Override
|
||||
public void onOpen(@NotNull ServerHandshake handshakeData) {
|
||||
send(authMessage);
|
||||
myIsShellOpen = true;
|
||||
notifyOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(@NotNull String message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(int code, @NotNull String reason, boolean remote) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NotNull Exception e) {
|
||||
}
|
||||
};
|
||||
myShellThread = new Thread(myShellClient);
|
||||
myShellThread.start();
|
||||
|
||||
myIOPubClient = new WebSocketClient(getIOPubURI(), draft) {
|
||||
private ArrayList<IpnbOutputCell> myOutput = new ArrayList<IpnbOutputCell>();
|
||||
private Integer myExecCount = null;
|
||||
@Override
|
||||
public void onOpen(ServerHandshake handshakeData) {
|
||||
send(authMessage);
|
||||
myIsIOPubOpen = true;
|
||||
notifyOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(String message) {
|
||||
final Gson gson = new Gson();
|
||||
final Message msg = gson.fromJson(message, Message.class);
|
||||
final Header header = msg.getHeader();
|
||||
final Header parentHeader = gson.fromJson(msg.getParentHeader(), Header.class);
|
||||
final String messageType = header.getMessageType();
|
||||
if ("pyout".equals(messageType) || "display_data".equals(messageType)) {
|
||||
final PyOutContent content = gson.fromJson(msg.getContent(), PyOutContent.class);
|
||||
addCellOutput(content, myOutput);
|
||||
}
|
||||
else if ("pyerr".equals(messageType)) {
|
||||
final PyErrContent content = gson.fromJson(msg.getContent(), PyErrContent.class);
|
||||
addCellOutput(content, myOutput);
|
||||
}
|
||||
else if ("stream".equals(messageType)) {
|
||||
final PyStreamContent content = gson.fromJson(msg.getContent(), PyStreamContent.class);
|
||||
addCellOutput(content, myOutput);
|
||||
}
|
||||
else if ("pyin".equals(messageType)) {
|
||||
final JsonElement executionCount = msg.getContent().get("execution_count");
|
||||
if (executionCount != null) {
|
||||
myExecCount = executionCount.getAsInt();
|
||||
}
|
||||
}
|
||||
else if ("status".equals(messageType)) {
|
||||
final PyStatusContent content = gson.fromJson(msg.getContent(), PyStatusContent.class);
|
||||
if (content.getExecutionState().equals("idle")) {
|
||||
//noinspection unchecked
|
||||
myListener.onOutput(IpnbConnection.this, parentHeader.getMessageId(), (List<IpnbOutputCell>)myOutput.clone(), myExecCount);
|
||||
myOutput.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(int code, String reason, boolean remote) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
|
||||
}
|
||||
};
|
||||
myIOPubThread = new Thread(myIOPubClient);
|
||||
myIOPubThread.start();
|
||||
}
|
||||
|
||||
private void notifyOpen() {
|
||||
if (!myIsOpened && myIsShellOpen && myIsIOPubOpen) {
|
||||
myIsOpened = true;
|
||||
myListener.onOpen(this);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String execute(@NotNull String code) {
|
||||
final String messageId = UUID.randomUUID().toString();
|
||||
myShellClient.send(new Gson().toJson(createExecuteRequest(code, messageId)));
|
||||
return messageId;
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
myIOPubClient.close();
|
||||
myShellClient.close();
|
||||
}
|
||||
|
||||
public void close() throws IOException, InterruptedException {
|
||||
myIOPubThread.join();
|
||||
myShellThread.join();
|
||||
shutdownKernel();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getKernelId() {
|
||||
return myKernelId;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String startKernel() throws IOException {
|
||||
final String s = httpRequest(myURI + KERNELS_URL, HTTP_POST);
|
||||
final Gson gson = new Gson();
|
||||
final Kernel kernel = gson.fromJson(s, Kernel.class);
|
||||
return kernel.getId();
|
||||
}
|
||||
|
||||
private void shutdownKernel() throws IOException {
|
||||
httpRequest(myURI + KERNELS_URL + "/" + myKernelId, HTTP_DELETE);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public URI getShellURI() throws URISyntaxException {
|
||||
return new URI(getWebSocketURIBase() + "/shell");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public URI getIOPubURI() throws URISyntaxException {
|
||||
return new URI(getWebSocketURIBase() + "/iopub");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getWebSocketURIBase() {
|
||||
return "ws://" + myURI.getAuthority() + KERNELS_URL + "/" + myKernelId;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String httpRequest(@NotNull String url, @NotNull String method) throws IOException {
|
||||
final URLConnection urlConnection = new URL(url).openConnection();
|
||||
if (urlConnection instanceof HttpURLConnection) {
|
||||
final HttpURLConnection connection = (HttpURLConnection)urlConnection;
|
||||
connection.setRequestMethod(method);
|
||||
final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"));
|
||||
try {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
char[] buffer = new char[4096];
|
||||
int n;
|
||||
while ((n = reader.read(buffer)) != -1) {
|
||||
builder.append(buffer, 0, n);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
finally {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedOperationException("Only HTTP URLs are supported");
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Message createExecuteRequest(String code, String messageId) {
|
||||
final JsonObject content = new JsonObject();
|
||||
content.addProperty("code", code);
|
||||
content.addProperty("silent", false);
|
||||
content.add("user_variables", new JsonArray());
|
||||
content.add("output_type", new JsonPrimitive(""));
|
||||
content.add("user_expressions", new JsonObject());
|
||||
content.addProperty("allow_stdin", false);
|
||||
|
||||
return createMessage("execute_request", content, messageId);
|
||||
}
|
||||
|
||||
private Message createMessage(String messageType, JsonObject content, String messageId) {
|
||||
final Header header = Header.create(messageId, "username", mySessionId, messageType);
|
||||
final JsonObject parentHeader = new JsonObject();
|
||||
|
||||
final JsonObject metadata = new JsonObject();
|
||||
|
||||
return Message.create(header, parentHeader, metadata, content);
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private static class Kernel {
|
||||
@NotNull private String id;
|
||||
|
||||
@NotNull
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private static class Header {
|
||||
private String msg_id;
|
||||
private String username;
|
||||
private String session;
|
||||
private String msg_type;
|
||||
|
||||
@NotNull
|
||||
public static Header create(String messageId, String username, String sessionId, String messageType) {
|
||||
final Header header = new Header();
|
||||
header.msg_id = messageId;
|
||||
header.username = username;
|
||||
header.session = sessionId;
|
||||
header.msg_type = messageType;
|
||||
return header;
|
||||
}
|
||||
|
||||
public String getMessageId() {
|
||||
return msg_id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getSessionId() {
|
||||
return session;
|
||||
}
|
||||
|
||||
public String getMessageType() {
|
||||
return msg_type;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private static class Message {
|
||||
private Header header;
|
||||
private JsonObject parent_header;
|
||||
private JsonObject metadata;
|
||||
private JsonObject content;
|
||||
|
||||
public static Message create(Header header, JsonObject parentHeader, JsonObject metadata, JsonObject content) {
|
||||
final Message message = new Message();
|
||||
message.header = header;
|
||||
message.parent_header = parentHeader;
|
||||
message.metadata = metadata;
|
||||
message.content = content;
|
||||
return message;
|
||||
}
|
||||
|
||||
public Header getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
public JsonObject getParentHeader() {
|
||||
return parent_header;
|
||||
}
|
||||
|
||||
public JsonObject getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
public JsonObject getContent() {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
private void addCellOutput(@NotNull final PyContent content, ArrayList<IpnbOutputCell> output) {
|
||||
if (content instanceof PyErrContent) {
|
||||
output.add(new IpnbErrorOutputCell(((PyErrContent)content).getEvalue(),
|
||||
((PyErrContent)content).getEname(), ((PyErrContent)content).getTraceback(), null));
|
||||
}
|
||||
else if (content instanceof PyStreamContent) {
|
||||
final String data = ((PyStreamContent)content).getData();
|
||||
output.add(new IpnbStreamOutputCell(data, new String[]{data}, null));
|
||||
}
|
||||
else if (content instanceof PyOutContent) {
|
||||
final Map<String, String> data = ((PyOutContent)content).getData();
|
||||
if (data.containsKey("text/latex")) {
|
||||
final String text = data.get("text/latex");
|
||||
final String plainText = data.get("text/plain");
|
||||
output.add(new IpnbLatexOutputCell(new String[]{text}, null, new String[]{plainText}));
|
||||
}
|
||||
else if (data.containsKey("text/html")) {
|
||||
final String html = data.get("text/html");
|
||||
output.add(new IpnbHtmlOutputCell(StringUtil.splitByLinesKeepSeparators(html), StringUtil.splitByLinesKeepSeparators(html), null));
|
||||
}
|
||||
else if (data.containsKey("image/png")) {
|
||||
final String png = data.get("image/png");
|
||||
final String plainText = data.get("text/plain");
|
||||
output.add(new IpnbPngOutputCell(png, StringUtil.splitByLinesKeepSeparators(plainText), null));
|
||||
}
|
||||
else if (data.containsKey("image/jpeg")) {
|
||||
final String jpeg = data.get("image/jpeg");
|
||||
final String plainText = data.get("text/plain");
|
||||
output.add(new IpnbJpegOutputCell(jpeg, StringUtil.splitByLinesKeepSeparators(plainText), null));
|
||||
}
|
||||
else if (data.containsKey("image/svg")) {
|
||||
final String svg = data.get("image/svg");
|
||||
final String plainText = data.get("text/plain");
|
||||
output.add(new IpnbSvgOutputCell(StringUtil.splitByLinesKeepSeparators(svg), StringUtil.splitByLinesKeepSeparators(plainText), null));
|
||||
}
|
||||
else {
|
||||
for (Map.Entry<String, String> entry : data.entrySet()) {
|
||||
output.add(new IpnbOutOutputCell(new String[]{entry.getValue()}, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private interface PyContent {}
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private static class PyOutContent implements PyContent {
|
||||
private int execution_count;
|
||||
private HashMap<String, String> data;
|
||||
private JsonObject metadata;
|
||||
|
||||
public int getExecutionCount() {
|
||||
return execution_count;
|
||||
}
|
||||
|
||||
public Map<String, String> getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public JsonObject getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private static class PyErrContent implements PyContent {
|
||||
private String ename;
|
||||
private String evalue;
|
||||
private String[] traceback;
|
||||
|
||||
public String getEname() {
|
||||
return ename;
|
||||
}
|
||||
|
||||
public String getEvalue() {
|
||||
return evalue;
|
||||
}
|
||||
|
||||
public String[] getTraceback() {
|
||||
return traceback;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private static class PyStreamContent implements PyContent {
|
||||
private String data;
|
||||
private String name;
|
||||
|
||||
public String getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private static class PyStatusContent {
|
||||
private String execution_state;
|
||||
|
||||
public String getExecutionState() {
|
||||
return execution_state;
|
||||
}
|
||||
}
|
||||
|
||||
private class Draft17WithOrigin extends Draft_17 {
|
||||
@Override
|
||||
public Draft copyInstance() {
|
||||
return new Draft17WithOrigin();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ClientHandshakeBuilder postProcessHandshakeRequestAsClient(@NotNull ClientHandshakeBuilder request) {
|
||||
super.postProcessHandshakeRequestAsClient(request);
|
||||
request.put("Origin", myURI.toString());
|
||||
return request;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.jetbrains.plugins.ipnb.protocol;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.output.IpnbOutputCell;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* TODO: Expose execution counter via API
|
||||
*
|
||||
* @author vlan
|
||||
*/
|
||||
public interface IpnbConnectionListener {
|
||||
void onOpen(@NotNull IpnbConnection connection);
|
||||
void onOutput(@NotNull IpnbConnection connection,
|
||||
@NotNull String parentMessageId,
|
||||
@NotNull List<IpnbOutputCell> outputs,
|
||||
@Nullable Integer execCount);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.jetbrains.plugins.ipnb.protocol;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.output.IpnbOutputCell;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
public class IpnbConnectionListenerBase implements IpnbConnectionListener {
|
||||
@Override
|
||||
public void onOpen(@NotNull IpnbConnection connection) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOutput(@NotNull IpnbConnection connection,
|
||||
@NotNull String parentMessageId,
|
||||
@NotNull List<IpnbOutputCell> outputs,
|
||||
@Nullable Integer execCount) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.jetbrains.plugins.ipnb.psi;
|
||||
|
||||
import com.intellij.lang.Language;
|
||||
import com.jetbrains.python.psi.PyFileElementType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class IpnbPyFileElementType extends PyFileElementType {
|
||||
public IpnbPyFileElementType(Language language) {
|
||||
super(language);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getExternalId() {
|
||||
return "IpnbFile.Python";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.jetbrains.plugins.ipnb.psi;
|
||||
|
||||
import com.jetbrains.python.PythonFileType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class IpnbPyFileType extends PythonFileType {
|
||||
public static PythonFileType INSTANCE = new IpnbPyFileType();
|
||||
|
||||
protected IpnbPyFileType() {
|
||||
super(new IpnbPyLanguageDialect());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Ipnb Python";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Ipnb Python";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDefaultExtension() {
|
||||
return "ipnb_py";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package org.jetbrains.plugins.ipnb.psi;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.FileViewProvider;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.SingleRootFileViewProvider;
|
||||
import com.intellij.psi.impl.PsiManagerEx;
|
||||
import com.intellij.psi.impl.file.impl.FileManager;
|
||||
import com.intellij.psi.impl.source.tree.FileElement;
|
||||
import com.intellij.testFramework.LightVirtualFile;
|
||||
import com.jetbrains.python.psi.impl.PyFileImpl;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.IpnbFilePanel;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.code.IpnbCodeSourcePanel;
|
||||
|
||||
public class IpnbPyFragment extends PyFileImpl {
|
||||
private PsiElement myContext;
|
||||
private boolean myPhysical;
|
||||
private final IpnbFilePanel myFilePanel;
|
||||
private final IpnbCodeSourcePanel myCodeSourcePanel;
|
||||
private FileViewProvider myViewProvider;
|
||||
|
||||
public IpnbPyFragment(Project project, CharSequence text, boolean isPhysical, IpnbCodeSourcePanel codeSourcePanel) {
|
||||
super(((PsiManagerEx)PsiManager.getInstance(project)).getFileManager().createFileViewProvider(
|
||||
new LightVirtualFile("code.py", IpnbPyLanguageDialect.getInstance(), text), isPhysical)
|
||||
);
|
||||
myPhysical = isPhysical;
|
||||
myCodeSourcePanel = codeSourcePanel;
|
||||
myFilePanel = codeSourcePanel.getIpnbCodePanel().getFileEditor().getIpnbFilePanel();
|
||||
((SingleRootFileViewProvider)getViewProvider()).forceCachedPsi(this);
|
||||
}
|
||||
|
||||
public IpnbCodeSourcePanel getCodeSourcePanel() {
|
||||
return myCodeSourcePanel;
|
||||
}
|
||||
|
||||
protected IpnbPyFragment clone() {
|
||||
final IpnbPyFragment clone = (IpnbPyFragment)cloneImpl((FileElement)calcTreeElement().clone());
|
||||
clone.myPhysical = false;
|
||||
clone.myOriginalFile = this;
|
||||
FileManager fileManager = ((PsiManagerEx)getManager()).getFileManager();
|
||||
SingleRootFileViewProvider cloneViewProvider = (SingleRootFileViewProvider)fileManager.createFileViewProvider(new LightVirtualFile(getName(), getLanguage(), getText()), false);
|
||||
cloneViewProvider.forceCachedPsi(clone);
|
||||
clone.myViewProvider = cloneViewProvider;
|
||||
return clone;
|
||||
}
|
||||
|
||||
public PsiElement getContext() {
|
||||
return myContext;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FileViewProvider getViewProvider() {
|
||||
if(myViewProvider != null) return myViewProvider;
|
||||
return super.getViewProvider();
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
if (!super.isValid()) return false;
|
||||
if (myContext != null && !myContext.isValid()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isPhysical() {
|
||||
return myPhysical;
|
||||
}
|
||||
|
||||
public void setContext(PsiElement context) {
|
||||
myContext = context;
|
||||
}
|
||||
|
||||
public IpnbFilePanel getFilePanel() {
|
||||
return myFilePanel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.jetbrains.plugins.ipnb.psi;
|
||||
|
||||
import com.intellij.lang.InjectableLanguage;
|
||||
import com.intellij.lang.Language;
|
||||
import com.jetbrains.python.PythonLanguage;
|
||||
|
||||
public class IpnbPyLanguageDialect extends Language implements InjectableLanguage {
|
||||
public static IpnbPyLanguageDialect getInstance() {
|
||||
return (IpnbPyLanguageDialect)IpnbPyFileType.INSTANCE.getLanguage();
|
||||
}
|
||||
|
||||
protected IpnbPyLanguageDialect() {
|
||||
super(PythonLanguage.getInstance(), "IpnbPython");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.jetbrains.plugins.ipnb.psi;
|
||||
|
||||
import com.intellij.lang.PsiBuilder;
|
||||
import com.jetbrains.python.parsing.ParsingContext;
|
||||
import com.jetbrains.python.parsing.PyParser;
|
||||
import com.jetbrains.python.parsing.StatementParsing;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
|
||||
public class IpnbPyParser extends PyParser {
|
||||
@Override
|
||||
protected ParsingContext createParsingContext(PsiBuilder builder, LanguageLevel languageLevel, StatementParsing.FUTURE futureFlag) {
|
||||
return new IpnbPyParsingContext(builder, languageLevel, futureFlag);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.jetbrains.plugins.ipnb.psi;
|
||||
|
||||
import com.intellij.lang.PsiParser;
|
||||
import com.intellij.lexer.Lexer;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.tree.IFileElementType;
|
||||
import com.jetbrains.python.PythonParserDefinition;
|
||||
import com.jetbrains.python.lexer.PythonIndentingLexer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class IpnbPyParserDefinition extends PythonParserDefinition {
|
||||
public static final IFileElementType IPNB_PYTHON_FILE = new IpnbPyFileElementType(IpnbPyLanguageDialect.getInstance());
|
||||
|
||||
@NotNull
|
||||
public Lexer createLexer(Project project) {
|
||||
return new PythonIndentingLexer();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiParser createParser(Project project) {
|
||||
return new IpnbPyParser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IFileElementType getFileNodeType() {
|
||||
return IPNB_PYTHON_FILE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package org.jetbrains.plugins.ipnb.psi;
|
||||
|
||||
import com.intellij.lang.PsiBuilder;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
import com.jetbrains.python.parsing.ExpressionParsing;
|
||||
import com.jetbrains.python.parsing.ParsingContext;
|
||||
import com.jetbrains.python.parsing.StatementParsing;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class IpnbPyParsingContext extends ParsingContext {
|
||||
private final StatementParsing myStatementParser;
|
||||
private final ExpressionParsing myExpressionParser;
|
||||
|
||||
public IpnbPyParsingContext(final PsiBuilder builder,
|
||||
LanguageLevel languageLevel,
|
||||
StatementParsing.FUTURE futureFlag) {
|
||||
super(builder, languageLevel, futureFlag);
|
||||
myStatementParser = new IpnbPyStatementParsing(this, futureFlag);
|
||||
myExpressionParser = new IpnbPyExpressionParsing(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionParsing getExpressionParser() {
|
||||
return myExpressionParser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatementParsing getStatementParser() {
|
||||
return myStatementParser;
|
||||
}
|
||||
|
||||
private static class IpnbPyExpressionParsing extends ExpressionParsing {
|
||||
public IpnbPyExpressionParsing(ParsingContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IElementType getReferenceType() {
|
||||
return IpnbPyTokenTypes.IPNB_REFERENCE;
|
||||
}
|
||||
|
||||
public boolean parsePrimaryExpression(boolean isTargetExpression) {
|
||||
final IElementType firstToken = myBuilder.getTokenType();
|
||||
if (firstToken == PyTokenTypes.IDENTIFIER) {
|
||||
if (isTargetExpression) {
|
||||
buildTokenElement(IpnbPyTokenTypes.IPNB_TARGET, myBuilder);
|
||||
}
|
||||
else {
|
||||
buildTokenElement(getReferenceType(), myBuilder);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return super.parsePrimaryExpression(isTargetExpression);
|
||||
}
|
||||
}
|
||||
|
||||
private static class IpnbPyStatementParsing extends StatementParsing {
|
||||
|
||||
protected IpnbPyStatementParsing(ParsingContext context, @Nullable FUTURE futureFlag) {
|
||||
super(context, futureFlag);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IElementType getReferenceType() {
|
||||
return IpnbPyTokenTypes.IPNB_REFERENCE;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.jetbrains.plugins.ipnb.psi;
|
||||
|
||||
import com.intellij.lang.annotation.HighlightSeverity;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.ResolveResult;
|
||||
import com.jetbrains.python.psi.PyQualifiedExpression;
|
||||
import com.jetbrains.python.psi.impl.references.PyReferenceImpl;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveUtil;
|
||||
import com.jetbrains.python.psi.resolve.RatedResolveResult;
|
||||
import com.jetbrains.python.psi.resolve.ResolveProcessor;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.IpnbEditablePanel;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.IpnbFilePanel;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.code.IpnbCodePanel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class IpnbPyReference extends PyReferenceImpl {
|
||||
public IpnbPyReference(PyQualifiedExpression element, @NotNull PyResolveContext context) {
|
||||
super(element, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HighlightSeverity getUnresolvedHighlightSeverity(TypeEvalContext context) {
|
||||
return HighlightSeverity.WARNING;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ResolveResult[] multiResolve(boolean incompleteCode) {
|
||||
ResolveResult[] results = super.multiResolve(incompleteCode);
|
||||
if (results.length == 0) {
|
||||
PsiFile file = myElement.getContainingFile();
|
||||
if (file instanceof IpnbPyFragment) {
|
||||
final IpnbFilePanel panel = ((IpnbPyFragment)file).getFilePanel();
|
||||
final List<IpnbEditablePanel> panels = panel.getIpnbPanels();
|
||||
|
||||
final String referencedName = myElement.getReferencedName();
|
||||
if (referencedName == null) return ResolveResult.EMPTY_ARRAY;
|
||||
|
||||
for (IpnbEditablePanel editablePanel : panels) {
|
||||
if (!(editablePanel instanceof IpnbCodePanel)) continue;
|
||||
final Editor editor = ((IpnbCodePanel)editablePanel).getEditor();
|
||||
final IpnbPyFragment psiFile = (IpnbPyFragment)PsiDocumentManager.getInstance(myElement.getProject()).getPsiFile(editor.getDocument());
|
||||
if (psiFile == null) continue;
|
||||
ResolveProcessor processor = new ResolveProcessor(referencedName);
|
||||
|
||||
PyResolveUtil.scopeCrawlUp(processor, psiFile, referencedName, psiFile);
|
||||
final List<RatedResolveResult> resultList = getResultsFromProcessor(referencedName, processor, psiFile, psiFile);
|
||||
if (resultList.size() > 0) {
|
||||
List<RatedResolveResult> ret = RatedResolveResult.sorted(resultList);
|
||||
return ret.toArray(new RatedResolveResult[ret.size()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.jetbrains.plugins.ipnb.psi;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiPolyVariantReference;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.psi.PyFromImportStatement;
|
||||
import com.jetbrains.python.psi.PyImportElement;
|
||||
import com.jetbrains.python.psi.impl.PyReferenceExpressionImpl;
|
||||
import com.jetbrains.python.psi.impl.references.PyImportReference;
|
||||
import com.jetbrains.python.psi.impl.references.PyQualifiedReference;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class IpnbPyReferenceExpression extends PyReferenceExpressionImpl {
|
||||
|
||||
public IpnbPyReferenceExpression(ASTNode astNode) {
|
||||
super(astNode);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiPolyVariantReference getReference(PyResolveContext context) {
|
||||
if (isQualified()) {
|
||||
return new PyQualifiedReference(this, context);
|
||||
}
|
||||
final PsiElement importParent = PsiTreeUtil.getParentOfType(this, PyImportElement.class, PyFromImportStatement.class);
|
||||
if (importParent != null) {
|
||||
return PyImportReference.forElement(this, importParent, context);
|
||||
}
|
||||
return new IpnbPyReference(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.jetbrains.plugins.ipnb.psi;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.jetbrains.python.psi.impl.PyTargetExpressionImpl;
|
||||
import org.jetbrains.plugins.ipnb.editor.IpnbFileEditor;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.IpnbFilePanel;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.code.IpnbCodePanel;
|
||||
import org.jetbrains.plugins.ipnb.editor.panels.code.IpnbCodeSourcePanel;
|
||||
|
||||
public class IpnbPyTargetExpression extends PyTargetExpressionImpl {
|
||||
|
||||
public IpnbPyTargetExpression(ASTNode astNode) {
|
||||
super(astNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void navigate(boolean requestFocus) {
|
||||
final IpnbCodeSourcePanel sourcePanel = ((IpnbPyFragment)getContainingFile()).getCodeSourcePanel();
|
||||
final Editor editor = sourcePanel.getEditor();
|
||||
|
||||
final IpnbCodePanel codePanel = sourcePanel.getIpnbCodePanel();
|
||||
final IpnbFileEditor fileEditor = codePanel.getFileEditor();
|
||||
final IpnbFilePanel filePanel = fileEditor.getIpnbFilePanel();
|
||||
codePanel.setEditing(true);
|
||||
filePanel.setSelectedCell(codePanel);
|
||||
super.navigate(false);
|
||||
UIUtil.requestFocus(editor.getContentComponent());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.jetbrains.plugins.ipnb.psi;
|
||||
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import com.jetbrains.python.PythonDialectsTokenSetContributorBase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class IpnbPyTokenSetContributor extends PythonDialectsTokenSetContributorBase {
|
||||
public static final TokenSet IPNB_REFERENCE_EXPRESSIONS = TokenSet.create(IpnbPyTokenTypes.IPNB_REFERENCE);
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public TokenSet getExpressionTokens() {
|
||||
return IPNB_REFERENCE_EXPRESSIONS;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public TokenSet getReferenceExpressionTokens() {
|
||||
return IPNB_REFERENCE_EXPRESSIONS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.jetbrains.plugins.ipnb.psi;
|
||||
|
||||
import com.jetbrains.python.psi.PyElementType;
|
||||
|
||||
public class IpnbPyTokenTypes {
|
||||
public static final PyElementType IPNB_REFERENCE = new PyElementType("IPNB_REFERENCE", IpnbPyReferenceExpression.class);
|
||||
public static final PyElementType IPNB_TARGET = new PyElementType("IPNB_TARGET", IpnbPyTargetExpression.class);
|
||||
|
||||
private IpnbPyTokenTypes() {
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"metadata": {
|
||||
"name": "",
|
||||
"signature": "sha256:0b4b631419772e40e0f4893f5a0f0fe089a39e46c8862af0256164628394302d"
|
||||
},
|
||||
"nbformat": 3,
|
||||
"nbformat_minor": 0,
|
||||
"worksheets": [
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"collapsed": true,
|
||||
"input": [
|
||||
"e = x + 2*y"
|
||||
],
|
||||
"language": "python",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"prompt_number": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"metadata": {
|
||||
"name": "",
|
||||
"signature": "sha256:0b4b631419772e40e0f4893f5a0f0fe089a39e46c8862af0256164628394302d"
|
||||
},
|
||||
"nbformat": 3,
|
||||
"nbformat_minor": 0,
|
||||
"worksheets": [
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<img src=\"images/ipython_logo.png\">"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"metadata": {
|
||||
"name": "",
|
||||
"signature": "sha256:0b4b631419772e40e0f4893f5a0f0fe089a39e46c8862af0256164628394302d"
|
||||
},
|
||||
"nbformat": 3,
|
||||
"nbformat_minor": 0,
|
||||
"worksheets": [
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"collapsed": false,
|
||||
"input": [
|
||||
"srepr(e)"
|
||||
],
|
||||
"language": "python",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"metadata": {},
|
||||
"output_type": "pyout",
|
||||
"prompt_number": 5,
|
||||
"text": [
|
||||
"\"Add(Symbol('x'), Mul(Integer(2), Symbol('y')))\""
|
||||
]
|
||||
}
|
||||
],
|
||||
"prompt_number": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import junit.framework.TestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.format.IpnbFile;
|
||||
import org.jetbrains.plugins.ipnb.format.IpnbParser;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.IpnbCodeCell;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.IpnbCell;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.IpnbMarkdownCell;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.output.IpnbOutputCell;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class JsonParserTest extends TestCase {
|
||||
public void testFile() throws IOException {
|
||||
final String fileName = "testData/SymPy.ipynb";
|
||||
final String fileText = getFileText(fileName);
|
||||
final IpnbFile ipnbFile = IpnbParser.parseIpnbFile(fileText, fileName);
|
||||
assertNotNull(ipnbFile);
|
||||
assertEquals(31, ipnbFile.getCells().size());
|
||||
}
|
||||
|
||||
public void testMarkdownCells() throws IOException {
|
||||
final String fileName = "testData/SymPy.ipynb";
|
||||
final String fileText = getFileText(fileName);
|
||||
final IpnbFile ipnbFile = IpnbParser.parseIpnbFile(fileText,fileName);
|
||||
assertNotNull(ipnbFile);
|
||||
final List<IpnbCell> cells = ipnbFile.getCells();
|
||||
Iterables.removeIf(cells, new Predicate<IpnbCell>() {
|
||||
@Override
|
||||
public boolean apply(IpnbCell cell) {
|
||||
return !(cell instanceof IpnbMarkdownCell);
|
||||
}
|
||||
});
|
||||
assertEquals(7, cells.size());
|
||||
}
|
||||
|
||||
public void testMarkdownCell() throws IOException {
|
||||
final String fileName = "testData/markdown.ipynb";
|
||||
final String fileText = getFileText(fileName);
|
||||
final IpnbFile ipnbFile = IpnbParser.parseIpnbFile(fileText, fileName);
|
||||
assertNotNull(ipnbFile);
|
||||
final List<IpnbCell> cells = ipnbFile.getCells();
|
||||
assertEquals(1, cells.size());
|
||||
final IpnbCell cell = cells.get(0);
|
||||
assertTrue(cell instanceof IpnbMarkdownCell);
|
||||
final String[] source = ((IpnbMarkdownCell)cell).getSource();
|
||||
final String joined = StringUtil.join(source);
|
||||
assertEquals("<img src=\"images/ipython_logo.png\">", joined);
|
||||
}
|
||||
|
||||
public void testCodeCell() throws IOException {
|
||||
final String fileName = "testData/code.ipynb";
|
||||
final String fileText = getFileText(fileName);
|
||||
final IpnbFile ipnbFile = IpnbParser.parseIpnbFile(fileText, fileName);
|
||||
assertNotNull(ipnbFile);
|
||||
final List<IpnbCell> cells = ipnbFile.getCells();
|
||||
assertEquals(1, cells.size());
|
||||
final IpnbCell cell = cells.get(0);
|
||||
assertTrue(cell instanceof IpnbCodeCell);
|
||||
final List<IpnbOutputCell> outputs = ((IpnbCodeCell)cell).getCellOutputs();
|
||||
assertEquals(0, outputs.size());
|
||||
final String[] source = ((IpnbCodeCell)cell).getSource();
|
||||
final String joined = StringUtil.join(source);
|
||||
assertEquals("e = x + 2*y", joined);
|
||||
final String language = ((IpnbCodeCell)cell).getLanguage();
|
||||
assertEquals("python", language);
|
||||
final Integer number = ((IpnbCodeCell)cell).getPromptNumber();
|
||||
assertEquals(new Integer(4), number);
|
||||
}
|
||||
|
||||
public void testOutputs() throws IOException {
|
||||
final String fileName = "testData/outputs.ipynb";
|
||||
final String fileText = getFileText(fileName);
|
||||
final IpnbFile ipnbFile = IpnbParser.parseIpnbFile(fileText, fileName);
|
||||
assertNotNull(ipnbFile);
|
||||
final List<IpnbCell> cells = ipnbFile.getCells();
|
||||
assertEquals(1, cells.size());
|
||||
final IpnbCell cell = cells.get(0);
|
||||
assertTrue(cell instanceof IpnbCodeCell);
|
||||
final List<IpnbOutputCell> outputs = ((IpnbCodeCell)cell).getCellOutputs();
|
||||
assertEquals(1, outputs.size());
|
||||
final IpnbOutputCell output = outputs.get(0);
|
||||
final String[] text = output.getText();
|
||||
final String joined = StringUtil.join(text);
|
||||
assertEquals("\"Add(Symbol('x'), Mul(Integer(2), Symbol('y')))\"", joined);
|
||||
}
|
||||
|
||||
private String getFileText(@NotNull final String fileName) throws IOException {
|
||||
final BufferedReader br = new BufferedReader(new FileReader(fileName));
|
||||
try {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
String line = br.readLine();
|
||||
|
||||
while (line != null) {
|
||||
sb.append(line);
|
||||
sb.append("\n");
|
||||
line = br.readLine();
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
finally {
|
||||
br.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import junit.framework.TestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.output.IpnbOutOutputCell;
|
||||
import org.jetbrains.plugins.ipnb.format.cells.output.IpnbOutputCell;
|
||||
import org.jetbrains.plugins.ipnb.protocol.IpnbConnection;
|
||||
import org.jetbrains.plugins.ipnb.protocol.IpnbConnectionListenerBase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* * Message Spec
|
||||
* http://ipython.org/ipython-doc/dev/development/messaging.html
|
||||
*
|
||||
* * Notebook REST API
|
||||
* https://github.com/ipython/ipython/wiki/IPEP-16%3A-Notebook-multi-directory-dashboard-and-URL-mapping
|
||||
*
|
||||
* @author vlan
|
||||
*/
|
||||
public class WebSocketConnectionTest extends TestCase {
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
//WebSocketImpl.DEBUG = true;
|
||||
}
|
||||
|
||||
public void testStartAndShutdownKernel() throws URISyntaxException, IOException, InterruptedException {
|
||||
final IpnbConnection connection = new IpnbConnection(getTestServerURI(), new IpnbConnectionListenerBase() {
|
||||
@Override
|
||||
public void onOpen(@NotNull IpnbConnection connection) {
|
||||
assertTrue(connection.getKernelId().length() > 0);
|
||||
connection.shutdown();
|
||||
}
|
||||
});
|
||||
connection.close();
|
||||
}
|
||||
|
||||
public void testBasicWebSocket() throws IOException, URISyntaxException, InterruptedException {
|
||||
final Ref<Boolean> evaluated = Ref.create(false);
|
||||
final IpnbConnection connection = new IpnbConnection(getTestServerURI(), new IpnbConnectionListenerBase() {
|
||||
private String myMessageId;
|
||||
|
||||
@Override
|
||||
public void onOpen(@NotNull IpnbConnection connection) {
|
||||
myMessageId = connection.execute("2 + 2");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOutput(@NotNull IpnbConnection connection,
|
||||
@NotNull String parentMessageId,
|
||||
@NotNull List<IpnbOutputCell> outputs,
|
||||
Integer execCount) {
|
||||
if (myMessageId.equals(parentMessageId)) {
|
||||
assertEquals(outputs.size(), 1);
|
||||
assertEquals(outputs.get(0).getClass(), IpnbOutOutputCell.class);
|
||||
final String[] text = outputs.get(0).getText();
|
||||
assertNotNull(text);
|
||||
assertEquals("4", text[0]);
|
||||
evaluated.set(true);
|
||||
connection.shutdown();
|
||||
}
|
||||
}
|
||||
});
|
||||
connection.close();
|
||||
assertTrue(evaluated.get());
|
||||
}
|
||||
|
||||
public void testCompositeInput() throws IOException, URISyntaxException, InterruptedException {
|
||||
final Ref<Boolean> evaluated = Ref.create(false);
|
||||
final IpnbConnection connection = new IpnbConnection(getTestServerURI(), new IpnbConnectionListenerBase() {
|
||||
private String myMessageId;
|
||||
|
||||
@Override
|
||||
public void onOpen(@NotNull IpnbConnection connection) {
|
||||
myMessageId = connection.execute("def simple_crit_func(feat_sub):\n" +
|
||||
"\n" +
|
||||
" \"\"\" Returns sum of numerical values of an input list. \"\"\" \n" +
|
||||
"\n" +
|
||||
" return sum(feat_sub)\n" +
|
||||
"\n" +
|
||||
"simple_crit_func([1,2,4])");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOutput(@NotNull IpnbConnection connection,
|
||||
@NotNull String parentMessageId,
|
||||
@NotNull List<IpnbOutputCell> outputs,
|
||||
Integer execCount) {
|
||||
if (myMessageId.equals(parentMessageId)) {
|
||||
assertEquals(outputs.size(), 1);
|
||||
assertEquals(outputs.get(0).getClass(), IpnbOutOutputCell.class);
|
||||
final String[] text = outputs.get(0).getText();
|
||||
assertNotNull(text);
|
||||
assertEquals("7", text[0]);
|
||||
evaluated.set(true);
|
||||
connection.shutdown();
|
||||
}
|
||||
}
|
||||
});
|
||||
connection.close();
|
||||
assertTrue(evaluated.get());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static URI getTestServerURI() {
|
||||
try {
|
||||
return new URI("http://127.0.0.1:8888");
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user