diff --git a/python/helpers/pydev/pydevconsole.py b/python/helpers/pydev/pydevconsole.py deleted file mode 100644 index 0e72d63d024d..000000000000 --- a/python/helpers/pydev/pydevconsole.py +++ /dev/null @@ -1,225 +0,0 @@ -try: - from code import InteractiveConsole -except ImportError: - from pydevconsole_code_for_ironpython import InteractiveConsole - -import os -import sys - -try: - False - True -except NameError: # version < 2.3 -- didn't have the True/False builtins - import __builtin__ - setattr(__builtin__, 'True', 1) #Python 3.0 does not accept __builtin__.True = 1 in its syntax - setattr(__builtin__, 'False', 0) - -try: - try: - import xmlrpclib - except ImportError: - import xmlrpc.client as xmlrpclib -except ImportError: - import _pydev_xmlrpclib as xmlrpclib - -#======================================================================================================================= -# StdIn -#======================================================================================================================= -class StdIn: - ''' - Object to be added to stdin (to emulate it as non-blocking while the next line arrives) - ''' - - def __init__(self, interpreter, host, client_port): - self.interpreter = interpreter - self.client_port = client_port - self.host = host - - def readline(self, *args, **kwargs): #@UnusedVariable - #Ok, callback into the client to see get the new input - server = xmlrpclib.Server('http://%s:%s' % (self.host, self.client_port)) - return server.RequestInput() - - def isatty(self): - return False #not really a file - - def write(self, *args, **kwargs): - pass #not available StdIn (but it can be expected to be in the stream interface) - - def flush(self, *args, **kwargs): - pass #not available StdIn (but it can be expected to be in the stream interface) - - #in the interactive interpreter, a read and a readline are the same. - read = readline - -#======================================================================================================================= -# InterpreterInterface -#======================================================================================================================= -class InterpreterInterface: - ''' - The methods in this class should be registered in the xml-rpc server. - ''' - - def __init__(self, host, client_port): - self.client_port = client_port - self.host = host - self.namespace = {} - self.interpreter = InteractiveConsole(self.namespace) - - - def addExec(self, line): - #f_opened = open('c:/temp/a.txt', 'a') - #f_opened.write(line+'\n') - - original_in = sys.stdin - try: - help = None - if 'pydoc' in sys.modules: - pydoc = sys.modules['pydoc'] #Don't import it if it still is not there. - - - if hasattr(pydoc, 'help'): - #You never know how will the API be changed, so, let's code defensively here - help = pydoc.help - if not hasattr(help, 'input'): - help = None - except: - #Just ignore any error here - pass - - sys.stdin = StdIn(self, self.host, self.client_port) - if help is not None: - #This will enable the help() function to work. - help.input = sys.stdin - - try: - more = self.interpreter.push(line) - finally: - if help is not None: - help.input = original_in - - sys.stdin = original_in - - #it's always false at this point - need_input = False - return more, need_input - - - def getCompletions(self, text): - from _completer import Completer - completer = Completer(self.namespace, None) - return completer.complete(text) - - - def getDescription(self, text): - obj = None - if '.' not in text: - try: - obj = self.namespace[text] - except KeyError: - return '' - - else: - try: - splitted = text.split('.') - obj = self.namespace[splitted[0]] - for t in splitted[1:]: - obj = getattr(obj, t) - except: - return '' - - - if obj is not None: - try: - if sys.platform.startswith("java"): - #Jython - doc = obj.__doc__ - if doc is not None: - return doc - - import jyimportsTipper - is_method, infos = jyimportsTipper.ismethod(obj) - ret = '' - if is_method: - for info in infos: - ret += info.getAsDoc() - return ret - - else: - #Python and Iron Python - import inspect #@UnresolvedImport - doc = inspect.getdoc(obj) - if doc is not None: - return doc - except: - pass - - try: - #if no attempt succeeded, try to return repr()... - return repr(obj) - except: - try: - #otherwise the class - return str(obj.__class__) - except: - #if all fails, go to an empty string - return '' - - - def close(self): - sys.exit(0) - - - -#======================================================================================================================= -# _DoExit -#======================================================================================================================= -def _DoExit(*args): - ''' - We have to override the exit because calling sys.exit will only actually exit the main thread, - and as we're in a Xml-rpc server, that won't work. - ''' - - try: - import java.lang.System - java.lang.System.exit(1) - except ImportError: - if len(args) == 1: - os._exit(args[0]) - else: - os._exit(0) - -#======================================================================================================================= -# StartServer -#======================================================================================================================= -def StartServer(host, port, client_port): - #replace exit (see comments on method) - #note that this does not work in jython!!! (sys method can't be replaced). - sys.exit = _DoExit - - try: - try: - from SimpleXMLRPCServer import SimpleXMLRPCServer - except ImportError: - from xmlrpc.server import SimpleXMLRPCServer - except ImportError: - from _pydev_SimpleXMLRPCServer import SimpleXMLRPCServer - - interpreter = InterpreterInterface(host, client_port) - server = SimpleXMLRPCServer((host, port), logRequests=False) - - server.register_function(interpreter.addExec) - server.register_function(interpreter.getCompletions) - server.register_function(interpreter.getDescription) - server.register_function(interpreter.close) - - server.serve_forever() - - -#======================================================================================================================= -# main -#======================================================================================================================= -if __name__ == '__main__': - port, client_port = sys.argv[1:3] - StartServer('localhost', int(port), int(client_port)) - diff --git a/python/src/com/jetbrains/python/console/PyConsoleHighlightingUtil.java b/python/src/com/jetbrains/python/console/PyConsoleHighlightingUtil.java new file mode 100644 index 000000000000..fab374431e78 --- /dev/null +++ b/python/src/com/jetbrains/python/console/PyConsoleHighlightingUtil.java @@ -0,0 +1,69 @@ +package com.jetbrains.python.console; + +import com.intellij.execution.console.LanguageConsoleImpl; +import com.intellij.execution.process.ColoredProcessHandler; +import com.intellij.execution.process.ConsoleHighlighter; +import com.intellij.execution.process.ProcessOutputTypes; +import com.intellij.execution.ui.ConsoleViewContentType; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.util.Key; +import com.jetbrains.python.PyHighlighter; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author oleg + */ +public class PyConsoleHighlightingUtil { + public static final String ORDINARY_PROMPT = ">>> "; + public static final String INDENT_PROMPT = "... "; + static final String HELP_PROMPT = "help> "; + + public static final Key STRING_KEY = new Key("PYTHON_STRING"); + public static final Key NUMBER_KEY = new Key("PYTHON_NUMBER"); + + static final String NUMBERS = "((0[xX][0-9a-fA-F]+)|(\\d+(_\\d+)*(\\.\\d+)?))"; + static final String STRINGS = "((\"[^\"\n]*\")|((?>> "; - private static final String INDENT_PROMPT = "... "; - private static final String HELP_PROMPT = "help> "; - - private final String[] PROMPTS = new String[]{ORDINARY_PROMPT, INDENT_PROMPT, HELP_PROMPT}; - - public static final Key STRING_KEY = new Key("PYTHON_STRING"); - public static final Key NUMBER_KEY = new Key("PYTHON_NUMBER"); - - private static final String NUMBERS = "((0[xX][0-9a-fA-F]+)|(\\d+(_\\d+)*(\\.\\d+)?))"; - private static final String STRINGS = "((\"[^\"\n]*\")|((? 0){ builder.append(' '); } diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java index 3494a35172f0..0a16d9c264ad 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java @@ -1,14 +1,17 @@ package com.jetbrains.python.console; import com.intellij.execution.ExecutionException; +import com.intellij.execution.Executor; import com.intellij.execution.console.LanguageConsoleImpl; import com.intellij.execution.process.*; -import com.intellij.execution.ui.ConsoleViewContentType; +import com.intellij.execution.ui.RunContentDescriptor; +import com.intellij.execution.ui.actions.CloseAction; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.encoding.EncodingManager; import com.intellij.util.net.NetUtils; import com.jetbrains.django.run.ExecutionHelper; import com.jetbrains.django.run.Runner; @@ -20,8 +23,8 @@ import com.jetbrains.python.console.pydev.PydevConsoleCommunication; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.awt.event.ActionEvent; import java.io.IOException; -import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -49,7 +52,7 @@ public class PydevConsoleRunner extends PyConsoleRunner { final String consoleTitle = "TRUE console"; final int[] ports; try { - // File "pydev/pydevconsole.py", line 223, in + // File "pydev/console/pydevconsole.py", line 223, in // port, client_port = sys.argv[1:3] ports = NetUtils.findAvailableSocketPorts(2); } @@ -57,7 +60,8 @@ public class PydevConsoleRunner extends PyConsoleRunner { ExecutionHelper.showErrors(project, Arrays.asList(e), consoleTitle, null); return; } - final ArrayList args = new ArrayList(Arrays.asList(sdk.getHomePath(), "-u", PythonHelpersLocator.getHelperPath("pydev/pydevconsole.py"))); + final ArrayList args = new ArrayList( + Arrays.asList(sdk.getHomePath(), "-u", PythonHelpersLocator.getHelperPath("pydev/console/pydevconsole.py"))); for (int port : ports) { args.add(String.valueOf(port)); } @@ -99,21 +103,101 @@ public class PydevConsoleRunner extends PyConsoleRunner { @Override public void initAndRun() throws ExecutionException { super.initAndRun(); + try { + Thread.sleep(100); + } + catch (InterruptedException e) { + // Ignore + } sendInput("import sys; print('Python %s on %s' % (sys.version, sys.platform))\n"); } + @Override + protected AnAction createCloseAction(final Executor defaultExecutor, final RunContentDescriptor myDescriptor) { + final AnAction generalCloseAction = super.createCloseAction(defaultExecutor, myDescriptor); + final AnAction closeAction = new AnAction() { + @Override + public void update(AnActionEvent e) { + generalCloseAction.update(e); + } + + @Override + public void actionPerformed(AnActionEvent e) { + if (myPydevConsoleCommunication != null) { + try { + myPydevConsoleCommunication.close(); + } + catch (Exception e1) { + // Ignore + } + generalCloseAction.actionPerformed(e); + } + } + }; + closeAction.copyFrom(generalCloseAction); + return closeAction; + } + + @Override + protected AnAction createStopAction() { + final AnAction generalStopAction = super.createStopAction(); + final AnAction stopAction = new AnAction() { + @Override + public void update(AnActionEvent e) { + generalStopAction.update(e); + } + + @Override + public void actionPerformed(AnActionEvent e) { + if (myPydevConsoleCommunication != null) { + try { + myPydevConsoleCommunication.close(); + } + catch (Exception e1) { + // Ignore + } + generalStopAction.actionPerformed(e); + } + } + }; + stopAction.copyFrom(generalStopAction); + return stopAction; + } + @Override protected void sendInput(final String input) { if (myPydevConsoleCommunication != null){ + //if ("comp\n".equals(input)){ + // String[] completions = new String[]{}; + // try { + // completions = myPydevConsoleCommunication.getCompletions("", 0); + // } + // catch (Exception e) { + // PyConsoleProcessHandler.processOutput(myConsoleView.getConsole(), e.toString()+'\n', ProcessOutputTypes.STDERR); + // } + // for (String completion : completions) { + // PyConsoleProcessHandler.processOutput(myConsoleView.getConsole(), completion + "\n", ProcessOutputTypes.STDOUT); + // } + // return; + //} myPydevConsoleCommunication.execInterpreter(input, new ICallback() { public Object call(final InterpreterResponse interpreterResponse) { final LanguageConsoleImpl console = myConsoleView.getConsole(); - // TODO[oleg] More sophisticated callback required! - // At least this one works! - if (!StringUtil.isEmpty(interpreterResponse.err)){ - PyConsoleProcessHandler.processOutput(console, interpreterResponse.err, ProcessOutputTypes.STDERR); + // Handle prompt + if (interpreterResponse.more){ + if (!PyConsoleHighlightingUtil.INDENT_PROMPT.equals(console.getPrompt())){ + console.setPrompt(PyConsoleHighlightingUtil.INDENT_PROMPT); + } } else { - PyConsoleProcessHandler.processOutput(console, interpreterResponse.out, ProcessOutputTypes.STDOUT); + if (!PyConsoleHighlightingUtil.ORDINARY_PROMPT.equals(console.getPrompt())){ + console.setPrompt(PyConsoleHighlightingUtil.ORDINARY_PROMPT); + } + } + // Handle output + if (!StringUtil.isEmpty(interpreterResponse.err)){ + PyConsoleHighlightingUtil.processOutput(console, interpreterResponse.err, ProcessOutputTypes.STDERR); + } else { + PyConsoleHighlightingUtil.processOutput(console, interpreterResponse.out, ProcessOutputTypes.STDOUT); } return null; } diff --git a/python/src/com/jetbrains/python/console/PydevWebServer.java b/python/src/com/jetbrains/python/console/PydevWebServer.java new file mode 100644 index 000000000000..5d88b76792a1 --- /dev/null +++ b/python/src/com/jetbrains/python/console/PydevWebServer.java @@ -0,0 +1,26 @@ +package com.jetbrains.python.console; + +import com.jetbrains.python.console.pydev.PydevConsoleCommunication; +import org.apache.xmlrpc.WebServer; +import org.apache.xmlrpc.XmlRpcHandlerMapping; +import org.apache.xmlrpc.XmlRpcServer; + +/** + * Web server to handle all the input requests by PydevConsoleCommunication + * @author oleg + */ +public class PydevWebServer extends WebServer { + public PydevWebServer(int port, final PydevConsoleCommunication communication) { + super(port, null, new XmlRpcServer(){ + final XmlRpcHandlerMapping xmlRpcHandlerMapping = new XmlRpcHandlerMapping() { + public Object getHandler(String handlerName) throws Exception { + return communication; + } + }; + @Override + public XmlRpcHandlerMapping getHandlerMapping() { + return xmlRpcHandlerMapping; + } + }); + } +}