Jupyter notebook: add option to disable markdown rendering

This commit is contained in:
Ekaterina Tuzova
2017-07-12 13:44:20 +03:00
parent 276a76b1ef
commit ff9508bc04
10 changed files with 135 additions and 122 deletions
@@ -10,7 +10,7 @@
<projectConfigurable groupId="language" instance="org.jetbrains.plugins.ipnb.configuration.IpnbConfigurable"
id="IpnbConfigurable" displayName="Jupyter Notebook"
provider="org.jetbrains.plugins.ipnb.configuration.IpnbConfigurableProvider" nonDefaultProject="true"/>
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"/>
@@ -6,9 +6,9 @@ import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.ui.JBColor;
import com.intellij.util.TimeoutUtil;
import javafx.application.Platform;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.ipnb.configuration.IpnbSettings;
import org.jetbrains.plugins.ipnb.editor.IpnbEditorUtil;
import javax.swing.*;
@@ -21,10 +21,9 @@ import java.util.concurrent.Future;
public class IpnbUtils {
private static final Logger LOG = Logger.getInstance(IpnbUtils.class);
private static int hasFx = 0;
public static JComponent createLatexPane(@NotNull final String source, int width) {
final JComponent panel = createHtmlPanel(source, width);
public static JComponent createLatexPane(@NotNull final String source, Project project, int width) {
final JComponent panel = createHtmlPanel(source, project, width);
panel.addMouseListener(new MouseAdapter() {
@Override
@@ -39,21 +38,8 @@ public class IpnbUtils {
return panel;
}
public static boolean hasFx() {
if (hasFx == 0) {
try {
Platform.setImplicitExit(false);
hasFx = 1;
}
catch (NoClassDefFoundError e) {
hasFx = 2;
}
}
return hasFx == 1;
}
public static JComponent createHtmlPanel(@NotNull final String source, int width) {
if (hasFx()) {
public static JComponent createHtmlPanel(@NotNull final String source, Project project, int width) {
if (IpnbSettings.getInstance(project).hasFx()) {
return IpnbJfxUtils.createHtmlPanel(source, width);
}
return createNonJfxPanel(source);
@@ -1,6 +1,6 @@
<?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="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="6" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="657" height="302"/>
@@ -8,47 +8,65 @@
<properties/>
<border type="none"/>
<children>
<vspacer id="9a11e">
<vspacer id="b8b6c">
<constraints>
<grid row="3" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
<grid row="5" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="a6cbc" class="com.intellij.ui.components.JBTextField" binding="myUsernameField">
<grid id="2dd2d" binding="myProPanel" layout-manager="GridLayoutManager" row-count="3" 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="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
<grid row="1" column="0" row-span="4" col-span="2" vsize-policy="3" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="769b7" class="com.intellij.ui.components.JBLabel">
<border type="none"/>
<children>
<component id="a6cbc" class="com.intellij.ui.components.JBTextField" binding="myUsernameField">
<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>
<component id="769b7" 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="0" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Username:"/>
</properties>
</component>
<component id="1c949" class="javax.swing.JPasswordField" binding="myPasswordField">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
<component id="bfaca" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Password:"/>
</properties>
</component>
<component id="a8589" class="javax.swing.JLabel" binding="myInterpreterSetupLinkLabel">
<constraints>
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Configure remote interpreter"/>
</properties>
</component>
</children>
</grid>
<component id="8da92" class="javax.swing.JCheckBox" binding="myMarkdownCheckBox" default-binding="true">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Username:"/>
</properties>
</component>
<component id="1c949" class="javax.swing.JPasswordField" binding="myPasswordField">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
<component id="bfaca" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Password:"/>
</properties>
</component>
<component id="a8589" class="javax.swing.JLabel" binding="myInterpreterSetupLinkLabel">
<constraints>
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Configure remote interpreter"/>
<text value="Markdown cells rendering enabled"/>
</properties>
</component>
</children>
@@ -1,16 +1,21 @@
package org.jetbrains.plugins.ipnb.configuration;
import com.intellij.ide.DataManager;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.options.SearchableConfigurable;
import com.intellij.openapi.options.ex.Settings;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.ClickListener;
import com.intellij.ui.UI;
import com.intellij.ui.components.JBTextField;
import com.intellij.util.PlatformUtils;
import com.intellij.util.ui.UIUtil;
import com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.ipnb.editor.IpnbFileEditor;
import javax.swing.*;
import java.awt.*;
@@ -23,11 +28,14 @@ public class IpnbConfigurable implements SearchableConfigurable {
private JBTextField myUsernameField;
private JLabel myInterpreterSetupLinkLabel;
private JPasswordField myPasswordField;
private JPanel myProPanel;
private JCheckBox myMarkdownCheckBox;
private final Project myProject;
public IpnbConfigurable(@NotNull Project project) {
myProject = project;
myProPanel.setVisible(PlatformUtils.isPyCharmPro() || PlatformUtils.isIdeaUltimate());
myInterpreterSetupLinkLabel.setForeground(UI.getColor("link.foreground"));
myInterpreterSetupLinkLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
createNavigateToInterpreterSettingsListener().installOn(myInterpreterSetupLinkLabel);
@@ -77,39 +85,57 @@ public class IpnbConfigurable implements SearchableConfigurable {
public void apply() {
final IpnbSettings ipnbSettings = IpnbSettings.getInstance(myProject);
ipnbSettings.setHasFx(myMarkdownCheckBox.isSelected());
for (FileEditor editor : FileEditorManager.getInstance(myProject).getAllEditors()) {
if (editor instanceof IpnbFileEditor) {
final VirtualFile file = ((IpnbFileEditor)editor).getVirtualFile();
FileEditorManager.getInstance(myProject).closeFile(file);
FileEditorManager.getInstance(myProject).openFile(file, false);
}
}
if (myProPanel.isVisible()) {
final String oldUsername = ipnbSettings.getUsername();
final String oldPassword = ipnbSettings.getPassword(myProject.getLocationHash());
final String oldUsername = ipnbSettings.getUsername();
final String oldPassword = ipnbSettings.getPassword(myProject.getLocationHash());
final String newUsername = getUsername();
final String newPassword = String.valueOf(myPasswordField.getPassword());
final String newUsername = getUsername();
final String newPassword = String.valueOf(myPasswordField.getPassword());
if (!oldUsername.equals(newUsername) || !oldPassword.equals(newPassword)) {
IpnbConnectionManager.getInstance(myProject).shutdownKernels();
ipnbSettings.setUsername(newUsername);
ipnbSettings.setPassword(newPassword, myProject.getLocationHash());
if (!oldUsername.equals(newUsername) || !oldPassword.equals(newPassword)) {
IpnbConnectionManager.getInstance(myProject).shutdownKernels();
ipnbSettings.setUsername(newUsername);
ipnbSettings.setPassword(newPassword, myProject.getLocationHash());
}
}
}
public void reset() {
final IpnbSettings ipnbSettings = IpnbSettings.getInstance(myProject);
final boolean hasFx = ipnbSettings.hasFx();
myMarkdownCheckBox.setSelected(hasFx);
if (myProPanel.isVisible()) {
final String savedUsername = ipnbSettings.getUsername();
setInitialText(myUsernameField, savedUsername, DEFAULT_USERNAME_TEXT);
final String savedUsername = ipnbSettings.getUsername();
setInitialText(myUsernameField, savedUsername, DEFAULT_USERNAME_TEXT);
final String savedPassword = ipnbSettings.getPassword(myProject.getLocationHash());
myPasswordField.setText(savedPassword);
final String savedPassword = ipnbSettings.getPassword(myProject.getLocationHash());
myPasswordField.setText(savedPassword);
}
}
public boolean isModified() {
final IpnbSettings ipnbSettings = IpnbSettings.getInstance(myProject);
final String oldUsername = ipnbSettings.getUsername();
final String oldPassword = ipnbSettings.getPassword(myProject.getLocationHash());
final boolean hasFx = ipnbSettings.hasFx();
if (hasFx != myMarkdownCheckBox.isSelected()) return true;
final String newPassword = String.valueOf(myPasswordField.getPassword());
final String newUsername = getUsername();
if (myProPanel.isVisible()) {
final String oldUsername = ipnbSettings.getUsername();
final String oldPassword = ipnbSettings.getPassword(myProject.getLocationHash());
return !oldPassword.equals(newPassword) || !oldUsername.equals(newUsername);
final String newPassword = String.valueOf(myPasswordField.getPassword());
final String newUsername = getUsername();
return !oldPassword.equals(newPassword) || !oldUsername.equals(newUsername);
}
return false;
}
private String getUsername() {
@@ -8,6 +8,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.xmlb.XmlSerializerUtil;
import com.intellij.util.xmlb.annotations.Transient;
import com.sun.javafx.application.PlatformImpl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -15,6 +16,7 @@ import org.jetbrains.annotations.Nullable;
public class IpnbSettings implements PersistentStateComponent<IpnbSettings> {
private static final String IPNB_PASSWORD_KEY = "IPNB_SSH_SETTINGS_PASSWORD_KEY";
private String myUsername;
private boolean hasFx = true;
public static IpnbSettings getInstance(@NotNull Project project) {
return ServiceManager.getService(project, IpnbSettings.class);
@@ -27,7 +29,23 @@ public class IpnbSettings implements PersistentStateComponent<IpnbSettings> {
public void setUsername(@Nullable String username) {
myUsername = username;
}
public boolean hasFx() {
if (hasFx) {
try {
PlatformImpl.setImplicitExit(false);
}
catch (NoClassDefFoundError e) {
hasFx = false;
}
}
return hasFx;
}
public void setHasFx(boolean hasFx) {
this.hasFx = hasFx;
}
@Transient
@NotNull
public String getPassword(@NotNull String projectPathHash) {
@@ -1,14 +1,13 @@
package org.jetbrains.plugins.ipnb.editor.panels;
import com.google.common.collect.Lists;
import com.intellij.ide.*;
import com.intellij.ide.CopyProvider;
import com.intellij.ide.CutProvider;
import com.intellij.ide.DeleteProvider;
import com.intellij.ide.PasteProvider;
import com.intellij.lang.Language;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.command.CommandProcessor;
@@ -32,12 +31,10 @@ import com.intellij.psi.PsiDocumentManager;
import com.intellij.ui.JBColor;
import com.intellij.ui.KeyStrokeAdapter;
import com.intellij.util.Alarm;
import com.intellij.util.PlatformUtils;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.messages.Topic;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.ipnb.IpnbUtils;
import org.jetbrains.plugins.ipnb.editor.IpnbEditorUtil;
import org.jetbrains.plugins.ipnb.editor.IpnbFileEditor;
import org.jetbrains.plugins.ipnb.editor.actions.IpnbToggleLineNumbersAction;
@@ -120,9 +117,7 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider, D
setFocusable(true);
}, 10, ModalityState.stateForComponent(this));
myParent.loaded();
IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> {
IdeFocusManager.getGlobalInstance().requestFocus(this, true);
});
IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> IdeFocusManager.getGlobalInstance().requestFocus(this, true));
myBusConnection = ApplicationManager.getApplication().getMessageBus().connect();
myBusConnection.subscribe(ProjectEx.ProjectSaved.TOPIC,
new ProjectEx.ProjectSaved() {
@@ -189,7 +184,6 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider, D
}
private void layoutFile() {
addWarningIfNeeded();
final List<IpnbCell> cells = myIpnbFile.getCells();
for (IpnbCell cell : cells) {
addCellToPanel(cell);
@@ -205,35 +199,7 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider, D
myParent.updateScrollPosition(mySelectedCellPanel);
}
});
IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> {
IdeFocusManager.getGlobalInstance().requestFocus(this, true);
});
}
private void addWarningIfNeeded() {
if (IpnbUtils.hasFx()) return;
final String text;
final String href;
if (PlatformUtils.isPyCharm()) {
href = "https://www.jetbrains.com/pycharm/download/";
text = "<html><a href=\"https://www.jetbrains.com/pycharm/download/\">Download PyCharm</a> with bundled JDK for better " +
"Markdown cell rendering</html>";
}
else {
href = "https://confluence.jetbrains.com/display/PYH/Pycharm+2016.1+Jupyter+Notebook+rendering";
text =
"<html>Follow instructions <a href=\"https://confluence.jetbrains.com/display/PYH/Pycharm+2016.1+Jupyter+Notebook+rendering\">" +
"here</a> for better Markdown cell rendering</html>";
}
final JLabel warning = new JLabel(text, SwingConstants.CENTER);
warning.setForeground(JBColor.RED);
warning.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
BrowserUtil.browse(href);
}
});
add(warning);
IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> IdeFocusManager.getGlobalInstance().requestFocus(this, true));
}
private void addCellToPanel(IpnbCell cell) {
@@ -665,6 +631,7 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider, D
}
}
@SuppressWarnings("unused")
public void selectNextOrPrev(@NotNull IpnbEditablePanel cell) {
int index = myIpnbPanels.indexOf(cell);
if (index < myIpnbPanels.size() - 1) {
@@ -684,9 +651,7 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider, D
IpnbEditablePanel ipnbPanel = getIpnbPanelByClick(e.getPoint());
if (ipnbPanel != null) {
ipnbPanel.setEditing(false);
IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> {
IdeFocusManager.getGlobalInstance().requestFocus(this, true);
});
IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> IdeFocusManager.getGlobalInstance().requestFocus(this, true));
setSelectedCell(ipnbPanel, true);
}
}
@@ -36,13 +36,13 @@ public class IpnbMarkdownPanel extends IpnbEditablePanel<JComponent, IpnbMarkdow
@Override
protected JComponent createViewPanel() {
int width = myParent.getWidth();
return IpnbUtils.createLatexPane(myCell.getSourceAsString(), width);
return IpnbUtils.createLatexPane(myCell.getSourceAsString(), myParent.getProject(), width);
}
@Override
public void updateCellView() {
int width = myParent.getWidth();
myViewPanel = IpnbUtils.createLatexPane(myCell.getSourceAsString(), width);
myViewPanel = IpnbUtils.createLatexPane(myCell.getSourceAsString(), myParent.getProject(), width);
myViewPanel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
@@ -19,6 +19,6 @@ public class IpnbHtmlPanel extends IpnbCodeOutputPanel<IpnbHtmlOutputCell> {
@Override
protected JComponent createViewPanel() {
final int width = myParent.getWidth();
return IpnbUtils.createLatexPane(StringUtil.join(myCell.getHtmls(), ""), width);
return IpnbUtils.createLatexPane(StringUtil.join(myCell.getHtmls(), ""), myParent.getProject(), width);
}
}
@@ -23,6 +23,6 @@ public class IpnbLatexPanel extends IpnbCodeOutputPanel<IpnbLatexOutputCell> {
@Override
protected JComponent createViewPanel() {
final int width = myParent.getWidth();
return IpnbUtils.createLatexPane(StringUtil.join(myCell.getLatex(), ""), width);
return IpnbUtils.createLatexPane(StringUtil.join(myCell.getLatex(), ""), myParent.getProject(), width);
}
}
@@ -91,7 +91,7 @@ public class IpnbRunConfiguration extends AbstractPythonRunConfiguration<IpnbRun
throw new RuntimeConfigurationError("Please select valid port");
}
final Sdk sdk = getSdk();
assert sdk != null;
if (sdk == null) return;
if (RemoteSdkCredentialsHolder.isRemoteSdk(sdk.getHomePath())) {
throw new RuntimeConfigurationError("Please select local python interpreter");
}