fixed PY-14121 IPython Notebook: missing Undo action

This commit is contained in:
Ekaterina Tuzova
2014-11-12 15:45:40 +03:00
parent b6d4b6450d
commit c4b76fc232
17 changed files with 366 additions and 159 deletions
@@ -20,7 +20,7 @@ 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 mySelectedIndex = 0;
private int mySelectedTop;
public IpnbEditorState(final long modificationStamp, int selectedComponentIndex, int top) {
@@ -34,9 +34,8 @@ final class IpnbEditorState implements FileEditorState{
if (!(o instanceof IpnbEditorState)) return false;
final IpnbEditorState state = (IpnbEditorState)o;
return myDocumentModificationStamp == state.myDocumentModificationStamp && mySelectedIndex == state.mySelectedIndex &&
mySelectedTop == state.mySelectedTop;
if (mySelectedIndex == -1) return true;
return myDocumentModificationStamp == state.myDocumentModificationStamp;
}
public int getSelectedTop() {
@@ -1,11 +1,11 @@
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.CustomShortcutSet;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.*;
import com.intellij.openapi.fileEditor.ex.FileEditorProviderManager;
@@ -63,27 +63,15 @@ public class IpnbFileEditor extends UserDataHolderBase implements FileEditor, Te
public IpnbFileEditor(Project project, final VirtualFile vFile) {
project.getMessageBus().connect(this).subscribe(AppTopics.FILE_DOCUMENT_SYNC, new FileDocumentManagerAdapter() {
@Override
public void beforeAllDocumentsSaving() {
if (!new File(vFile.getPath()).exists()) return;
final IpnbFilePanel filePanel = myIpnbFilePanel;
if (filePanel != null) {
IpnbParser.saveIpnbFile(filePanel);
vFile.refresh(false, false);
}
}
});
project.getMessageBus().connect(this).subscribe(FileEditorManagerListener.Before.FILE_EDITOR_MANAGER, new FileEditorManagerListener.Before.Adapter() {
@Override
public void beforeFileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
if (!new File(file.getPath()).exists()) return;
IpnbParser.saveIpnbFile(myIpnbFilePanel);
file.refresh(false, false);
file.refresh(false, false);
}
});
myFile = vFile;
myName = vFile.getName();
@@ -94,7 +82,7 @@ public class IpnbFileEditor extends UserDataHolderBase implements FileEditor, Te
myEditorPanel.setBackground(IpnbEditorUtil.getBackground());
myIpnbFilePanel = createIpnbEditorPanel(project, vFile);
Disposer.register(this, myIpnbFilePanel);
final JPanel controlPanel = createControlPanel();
myEditorPanel.add(controlPanel, BorderLayout.NORTH);
myScrollPane = ScrollPaneFactory.createScrollPane(myIpnbFilePanel);
@@ -156,7 +144,9 @@ public class IpnbFileEditor extends UserDataHolderBase implements FileEditor, Te
}
});
final IpnbPanel selectedCell = myIpnbFilePanel.getSelectedCell();
updateCellTypeCombo(selectedCell);
if (selectedCell != null) {
updateCellTypeCombo(selectedCell);
}
controlPanel.add(myCellTypeCombo);
final MatteBorder border = BorderFactory.createMatteBorder(0, 0, 1, 0, JBColor.GRAY);
controlPanel.setBorder(border);
@@ -329,7 +319,7 @@ public class IpnbFileEditor extends UserDataHolderBase implements FileEditor, Te
});
}
private void updateCellTypeCombo(IpnbPanel ipnbPanel) {
private void updateCellTypeCombo(@NotNull final IpnbPanel ipnbPanel) {
if (ipnbPanel instanceof IpnbHeadingPanel) {
final IpnbHeadingCell cell = ((IpnbHeadingPanel)ipnbPanel).getCell();
final int level = cell.getLevel();
@@ -370,7 +360,9 @@ public class IpnbFileEditor extends UserDataHolderBase implements FileEditor, Te
final int index = getIpnbFilePanel().getSelectedIndex();
final IpnbEditablePanel cell = getIpnbFilePanel().getSelectedCell();
final int top = cell != null ? cell.getTop() : 0;
return new IpnbEditorState(-1, index, top);
final Document document = FileDocumentManager.getInstance().getCachedDocument(myFile);
long modificationStamp = document != null ? document.getModificationStamp() : myFile.getModificationStamp();
return new IpnbEditorState(modificationStamp, index, top);
}
@Override
@@ -4,6 +4,8 @@ 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.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.fileEditor.FileEditor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.ipnb.editor.IpnbFileEditor;
@@ -22,6 +24,16 @@ public class IpnbAddCellAboveAction extends AnAction {
}
public void addCell(@NotNull final IpnbFilePanel ipnbFilePanel) {
ipnbFilePanel.createAndAddCell(false);
CommandProcessor.getInstance().executeCommand(ipnbFilePanel.getProject(), new Runnable() {
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
ipnbFilePanel.createAndAddCell(false);
ipnbFilePanel.saveToFile();
}
});
}
}, "Ipnb.createAndAddCell", new Object());
}
}
@@ -4,6 +4,8 @@ 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.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.fileEditor.FileEditor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.ipnb.editor.IpnbFileEditor;
@@ -22,6 +24,15 @@ public class IpnbAddCellBelowAction extends AnAction {
}
public static void addCell(@NotNull final IpnbFilePanel ipnbFilePanel) {
ipnbFilePanel.createAndAddCell(true);
CommandProcessor.getInstance().executeCommand(ipnbFilePanel.getProject(), new Runnable() {
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
ipnbFilePanel.createAndAddCell(true);
ipnbFilePanel.saveToFile();
}
});
}
}, "Ipnb.createAndAddCell", new Object());
}
}
@@ -31,6 +31,7 @@ public class IpnbCodeCellAction extends AnAction {
public void changeTypeToCode(@NotNull final IpnbFileEditor editor) {
final IpnbFilePanel filePanel = editor.getIpnbFilePanel();
final IpnbEditablePanel selectedCell = filePanel.getSelectedCell();
if (selectedCell == null) return;
final IpnbEditableCell cell = selectedCell.getCell();
final List<IpnbCell> cells = filePanel.getIpnbFile().getCells();
@@ -5,6 +5,8 @@ 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.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.fileEditor.FileEditor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.ipnb.editor.IpnbFileEditor;
@@ -26,8 +28,15 @@ public class IpnbCutCellAction extends AnAction {
}
public static void cutCell(@NotNull final IpnbFilePanel ipnbFilePanel) {
ipnbFilePanel.cutCell();
ipnbFilePanel.revalidate();
ipnbFilePanel.repaint();
CommandProcessor.getInstance().executeCommand(ipnbFilePanel.getProject(), new Runnable() {
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
ipnbFilePanel.cutCell();
ipnbFilePanel.saveToFile();
}
});
}
}, "Ipnb.cutCell", new Object());
}
}
@@ -5,6 +5,8 @@ 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.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.fileEditor.FileEditor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.ipnb.editor.IpnbFileEditor;
@@ -21,13 +23,20 @@ public class IpnbDeleteCellAction extends AnAction {
final FileEditor editor = PlatformDataKeys.FILE_EDITOR.getData(context);
if (editor instanceof IpnbFileEditor) {
final IpnbFilePanel component = ((IpnbFileEditor)editor).getIpnbFilePanel();
cutCell(component);
deleteCell(component);
}
}
public void cutCell(@NotNull final IpnbFilePanel ipnbFilePanel) {
ipnbFilePanel.deleteSelectedCell();
ipnbFilePanel.revalidate();
ipnbFilePanel.repaint();
public static void deleteCell(@NotNull final IpnbFilePanel ipnbFilePanel) {
CommandProcessor.getInstance().executeCommand(ipnbFilePanel.getProject(), new Runnable() {
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
ipnbFilePanel.deleteSelectedCell();
ipnbFilePanel.saveToFile();
}
});
}
}, "Ipnb.deleteCell", new Object());
}
}
@@ -34,6 +34,7 @@ public class IpnbHeadingCellActionBase extends AnAction {
public void changeTypeToHeading(@NotNull final IpnbFileEditor editor) {
final IpnbFilePanel filePanel = editor.getIpnbFilePanel();
final IpnbEditablePanel selectedCell = filePanel.getSelectedCell();
if (selectedCell == null) return;
final IpnbEditableCell cell = selectedCell.getCell();
final List<IpnbCell> cells = filePanel.getIpnbFile().getCells();
@@ -29,6 +29,7 @@ public class IpnbMarkdownCellAction extends AnAction {
public void changeTypeToMarkdown(@NotNull final IpnbFileEditor editor) {
final IpnbFilePanel filePanel = editor.getIpnbFilePanel();
final IpnbEditablePanel selectedCell = filePanel.getSelectedCell();
if (selectedCell == null) return;
final IpnbEditableCell cell = selectedCell.getCell();
final List<IpnbCell> cells = filePanel.getIpnbFile().getCells();
@@ -4,6 +4,8 @@ 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.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.fileEditor.FileEditor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.ipnb.editor.IpnbFileEditor;
@@ -17,7 +19,17 @@ public class IpnbMoveCellDownAction extends AnAction {
final FileEditor editor = PlatformDataKeys.FILE_EDITOR.getData(context);
if (editor instanceof IpnbFileEditor) {
final IpnbFilePanel ipnbFilePanel = ((IpnbFileEditor)editor).getIpnbFilePanel();
ipnbFilePanel.moveCell(true);
CommandProcessor.getInstance().executeCommand(ipnbFilePanel.getProject(), new Runnable() {
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
ipnbFilePanel.moveCell(true);
ipnbFilePanel.saveToFile();
}
});
}
}, "Ipnb.moveCell", new Object());
}
}
}
@@ -4,6 +4,8 @@ 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.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.fileEditor.FileEditor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.ipnb.editor.IpnbFileEditor;
@@ -16,8 +18,18 @@ public class IpnbMoveCellUpAction extends AnAction {
final DataContext context = event.getDataContext();
final FileEditor editor = PlatformDataKeys.FILE_EDITOR.getData(context);
if (editor instanceof IpnbFileEditor) {
final IpnbFilePanel filePanel = ((IpnbFileEditor)editor).getIpnbFilePanel();
filePanel.moveCell(false);
final IpnbFilePanel ipnbFilePanel = ((IpnbFileEditor)editor).getIpnbFilePanel();
CommandProcessor.getInstance().executeCommand(ipnbFilePanel.getProject(), new Runnable() {
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
ipnbFilePanel.moveCell(false);
ipnbFilePanel.saveToFile();
}
});
}
}, "Ipnb.moveCell", new Object());
}
}
@@ -5,6 +5,8 @@ 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.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.fileEditor.FileEditor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.ipnb.editor.IpnbFileEditor;
@@ -26,6 +28,16 @@ public class IpnbPasteCellAction extends AnAction {
}
public static void pasteCell(@NotNull final IpnbFilePanel ipnbFilePanel) {
ipnbFilePanel.pasteCell();
CommandProcessor.getInstance().executeCommand(ipnbFilePanel.getProject(), new Runnable() {
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
ipnbFilePanel.pasteCell();
ipnbFilePanel.saveToFile();
}
});
}
}, "Ipnb.pasteCell", new Object());
}
}
@@ -19,6 +19,7 @@ public abstract class IpnbRunCellBaseAction extends AnAction {
final int index = ipnbFilePanel.getSelectedIndex();
if (ipnbFilePanel.getIpnbPanels().size()-1 == index) {
ipnbFilePanel.createAndAddCell(true);
ipnbFilePanel.saveToFile();
}
ipnbFilePanel.selectNext(cell);
}
@@ -1,19 +1,31 @@
package org.jetbrains.plugins.ipnb.editor.panels;
import com.google.common.collect.Lists;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.event.DocumentAdapter;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.VerticalFlowLayout;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.ui.JBColor;
import com.intellij.util.Alarm;
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.actions.IpnbCutCellAction;
import org.jetbrains.plugins.ipnb.editor.actions.IpnbDeleteCellAction;
import org.jetbrains.plugins.ipnb.editor.actions.IpnbPasteCellAction;
import org.jetbrains.plugins.ipnb.editor.panels.code.IpnbCodePanel;
import org.jetbrains.plugins.ipnb.format.IpnbFile;
import org.jetbrains.plugins.ipnb.format.IpnbParser;
@@ -30,16 +42,19 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider {
public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider, Disposable {
private static final Logger LOG = Logger.getInstance(IpnbFilePanel.class);
private final DocumentAdapter myDocumentListener;
private Document myDocument;
private IpnbFile myIpnbFile;
private Project myProject;
@NotNull private IpnbFileEditor myParent;
@NotNull private final VirtualFile myVirtualFile;
@NotNull private final IpnbFileEditor.CellSelectionListener myListener;
private final List<IpnbEditablePanel> myIpnbPanels = Lists.newArrayList();
private IpnbEditablePanel mySelectedCell;
@Nullable private IpnbEditablePanel mySelectedCell;
private IpnbEditablePanel myBufferPanel;
private int myIncrement = 10;
private int myInitialSelection = 0;
@@ -50,37 +65,61 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider {
super(new VerticalFlowLayout(VerticalFlowLayout.TOP, 100, 5, true, false));
myProject = project;
myParent = parent;
myVirtualFile = vFile;
myListener = listener;
setBackground(IpnbEditorUtil.getBackground());
ApplicationManager.getApplication().invokeLater(new Runnable() {
final Alarm alarm = new Alarm();
myDocumentListener = new DocumentAdapter() {
public void documentChanged(final DocumentEvent e) {
alarm.cancelAllRequests();
alarm.addRequest(new MySynchronizeRequest(), 10, ModalityState.stateForComponent(IpnbFilePanel.this));
}
};
myDocument = myParent.getEditor().getDocument();
myDocument.addDocumentListener(myDocumentListener);
alarm.addRequest(new Runnable() {
@Override
public void run() {
try {
myIpnbFile = IpnbParser.parseIpnbFile(vFile);
if (myIpnbFile.getCells().isEmpty()) {
final IpnbCodeCell cell = new IpnbCodeCell("python", new String[]{""}, null, new ArrayList<IpnbOutputCell>());
myIpnbFile.addCell(cell, 0);
readFromFile(true);
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
updateCellSelection(e);
}
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());
}
});
setFocusable(true);
}
});
}, 10, ModalityState.stateForComponent(this));
UIUtil.requestFocus(this);
}
public void dispose() {
myDocument.removeDocumentListener(myDocumentListener);
}
private void readFromFile(boolean showError) {
try {
myIpnbPanels.clear();
mySelectedCell = null;
myIpnbFile = IpnbParser.parseIpnbFile(myDocument, myVirtualFile.getPath());
if (myIpnbFile.getCells().isEmpty()) {
final IpnbCodeCell cell = new IpnbCodeCell("python", new String[]{""}, null, new ArrayList<IpnbOutputCell>());
myIpnbFile.addCell(cell, 0);
saveToFile();
}
} catch (IOException e) {
if (showError)
Messages.showErrorDialog(getProject(), e.getMessage(), "Can't open " + myVirtualFile.getPath());
else
LOG.error(e.getMessage(), "Can't open " + myVirtualFile.getPath());
}
layoutFile();
}
public List<IpnbEditablePanel> getIpnbPanels() {
return myIpnbPanels;
}
@@ -119,8 +158,7 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider {
}
}
public void createAndAddCell(boolean below) {
removeAll();
public void createAndAddCell(final boolean below) {
final IpnbCodeCell cell = new IpnbCodeCell("python", new String[]{""}, null, new ArrayList<IpnbOutputCell>());
final IpnbCodePanel codePanel = new IpnbCodePanel(myProject, myParent, cell);
@@ -128,7 +166,6 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider {
}
private void addCell(@NotNull final IpnbEditablePanel panel, boolean below) {
removeAll();
final IpnbEditablePanel selectedCell = getSelectedCell();
int index = myIpnbPanels.indexOf(selectedCell);
if (below) {
@@ -137,16 +174,7 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider {
final IpnbEditableCell cell = panel.getCell();
myIpnbFile.addCell(cell, index);
myIpnbPanels.add(index, panel);
for (IpnbPanel comp : myIpnbPanels) {
add(comp);
}
add(createEmptyPanel());
setSelectedCell(panel);
requestFocus();
revalidate();
repaint();
}
public void cutCell() {
@@ -171,7 +199,10 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider {
if (down) {
deleteSelectedCell();
addCell(selectedCell, true);
final IpnbEditableCell cell = selectedCell.getCell();
myIpnbFile.addCell(cell, index + 1);
myIpnbPanels.add(index + 1, selectedCell);
setSelectedCell(selectedCell);
}
else {
final IpnbEditablePanel siblingPanel = myIpnbPanels.get(siblingIndex);
@@ -183,65 +214,161 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider {
public void deleteSelectedCell() {
final IpnbEditablePanel cell = getSelectedCell();
deleteCell(cell);
if (cell != null)
deleteCell(cell);
}
private void deleteCell(@NotNull final IpnbEditablePanel cell) {
selectNextOrPrev(cell);
private boolean deleteCell(@NotNull final IpnbEditablePanel cell) {
final int index = myIpnbPanels.indexOf(cell);
if (index < 0) return;
if (index < 0) return true;
myIpnbPanels.remove(index);
myIpnbFile.removeCell(index);
if (myIpnbFile.getCells().isEmpty()) {
final IpnbCodeCell newCell = new IpnbCodeCell("python", new String[]{""}, null, new ArrayList<IpnbOutputCell>());
final IpnbCodePanel codePanel = new IpnbCodePanel(myProject, myParent, newCell);
addCell(codePanel, false);
}
return false;
}
remove(cell);
if (myIpnbPanels.isEmpty()) {
createAndAddCell(true);
public void saveToFile() {
final String oldText = myDocument.getText();
final String newText = IpnbParser.newDocumentText(this);
try {
final ReplaceInfo replaceInfo = findFragmentToChange(oldText, newText);
if (replaceInfo.getStartOffset() != -1) {
myDocument.replaceString(replaceInfo.getStartOffset(), replaceInfo.getEndOffset(), replaceInfo.getReplacement());
}
}
catch (Exception e) {
myDocument.replaceString(0, oldText.length(), newText);
}
}
public static final class ReplaceInfo {
private final int myStartOffset;
private final int myEndOffset;
private final String myReplacement;
public ReplaceInfo(final int startOffset, final int endOffset, final String replacement) {
myStartOffset = startOffset;
myEndOffset = endOffset;
myReplacement = replacement;
}
public int getStartOffset() {
return myStartOffset;
}
public int getEndOffset() {
return myEndOffset;
}
public String getReplacement() {
return myReplacement;
}
}
public static ReplaceInfo findFragmentToChange(final String oldText, final String newText) {
if (oldText.equals(newText)) {
return new ReplaceInfo(-1, -1, null);
}
final int oldLength = oldText.length();
final int newLength = newText.length();
int startOffset = 0;
while (
startOffset < oldLength && startOffset < newLength &&
oldText.charAt(startOffset) == newText.charAt(startOffset)
) {
startOffset++;
}
int endOffset = oldLength;
while (true) {
if (endOffset <= startOffset) {
break;
}
final int idxInNew = newLength - (oldLength - endOffset) - 1;
if (idxInNew < startOffset) {
break;
}
final char c1 = oldText.charAt(endOffset - 1);
final char c2 = newText.charAt(idxInNew);
if (c1 != c2) {
break;
}
endOffset--;
}
return new ReplaceInfo(startOffset, endOffset, newText.substring(startOffset, newLength - (oldLength - endOffset)));
}
private class MySynchronizeRequest implements Runnable {
public void run() {
final Project project = getProject();
if (project.isDisposed()) {
return;
}
PsiDocumentManager.getInstance(project).commitDocument(myDocument);
final IpnbEditablePanel selectedCell = getSelectedCell();
final int index = myIpnbPanels.indexOf(selectedCell);
myInitialSelection = index >= 0 && index < myIpnbPanels.size() ? index : myIpnbPanels.size() - 1;
removeAll();
readFromFile(false);
}
}
public void copyCell() {
myBufferPanel = getSelectedCell();
}
public void pasteCell() {
if (myBufferPanel == null) return;
removeAll();
final IpnbEditablePanel editablePanel = (IpnbEditablePanel)myBufferPanel.clone();
addCell(editablePanel, true);
}
public void replaceComponent(@NotNull final IpnbEditablePanel from, @NotNull final IpnbCell cell) {
removeAll();
final int index = myIpnbPanels.indexOf(from);
IpnbEditablePanel panel;
if (cell instanceof IpnbCodeCell) {
panel = new IpnbCodePanel(myProject, myParent, (IpnbCodeCell)cell);
}
else if (cell instanceof IpnbMarkdownCell) {
panel = new IpnbMarkdownPanel((IpnbMarkdownCell)cell);
}
else if (cell instanceof IpnbHeadingCell) {
panel = new IpnbHeadingPanel((IpnbHeadingCell)cell);
}
else {
throw new UnsupportedOperationException(cell.getClass().toString());
}
if (index >= 0) {
myIpnbPanels.remove(index);
myIpnbPanels.add(index, panel);
}
for (IpnbPanel comp : myIpnbPanels) {
add(comp);
}
add(createEmptyPanel());
CommandProcessor.getInstance().executeCommand(getProject(), new Runnable() {
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
final int index = myIpnbPanels.indexOf(from);
IpnbEditablePanel panel;
if (cell instanceof IpnbCodeCell) {
panel = new IpnbCodePanel(myProject, myParent, (IpnbCodeCell)cell);
}
else if (cell instanceof IpnbMarkdownCell) {
panel = new IpnbMarkdownPanel((IpnbMarkdownCell)cell);
}
else if (cell instanceof IpnbHeadingCell) {
panel = new IpnbHeadingPanel((IpnbHeadingCell)cell);
}
else {
throw new UnsupportedOperationException(cell.getClass().toString());
}
if (index >= 0) {
myIpnbFile.removeCell(index);
myIpnbFile.addCell(cell, index);
myIpnbPanels.remove(index);
myIpnbPanels.add(index, panel);
}
if (from instanceof IpnbCodePanel) {
panel.switchToEditing();
}
setSelectedCell(panel);
saveToFile();
}
});
}
}, "Ipnb.changeCellType", new Object());
if (from instanceof IpnbCodePanel) {
panel.switchToEditing();
}
setSelectedCell(panel);
remove(from);
revalidate();
repaint();
}
private void addComponent(@NotNull final IpnbEditablePanel comp) {
@@ -287,20 +414,26 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider {
}
}
else if (e.getKeyCode() == KeyEvent.VK_DELETE) {
if (!mySelectedCell.isEditing())
deleteCell(mySelectedCell);
if (!mySelectedCell.isEditing()) {
IpnbDeleteCellAction.deleteCell(this);
}
}
else if (e.getKeyCode() == KeyEvent.VK_X && e.getModifiers() == InputEvent.CTRL_MASK) {
if (!mySelectedCell.isEditing())
cutCell();
}
else if (e.getKeyCode() == KeyEvent.VK_C && e.getModifiers() == InputEvent.CTRL_MASK) {
if (!mySelectedCell.isEditing())
copyCell();
}
else if (e.getKeyCode() == KeyEvent.VK_V && e.getModifiers() == InputEvent.CTRL_MASK) {
if (!mySelectedCell.isEditing())
pasteCell();
else if (e.getModifiers() == InputEvent.CTRL_MASK) {
if (e.getKeyCode() == KeyEvent.VK_X) {
if (!mySelectedCell.isEditing()) {
IpnbCutCellAction.cutCell(this);
}
}
else if (e.getKeyCode() == KeyEvent.VK_C) {
if (!mySelectedCell.isEditing()) {
copyCell();
}
}
else if (e.getKeyCode() == KeyEvent.VK_V) {
if (!mySelectedCell.isEditing()) {
IpnbPasteCellAction.pasteCell(this);
}
}
}
else {
getParent().dispatchEvent(e);
@@ -435,4 +568,8 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider {
}
return null;
}
public Project getProject() {
return myProject;
}
}
@@ -1,13 +1,10 @@
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;
@@ -30,7 +27,7 @@ public class IpnbCodePanel extends IpnbEditablePanel<JComponent, IpnbCodeCell> {
private IpnbCodeSourcePanel myCodeSourcePanel;
private final List<IpnbPanel> myOutputPanels = Lists.newArrayList();
public IpnbCodePanel(@NotNull final Project project, final IpnbFileEditor parent, @NotNull final IpnbCodeCell cell) {
public IpnbCodePanel(@NotNull final Project project, @NotNull final IpnbFileEditor parent, @NotNull final IpnbCodeCell cell) {
super(cell, new BorderLayout());
myProject = project;
myParent = parent;
@@ -40,7 +37,6 @@ public class IpnbCodePanel extends IpnbEditablePanel<JComponent, IpnbCodeCell> {
}
public IpnbFileEditor getFileEditor() {
assert myParent != null;
return myParent;
}
@@ -68,13 +64,6 @@ public class IpnbCodePanel extends IpnbEditablePanel<JComponent, IpnbCodeCell> {
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;
@@ -15,15 +15,16 @@
*/
package org.jetbrains.plugins.ipnb.editor.panels.code;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.Disposable;
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.ui.Gray;
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.actions.IpnbRunCellInplaceAction;
import org.jetbrains.plugins.ipnb.editor.actions.IpnbRunCellBaseAction;
import org.jetbrains.plugins.ipnb.editor.panels.IpnbEditorPanel;
import org.jetbrains.plugins.ipnb.editor.panels.IpnbFilePanel;
import org.jetbrains.plugins.ipnb.editor.panels.IpnbPanel;
@@ -73,7 +74,12 @@ public class IpnbCodeSourcePanel extends IpnbPanel<JComponent, IpnbCodeCell> imp
else {
myEditor = IpnbEditorUtil.createPythonCodeEditor(myProject, this);
}
Disposer.register(myParent.getFileEditor(), new Disposable() {
@Override
public void dispose() {
EditorFactory.getInstance().releaseEditor(myEditor);
}
});
final JComponent component = myEditor.getComponent();
final JComponent contentComponent = myEditor.getContentComponent();
@@ -100,12 +106,10 @@ public class IpnbCodeSourcePanel extends IpnbPanel<JComponent, IpnbCodeCell> imp
UIUtil.requestFocus(getIpnbCodePanel().getFileEditor().getIpnbFilePanel());
}
else if (keyCode == KeyEvent.VK_ENTER && InputEvent.CTRL_DOWN_MASK == e.getModifiersEx()) {
final IpnbRunCellInplaceAction action = (IpnbRunCellInplaceAction)ActionManager.getInstance().getAction("IpnbRunCellInplaceAction");
action.runCell(ipnbFilePanel, false);
IpnbRunCellBaseAction.runCell(ipnbFilePanel, false);
}
else if (keyCode == KeyEvent.VK_ENTER && InputEvent.SHIFT_DOWN_MASK == e.getModifiersEx()) {
final IpnbRunCellAction action = (IpnbRunCellAction)ActionManager.getInstance().getAction("IpnbRunCellAction");
action.runCell(ipnbFilePanel, true);
IpnbRunCellBaseAction.runCell(ipnbFilePanel, true);
}
}
@@ -4,8 +4,7 @@ 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 com.intellij.openapi.editor.Document;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.ipnb.editor.panels.IpnbEditablePanel;
import org.jetbrains.plugins.ipnb.editor.panels.IpnbFilePanel;
@@ -31,13 +30,13 @@ public class IpnbParser {
}
@NotNull
public static IpnbFile parseIpnbFile(@NotNull String fileText, String path) throws IOException {
IpnbFileRaw rawFile = gson.fromJson(fileText, IpnbFileRaw.class);
public static IpnbFile parseIpnbFile(@NotNull final CharSequence fileText, String path) throws IOException {
IpnbFileRaw rawFile = gson.fromJson(fileText.toString(), 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;
final List<IpnbCellRaw> rawCells = worksheet.cells;
for (IpnbCellRaw rawCell : rawCells) {
cells.add(rawCell.createCell());
}
@@ -46,14 +45,19 @@ public class IpnbParser {
}
@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 IpnbFile parseIpnbFile(@NotNull Document document, @NotNull final String path) throws IOException {
return parseIpnbFile(document.getImmutableCharSequence(), path);
}
public static void saveIpnbFile(@NotNull final IpnbFilePanel ipnbPanel) {
final String json = newDocumentText(ipnbPanel);
if (json == null) return;
writeToFile(ipnbPanel.getIpnbFile().getPath(), json);
}
public static String newDocumentText(@NotNull final IpnbFilePanel ipnbPanel) {
final IpnbFile ipnbFile = ipnbPanel.getIpnbFile();
if (ipnbFile == null) return;
if (ipnbFile == null) return null;
for (IpnbEditablePanel panel : ipnbPanel.getIpnbPanels()) {
if (panel.isModified()) {
panel.updateCellSource();
@@ -62,14 +66,14 @@ public class IpnbParser {
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.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();
return gson.toJson(fileRaw);
}
private static void writeToFile(@NotNull final String path, @NotNull final String json) {
final File file = new File(path);
FileWriter writer = null;
try {
@@ -96,8 +100,9 @@ public class IpnbParser {
}
private static class IpnbWorksheet {
IpnbCellRaw[] cells;
List<IpnbCellRaw> cells = new ArrayList<IpnbCellRaw>();
}
private static class IpnbCellRaw {
String cell_type;
Integer level;