diff --git a/python/helpers/pydev/pydev_console_utils.py b/python/helpers/pydev/pydev_console_utils.py index bc6708727c26..ef8f68670239 100644 --- a/python/helpers/pydev/pydev_console_utils.py +++ b/python/helpers/pydev/pydev_console_utils.py @@ -132,6 +132,11 @@ class StdIn(BaseStdIn): return '\n' +class ExecCode: + def __init__(self, code, is_single_line=True): + self.code = code + self.is_single_line = is_single_line + #======================================================================================================================= # BaseInterpreterInterface #======================================================================================================================= @@ -142,20 +147,14 @@ class BaseInterpreterInterface: self.exec_queue = _queue.Queue(0) self.buffer = [] - def needMore(self, buffer, line): - if not buffer: - buffer = [] - buffer.append(line) - source = "\n".join(buffer) + def needMoreForCode(self, source): if hasattr(self.interpreter, 'is_complete'): return not self.interpreter.is_complete(source) - try: - code = self.interpreter.compile(source, "", "single") + code = self.interpreter.compile(source, "", "exec") except (OverflowError, SyntaxError, ValueError): # Case 1 return False - if code is None: # Case 2 return True @@ -163,8 +162,16 @@ class BaseInterpreterInterface: # Case 3 return False + def needMore(self, buffer, line): + if not buffer: + buffer = [] + buffer.append(line) + source = "\n".join(buffer) + + return self.needMoreForCode(source) - def addExec(self, line): + + def addExec(self, command): original_in = sys.stdin try: help = None @@ -203,7 +210,7 @@ class BaseInterpreterInterface: try: self.startExec() - more = self.doAddExec(line) + more = self.doAddExec(command) self.finishExec() finally: if help is not None: @@ -229,7 +236,7 @@ class BaseInterpreterInterface: return more, need_input - def doAddExec(self, line): + def doAddExec(self, command): ''' Subclasses should override. @@ -309,14 +316,22 @@ class BaseInterpreterInterface: def execLine(self, line): try: - #buffer = self.interpreter.buffer[:] - self.exec_queue.put(line) + self.exec_queue.put(ExecCode(line, True)) return self.needMore(self.buffer, line) except: traceback.print_exc() return False + def execMultipleLines(self, lines): + try: + self.exec_queue.put(ExecCode(lines, False)) + return self.needMoreForCode(lines) + except: + traceback.print_exc() + return False + + def interrupt(self): try: if self.interruptable: diff --git a/python/helpers/pydev/pydev_ipython_console.py b/python/helpers/pydev/pydev_ipython_console.py index d3d4ae8492a3..d38127165bea 100644 --- a/python/helpers/pydev/pydev_ipython_console.py +++ b/python/helpers/pydev/pydev_ipython_console.py @@ -39,14 +39,14 @@ class InterpreterInterface(BaseInterpreterInterface): def get_greeting_msg(self): return self.interpreter.get_greeting_msg() - def doAddExec(self, line): + def doAddExec(self, command): self.notify_about_magic() - if (line.rstrip().endswith('??')): + if (command.rstrip().endswith('??')): print('IPython-->') try: - res = bool(self.interpreter.addExec(line)) + res = bool(self.interpreter.addExec(command)) finally: - if (line.rstrip().endswith('??')): + if (command.rstrip().endswith('??')): print('<--IPython') return res diff --git a/python/helpers/pydev/pydevconsole.py b/python/helpers/pydev/pydevconsole.py index 026fb7f9880c..a5f66a80d3e4 100644 --- a/python/helpers/pydev/pydevconsole.py +++ b/python/helpers/pydev/pydevconsole.py @@ -10,7 +10,6 @@ import os import sys from pydevd_constants import USE_LIB_COPY -from pydevd_utils import * if USE_LIB_COPY: import _pydev_threading as threading @@ -72,56 +71,25 @@ try: except ImportError: import _pydev_xmlrpclib as xmlrpclib -try: - class ExecState: - FIRST_CALL = True - PYDEV_CONSOLE_RUN_IN_UI = False #Defines if we should run commands in the UI thread. - from org.python.pydev.core.uiutils import RunInUiThread #@UnresolvedImport - from java.lang import Runnable #@UnresolvedImport +class Command: + def __init__(self, interpreter, command): + """ + :type command: object + :type interpreter: InteractiveConsole + """ + self.interpreter = interpreter + self.command = command + self.more = None - class Command(Runnable): - def __init__(self, interpreter, line): - self.interpreter = interpreter - self.line = line - - def run(self): - if ExecState.FIRST_CALL: - ExecState.FIRST_CALL = False - sys.stdout.write('\nYou are now in a console within Eclipse.\nUse it with care as it can halt the VM.\n') - sys.stdout.write( - 'Typing a line with "PYDEV_CONSOLE_TOGGLE_RUN_IN_UI"\nwill start executing all the commands in the UI thread.\n\n') - - if self.line == 'PYDEV_CONSOLE_TOGGLE_RUN_IN_UI': - ExecState.PYDEV_CONSOLE_RUN_IN_UI = not ExecState.PYDEV_CONSOLE_RUN_IN_UI - if ExecState.PYDEV_CONSOLE_RUN_IN_UI: - sys.stdout.write( - 'Running commands in UI mode. WARNING: using sys.stdin (i.e.: calling raw_input()) WILL HALT ECLIPSE.\n') - else: - sys.stdout.write('No longer running commands in UI mode.\n') - self.more = False - else: - self.more = self.interpreter.push(self.line) - - - def Sync(runnable): - if ExecState.PYDEV_CONSOLE_RUN_IN_UI: - return RunInUiThread.sync(runnable) + def run(self): + if self.command.is_single_line: + self.more = self.interpreter.push(self.command.code) else: - return runnable.run() + self.more = self.interpreter.runsource(self.command.code, '', 'exec') -except: - #If things are not there, define a way in which there's no 'real' sync, only the default execution. - class Command: - def __init__(self, interpreter, line): - self.interpreter = interpreter - self.line = line - - def run(self): - self.more = self.interpreter.push(self.line) - - def Sync(runnable): - runnable.run() +def Sync(runnable): + runnable.run() try: try: @@ -152,8 +120,8 @@ class InterpreterInterface(BaseInterpreterInterface): self._input_error_printed = False - def doAddExec(self, line): - command = Command(self.interpreter, line) + def doAddExec(self, command): + command = Command(self.interpreter, command) Sync(command) return command.more @@ -185,11 +153,11 @@ def process_exec_queue(interpreter): while 1: try: try: - line = interpreter.exec_queue.get(block=True, timeout=0.05) + command = interpreter.exec_queue.get(block=True, timeout=0.05) except _queue.Empty: continue - if not interpreter.addExec(line): #TODO: think about locks here + if not interpreter.addExec(command): #TODO: think about locks here interpreter.buffer = [] except KeyboardInterrupt: interpreter.buffer = [] @@ -274,6 +242,7 @@ def start_server(host, port, interpreter): raise server.register_function(interpreter.execLine) + server.register_function(interpreter.execMultipleLines) server.register_function(interpreter.getCompletions) server.register_function(interpreter.getFrame) server.register_function(interpreter.getVariable) diff --git a/python/pydevSrc/com/jetbrains/python/console/pydev/ConsoleCommunication.java b/python/pydevSrc/com/jetbrains/python/console/pydev/ConsoleCommunication.java index a114c2c07f57..51604d73ee5c 100644 --- a/python/pydevSrc/com/jetbrains/python/console/pydev/ConsoleCommunication.java +++ b/python/pydevSrc/com/jetbrains/python/console/pydev/ConsoleCommunication.java @@ -18,7 +18,7 @@ public interface ConsoleCommunication { boolean isExecuting(); - void execInterpreter(String s, Function callback); + void execInterpreter(ConsoleCodeFragment code, Function callback); void interrupt(); @@ -27,4 +27,21 @@ public interface ConsoleCommunication { void notifyCommandExecuted(); void notifyInputRequested(); + class ConsoleCodeFragment { + private final String myText; + private final boolean myIsSingleLine; + + public ConsoleCodeFragment(String text, boolean isSingleLine) { + myText = text; + myIsSingleLine = isSingleLine; + } + + public String getText() { + return myText; + } + + public boolean isSingleLine() { + return myIsSingleLine; + } + } } diff --git a/python/pydevSrc/com/jetbrains/python/console/pydev/IScriptConsoleCommunication.java b/python/pydevSrc/com/jetbrains/python/console/pydev/IScriptConsoleCommunication.java deleted file mode 100644 index 3c998c8e1e32..000000000000 --- a/python/pydevSrc/com/jetbrains/python/console/pydev/IScriptConsoleCommunication.java +++ /dev/null @@ -1,54 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2005, 2007 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - - *******************************************************************************/ -package com.jetbrains.python.console.pydev; - -import com.intellij.util.Function; - -/** - * Interface for the console communication. - * - * This interface is meant to be the way to communicate with the shell. - */ -public interface IScriptConsoleCommunication { - - /** - * Executes a given command in the interpreter (push a line) - * - * @param command the command to be executed - * @return the response from the interpreter (contains the stdout, stderr, etc). - * @throws Exception - */ - void execInterpreter(String command, Function onResponseReceived); - - /** - * Creates the completions to be applied in the interpreter. - * - * @param text the text with what should be completed (e.g.: xxx.bar.foo) - * @param offset the offset where the completion was requested in the console document - * @return a list of proposals that can be applied for the given text. - * @throws Exception - */ - //public ICompletionProposal[] getCompletions(String text, int offset) throws Exception; - - /** - * Gets the description to be shown on hover to the user - * - * @param text the text representing the completion to be applied - * @return the description to be shown to the user - * @throws Exception - */ - String getDescription(String text) throws Exception; - - /** - * Stops the communication with the server. Should ask the server to terminate at this point. - * @throws Exception - */ - void close(); -} \ No newline at end of file diff --git a/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java b/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java index b0ef1d378699..387226f7ae14 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java @@ -53,10 +53,11 @@ import java.util.Vector; * * @author Fabio */ -public class PydevConsoleCommunication extends AbstractConsoleCommunication implements IScriptConsoleCommunication, XmlRpcHandler, +public class PydevConsoleCommunication extends AbstractConsoleCommunication implements XmlRpcHandler, PyFrameAccessor { private static final String EXEC_LINE = "execLine"; + private static final String EXEC_MULTILINE = "execMultipleLines"; private static final String GET_COMPLETIONS = "getCompletions"; private static final String GET_DESCRIPTION = "getDescription"; private static final String GET_FRAME = "getFrame"; @@ -64,6 +65,7 @@ public class PydevConsoleCommunication extends AbstractConsoleCommunication impl private static final String CHANGE_VARIABLE = "changeVariable"; private static final String HANDSHAKE = "handshake"; private static final String CLOSE = "close"; + /** * XML-RPC client for sending messages to the server. */ @@ -261,12 +263,13 @@ public class PydevConsoleCommunication extends AbstractConsoleCommunication impl /** * Executes the needed command * + * @param command * @return a Pair with (null, more) or (error, false) * @throws XmlRpcException */ - protected Pair exec(final String command) throws XmlRpcException { + protected Pair exec(final ConsoleCodeFragment command) throws XmlRpcException { setExecuting(true); - Object execute = myClient.execute(EXEC_LINE, new Object[]{command}); + Object execute = myClient.execute(command.isSingleLine() ? EXEC_LINE : EXEC_MULTILINE, new Object[]{command.getText()}); Object object; if (execute instanceof Vector) { @@ -314,10 +317,10 @@ public class PydevConsoleCommunication extends AbstractConsoleCommunication impl * * @param command the command to be executed in the client */ - public void execInterpreter(final String command, final Function onResponseReceived) { + public void execInterpreter(final ConsoleCodeFragment command, final Function onResponseReceived) { nextResponse = null; if (waitingForInput) { - inputReceived = command; + inputReceived = command.getText(); waitingForInput = false; //the thread that we started in the last exec is still alive if we were waiting for an input. } diff --git a/python/src/com/jetbrains/python/console/PydevConsoleExecuteActionHandler.java b/python/src/com/jetbrains/python/console/PydevConsoleExecuteActionHandler.java index 7721db25b4b6..4698cc8baec6 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleExecuteActionHandler.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleExecuteActionHandler.java @@ -40,8 +40,6 @@ import com.jetbrains.python.console.pydev.InterpreterResponse; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Scanner; - /** * @author traff */ @@ -76,10 +74,11 @@ public class PydevConsoleExecuteActionHandler extends ConsoleExecuteActionHandle processOneLine(text); } else { - Scanner s = new Scanner(text); - while (s.hasNextLine()) { - String line = s.nextLine(); - processOneLine(line); + if (StringUtil.countNewLines(text.trim()) > 0) { + executeMultiLine(text); + } + else { + processOneLine(text); } } if (execAnyway && myCurrentIndentSize > 0 && indentBefore == 0) { //if code was indented and we need to exec anyway @@ -87,6 +86,19 @@ public class PydevConsoleExecuteActionHandler extends ConsoleExecuteActionHandle } } + private void executeMultiLine(@NotNull String text) { + if (myInputBuffer == null) { + myInputBuffer = new StringBuilder(); + } + + myInputBuffer.append(text); + + final LanguageConsoleImpl console = myConsoleView.getConsole(); + final Editor currentEditor = console.getConsoleEditor(); + + sendLineToConsole(new ConsoleCommunication.ConsoleCodeFragment(myInputBuffer.toString(), false), null, console, currentEditor); + } + private void processOneLine(String line) { int indentSize = IndentHelperImpl.getIndent(getProject(), PythonFileType.INSTANCE, line, false); line = StringUtil.trimTrailing(line); @@ -174,16 +186,22 @@ public class PydevConsoleExecuteActionHandler extends ConsoleExecuteActionHandle } + sendLineToConsole(new ConsoleCommunication.ConsoleCodeFragment(myInputBuffer.toString(), true), line, console, currentEditor); + } + + private void sendLineToConsole(@NotNull final ConsoleCommunication.ConsoleCodeFragment code, + @Nullable final String line, + @NotNull final LanguageConsoleImpl console, + @NotNull final Editor currentEditor) { if (myConsoleCommunication != null) { final boolean waitedForInputBefore = myConsoleCommunication.isWaitingForInput(); - final String command = myInputBuffer.toString(); if (myConsoleCommunication.isWaitingForInput()) { myInputBuffer.setLength(0); } else { executingPrompt(console); } - myConsoleCommunication.execInterpreter(command, new Function() { + myConsoleCommunication.execInterpreter(code, new Function() { public Object fun(final InterpreterResponse interpreterResponse) { // clear myInputBuffer = null; @@ -192,7 +210,8 @@ public class PydevConsoleExecuteActionHandler extends ConsoleExecuteActionHandle more(console, currentEditor); if (myCurrentIndentSize == 0) { // compute current indentation - setCurrentIndentSize(IndentHelperImpl.getIndent(getProject(), PythonFileType.INSTANCE, line, false) + getPythonIndent()); + setCurrentIndentSize( + (line != null ? IndentHelperImpl.getIndent(getProject(), PythonFileType.INSTANCE, line, false) : 0) + getPythonIndent()); // In this case we can insert indent automatically indentEditor(currentEditor, myCurrentIndentSize); } diff --git a/python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java b/python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java index 8f55aed81db1..d24a2a8a8320 100644 --- a/python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java +++ b/python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java @@ -63,8 +63,8 @@ public class PythonDebugConsoleCommunication extends AbstractConsoleCommunicatio return false; } - protected void exec(final String command, final ProcessDebugger.DebugCallback> callback) { - myDebugProcess.consoleExec(command, new ProcessDebugger.DebugCallback() { + protected void exec(final ConsoleCodeFragment command, final ProcessDebugger.DebugCallback> callback) { + myDebugProcess.consoleExec(command.getText(), new ProcessDebugger.DebugCallback() { @Override public void ok(String value) { callback.ok(parseExecResponseString(value)); @@ -77,9 +77,9 @@ public class PythonDebugConsoleCommunication extends AbstractConsoleCommunicatio }); } - public void execInterpreter(String s, final Function callback) { - myExpression.append(s); - exec(myExpression.toString(), new ProcessDebugger.DebugCallback>() { + public void execInterpreter(ConsoleCodeFragment code, final Function callback) { + myExpression.append(code.getText()); + exec(new ConsoleCodeFragment(myExpression.toString(), false), new ProcessDebugger.DebugCallback>() { @Override public void ok(Pair executed) { boolean more = executed.second;