diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/configuration/IpnbConnectionManager.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/configuration/IpnbConnectionManager.java index f850663c91e2..c76751618387 100644 --- a/python/ipnb/src/org/jetbrains/plugins/ipnb/configuration/IpnbConnectionManager.java +++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/configuration/IpnbConnectionManager.java @@ -6,6 +6,7 @@ import com.intellij.execution.ExecutionException; import com.intellij.execution.RunContentExecutor; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.process.KillableColoredProcessHandler; +import com.intellij.execution.process.UnixProcessManager; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.diagnostic.Logger; @@ -157,13 +158,22 @@ public final class IpnbConnectionManager implements ProjectComponent { @Override public void onOutput(@NotNull IpnbConnection connection, - @NotNull String parentMessageId, - @NotNull List outputs, - @Nullable Integer execCount) { + @NotNull String parentMessageId) { + if (!myUpdateMap.containsKey(parentMessageId)) return; + final IpnbCodePanel cell = myUpdateMap.get(parentMessageId); + cell.getCell().setPromptNumber(connection.getExecCount()); + //noinspection unchecked + cell.updatePanel(null, (List)connection.getOutput().clone()); + } + + @Override + public void onPayload(@Nullable String payload, @NotNull String parentMessageId) { if (!myUpdateMap.containsKey(parentMessageId)) return; final IpnbCodePanel cell = myUpdateMap.remove(parentMessageId); - cell.getCell().setPromptNumber(execCount); - cell.updatePanel(outputs); + if (payload != null) { + //noinspection unchecked + cell.updatePanel(payload, null); + } } }; @@ -296,6 +306,7 @@ public final class IpnbConnectionManager implements ProjectComponent { private void shutdownKernels() { for (IpnbConnection connection : myKernels.values()) { + if (!connection.isAlive()) continue; connection.shutdown(); try { connection.close(); @@ -309,7 +320,8 @@ public final class IpnbConnectionManager implements ProjectComponent { } myKernels.clear(); if (myProcessHandler != null && !myProcessHandler.isProcessTerminated()) { - myProcessHandler.killProcess(); + myProcessHandler.destroyProcess(); + UnixProcessManager.sendSigIntToProcessTree(myProcessHandler.getProcess()); } } diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbCodePanel.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbCodePanel.java index a376e84db6b9..3b4980e8ba97 100644 --- a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbCodePanel.java +++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbCodePanel.java @@ -1,6 +1,7 @@ package org.jetbrains.plugins.ipnb.editor.panels.code; import com.google.common.collect.Lists; +import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; @@ -9,6 +10,7 @@ import com.intellij.openapi.ui.VerticalFlowLayout; 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; @@ -117,7 +119,6 @@ public class IpnbCodePanel extends IpnbEditablePanel { super.runCell(); updateCellSource(); myCell.setPromptNumber(-1); - updatePanel(myCell.getCellOutputs()); final IpnbConnectionManager connectionManager = IpnbConnectionManager.getInstance(myProject); connectionManager.executeCell(this); setEditing(false); @@ -135,10 +136,20 @@ public class IpnbCodePanel extends IpnbEditablePanel { myCell.setSource(StringUtil.splitByLinesKeepSeparators(text)); } - public void updatePanel(@NotNull final List outputContent) { - ApplicationManager.getApplication().invokeLater(new Runnable() { + public void updatePanel(@Nullable final String replacementContent, @Nullable final List outputContent) { + final Application application = ApplicationManager.getApplication(); + application.invokeLater(new Runnable() { @Override public void run() { + if (replacementContent != null) { + myCell.setSource(StringUtil.splitByLinesKeepSeparators(replacementContent)); + application.runWriteAction(new Runnable() { + @Override + public void run() { + myCodeSourcePanel.getEditor().getDocument().setText(replacementContent); + } + }); + } myCell.removeCellOutputs(); myViewPanel.removeAll(); @@ -147,9 +158,11 @@ public class IpnbCodePanel extends IpnbEditablePanel { addPromptPanel(panel, myCell.getPromptNumber(), IpnbEditorUtil.PromptType.In, myCodeSourcePanel); myViewPanel.add(panel); - for (IpnbOutputCell output : outputContent) { - myCell.addCellOutput(output); - addOutputPanel(myViewPanel, output, true); + if (outputContent != null) { + for (IpnbOutputCell output : outputContent) { + myCell.addCellOutput(output); + addOutputPanel(myViewPanel, output, true); + } } final IpnbFilePanel filePanel = myParent.getIpnbFilePanel(); diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/protocol/IpnbConnection.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/protocol/IpnbConnection.java index 6c88d8e885e5..8c19acc2715c 100644 --- a/python/ipnb/src/org/jetbrains/plugins/ipnb/protocol/IpnbConnection.java +++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/protocol/IpnbConnection.java @@ -43,6 +43,10 @@ public class IpnbConnection { private volatile boolean myIsIOPubOpen = false; protected volatile boolean myIsOpened = false; + private ArrayList myOutput = new ArrayList(); + private int myExecCount; + + public IpnbConnection(@NotNull String uri, @NotNull IpnbConnectionListener listener) throws IOException, URISyntaxException { myURI = new URI(uri); myListener = listener; @@ -310,6 +314,33 @@ public class IpnbConnection { private interface PyContent {} + @SuppressWarnings("UnusedDeclaration") + protected static class Payload { + String text; + boolean replace; + String source; + } + + @SuppressWarnings("UnusedDeclaration") + protected static class PyExecuteReplyContent implements PyContent { + private int execution_count; + private JsonObject metadata; + private String status; + private List payload; + + public int getExecutionCount() { + return execution_count; + } + + public JsonObject getMetadata() { + return metadata; + } + + public List getPayload() { + return payload; + } + } + @SuppressWarnings("UnusedDeclaration") protected static class PyOutContent implements PyContent { private int execution_count; @@ -388,10 +419,7 @@ public class IpnbConnection { } protected class IpnbWebSocketClient extends WebSocketClient { - private ArrayList myOutput = new ArrayList(); - private Integer myExecCount = null; - - IpnbWebSocketClient(URI serverUri, Draft draft) { + protected IpnbWebSocketClient(@NotNull final URI serverUri, @NotNull final Draft draft) { super(serverUri, draft); } @@ -413,6 +441,19 @@ public class IpnbConnection { final PyOutContent content = gson.fromJson(msg.getContent(), PyOutContent.class); addCellOutput(content, myOutput); } + if ("execute_reply".equals(messageType)) { + final PyExecuteReplyContent content = gson.fromJson(msg.getContent(), PyExecuteReplyContent.class); + final List payloads = content.payload; + if (payloads != null && !payloads.isEmpty()) { + final Payload payload = payloads.get(0); + if (payload.replace) { + myListener.onPayload(payload.text, parentHeader.getMessageId()); + } + } + else { + myListener.onPayload(null, parentHeader.getMessageId()); + } + } else if ("pyerr".equals(messageType) || "error".equals(messageType)) { final PyErrContent content = gson.fromJson(msg.getContent(), PyErrContent.class); addCellOutput(content, myOutput); @@ -429,9 +470,9 @@ public class IpnbConnection { } 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)myOutput.clone(), myExecCount); + final String executionState = content.getExecutionState(); + if ("idle".equals(executionState)) { + myListener.onOutput(IpnbConnection.this, parentHeader.getMessageId()); myOutput.clear(); } } @@ -447,4 +488,13 @@ public class IpnbConnection { } } + + public ArrayList getOutput() { + return myOutput; + } + + public int getExecCount() { + return myExecCount; + } + } diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/protocol/IpnbConnectionListener.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/protocol/IpnbConnectionListener.java index 8def54cb44a0..a4fb5cf15ad0 100644 --- a/python/ipnb/src/org/jetbrains/plugins/ipnb/protocol/IpnbConnectionListener.java +++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/protocol/IpnbConnectionListener.java @@ -2,9 +2,6 @@ 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 @@ -14,7 +11,8 @@ import java.util.List; public interface IpnbConnectionListener { void onOpen(@NotNull IpnbConnection connection); void onOutput(@NotNull IpnbConnection connection, - @NotNull String parentMessageId, - @NotNull List outputs, - @Nullable Integer execCount); + @NotNull String parentMessageId); + + void onPayload(@Nullable final String payload, + @NotNull String parentMessageId); } diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/protocol/IpnbConnectionListenerBase.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/protocol/IpnbConnectionListenerBase.java index 2fb6fa5881ec..61a97a4b193d 100644 --- a/python/ipnb/src/org/jetbrains/plugins/ipnb/protocol/IpnbConnectionListenerBase.java +++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/protocol/IpnbConnectionListenerBase.java @@ -2,9 +2,6 @@ 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 @@ -16,8 +13,10 @@ public class IpnbConnectionListenerBase implements IpnbConnectionListener { @Override public void onOutput(@NotNull IpnbConnection connection, - @NotNull String parentMessageId, - @NotNull List outputs, - @Nullable Integer execCount) { + @NotNull String parentMessageId) { + } + + public void onPayload(@Nullable final String payload, + @NotNull String parentMessageId) { } } diff --git a/python/ipnb/testSrc/WebSocketConnectionTest.java b/python/ipnb/testSrc/WebSocketConnectionTest.java index 97398fed0310..4e88164800d9 100644 --- a/python/ipnb/testSrc/WebSocketConnectionTest.java +++ b/python/ipnb/testSrc/WebSocketConnectionTest.java @@ -8,7 +8,7 @@ import org.jetbrains.plugins.ipnb.protocol.IpnbConnectionListenerBase; import java.io.IOException; import java.net.URISyntaxException; -import java.util.List; +import java.util.ArrayList; /** * @@ -49,10 +49,9 @@ public class WebSocketConnectionTest extends TestCase { @Override public void onOutput(@NotNull IpnbConnection connection, - @NotNull String parentMessageId, - @NotNull List outputs, - Integer execCount) { + @NotNull String parentMessageId) { if (myMessageId.equals(parentMessageId)) { + final ArrayList outputs = connection.getOutput(); assertEquals(outputs.size(), 1); assertEquals(outputs.get(0).getClass(), IpnbOutOutputCell.class); final String[] text = outputs.get(0).getText(); @@ -85,10 +84,9 @@ public class WebSocketConnectionTest extends TestCase { @Override public void onOutput(@NotNull IpnbConnection connection, - @NotNull String parentMessageId, - @NotNull List outputs, - Integer execCount) { + @NotNull String parentMessageId) { if (myMessageId.equals(parentMessageId)) { + final ArrayList outputs = connection.getOutput(); assertEquals(outputs.size(), 1); assertEquals(outputs.get(0).getClass(), IpnbOutOutputCell.class); final String[] text = outputs.get(0).getText();