1)added show command-line action to debug console

2)fixed multi-line command-line console input in debug
This commit is contained in:
Dmitry Trofimov
2010-11-05 08:59:18 +03:00
parent bcb0485e74
commit d2382c4b48
20 changed files with 2576 additions and 2242 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -101,6 +101,7 @@ CMD_CHANGE_VARIABLE = 117
CMD_RUN_TO_LINE = 118
CMD_RELOAD_CODE = 119
CMD_GET_COMPLETIONS = 120
CMD_CONSOLE_EXEC = 121
CMD_VERSION = 501
CMD_RETURN = 502
CMD_ERROR = 901
@@ -126,6 +127,7 @@ ID_TO_MEANING = {
'118':'CMD_RUN_TO_LINE',
'119':'CMD_RELOAD_CODE',
'120':'CMD_GET_COMPLETIONS',
'121':'CMD_CONSOLE_EXEC',
'501':'CMD_VERSION',
'502':'CMD_RETURN',
'901':'CMD_ERROR',
@@ -694,6 +696,33 @@ class InternalEvaluateExpression(InternalThreadCommand):
sys.stderr.write('%s\n' % (exc,))
cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error evaluating expression " + exc)
dbg.writer.addCommand(cmd)
#=======================================================================================================================
# InternalConsoleExec
#=======================================================================================================================
class InternalConsoleExec(InternalThreadCommand):
""" gets the value of a variable """
def __init__(self, seq, thread_id, frame_id, expression):
self.sequence = seq
self.thread_id = thread_id
self.frame_id = frame_id
self.expression = expression
def doIt(self, dbg):
""" Converts request into python variable """
try:
result = pydevd_vars.consoleExec(self.thread_id, self.frame_id, self.expression)
xml = "<xml>"
xml += pydevd_vars.varToXML(result, "")
xml += "</xml>"
cmd = dbg.cmdFactory.makeEvaluateExpressionMessage(self.sequence, xml)
dbg.writer.addCommand(cmd)
except:
exc = GetExceptionTracebackStr()
sys.stderr.write('%s\n' % (exc,))
cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error evaluating console expression " + exc)
dbg.writer.addCommand(cmd)
#=======================================================================================================================
# InternalGetCompletions

View File

@@ -4,6 +4,8 @@
from pydevd_constants import * #@UnusedWildImport
from types import * #@UnusedWildImport
from console import pydevconsole
from code import compile_command
try:
from StringIO import StringIO
except ImportError:
@@ -347,9 +349,6 @@ def resolveCompoundVariable(thread_id, frame_id, scope, attrs):
except:
traceback.print_exc()
def evaluateExpressionConsole(expression):
pass
def evaluateExpression(thread_id, frame_id, expression, doExec):
'''returns the result of the evaluated expression
@@ -406,6 +405,29 @@ def evaluateExpression(thread_id, frame_id, expression, doExec):
del updated_globals
del frame
def consoleExec(thread_id, frame_id, expression):
'''returns 'False' in case expression is partialy correct
'''
frame = findFrame(thread_id, frame_id)
expression = str(expression.replace('@LINE@', '\n'))
#Not using frame.f_globals because of https://sourceforge.net/tracker2/?func=detail&aid=2541355&group_id=85796&atid=577329
#(Names not resolved in generator expression in method)
#See message: http://mail.python.org/pipermail/python-list/2009-January/526522.html
updated_globals = {}
updated_globals.update(frame.f_globals)
updated_globals.update(frame.f_locals) #locals later because it has precedence over the actual globals
try:
result = compile_command(expression)
if result is None:
return True
evaluateExpression(thread_id, frame_id, expression, True)
except:
evaluateExpression(thread_id, frame_id, expression, True)
def changeAttrExpression(thread_id, frame_id, attr, expression):
'''Changes some attribute in a given frame.

View File

@@ -0,0 +1,46 @@
package com.jetbrains.python.console.pydev;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
/**
* @author traff
*/
public abstract class AbstractConsoleCommunication implements ConsoleCommunication {
public static final int MAX_ATTEMPTS = 3;
public static final long TIMEOUT = (long)(10e9);
protected final Project myProject;
/**
* Signals that the next command added should be sent as an input to the server.
*/
public volatile boolean waitingForInput;
public AbstractConsoleCommunication(Project project) {
myProject = project;
}
public static Pair<String, Boolean> parseExecResponseString(String str) {
Boolean more;
String errorContents = null;
String lower = str.toLowerCase();
if (lower.equals("true") || lower.equals("1")) {
more = true;
}
else if (lower.equals("false") || lower.equals("0")) {
more = false;
}
else {
more = false;
errorContents = str;
}
return new Pair<String, Boolean>(errorContents, more);
}
@Override
public boolean isWaitingForInput() {
return waitingForInput;
}
}

View File

@@ -9,4 +9,8 @@ public interface ConsoleCommunication {
List<PydevCompletionVariant> getCompletions(String prefix) throws Exception;
String getDescription(String text) throws Exception;
boolean isWaitingForInput();
void execInterpreter(String s, ICallback<Object,InterpreterResponse> callback);
}

View File

@@ -6,45 +6,63 @@ import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.jetbrains.python.debugger.PydevXmlUtils;
import org.apache.xmlrpc.WebServer;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.XmlRpcHandler;
import org.jetbrains.annotations.NotNull;
import java.net.MalformedURLException;
import java.util.*;
import java.util.Collections;
import java.util.List;
import java.util.Vector;
/**
* Communication with Xml-rpc with the client.
*
* @author Fabio
*/
public class PydevConsoleCommunication implements IScriptConsoleCommunication, XmlRpcHandler, ConsoleCommunication {
public class PydevConsoleCommunication extends AbstractConsoleCommunication implements IScriptConsoleCommunication, XmlRpcHandler {
/**
* XML-RPC client for sending messages to the server.
*/
private IPydevXmlRpcClient client;
/**
* Responsible for getting the stdout of the process.
*/
private final ThreadStreamReader stdOutReader;
/**
* Responsible for getting the stderr of the process.
*/
private final ThreadStreamReader stdErrReader;
/**
* This is the server responsible for giving input to a raw_input() requested.
*/
private WebServer webServer;
private static final Logger LOG = Logger.getInstance(PydevConsoleCommunication.class.getName());
private final Project myProject;
public static final int MAX_ATTEMPTS = 3;
public static final long TIMEOUT = (long)(10e9);
/**
* Responsible for getting the stdout of the process.
*/
protected final ThreadStreamReader stdOutReader;
/**
* Responsible for getting the stderr of the process.
*/
protected final ThreadStreamReader stdErrReader;
/**
* Input that should be sent to the server (waiting for raw_input)
*/
protected volatile String inputReceived;
/**
* Response that should be sent back to the shell.
*/
protected volatile InterpreterResponse nextResponse;
/**
* Helper to keep on busy loop.
*/
private volatile Object lock2 = new Object();
/**
* Keeps a flag indicating that we were able to communicate successfully with the shell at least once
* (if we haven't we may retry more than once the first time, as jython can take a while to initialize
* the communication)
*/
private volatile boolean firstCommWorked = false;
/**
* Initializes the xml-rpc communication.
@@ -54,9 +72,11 @@ public class PydevConsoleCommunication implements IScriptConsoleCommunication, X
* @throws MalformedURLException
*/
public PydevConsoleCommunication(Project project, int port, Process process, int clientPort) throws Exception {
myProject = project;
stdOutReader = new ThreadStreamReader(process.getInputStream());
super(project);
stdErrReader = new ThreadStreamReader(process.getErrorStream());
stdOutReader = new ThreadStreamReader(process.getInputStream());
stdOutReader.start();
stdErrReader.start();
@@ -99,38 +119,11 @@ public class PydevConsoleCommunication implements IScriptConsoleCommunication, X
* adding some line to be executed
*/
/**
* Signals that the next command added should be sent as an input to the server.
*/
public volatile boolean waitingForInput;
/**
* Input that should be sent to the server (waiting for raw_input)
*/
private volatile String inputReceived;
/**
* Response that should be sent back to the shell.
*/
private volatile InterpreterResponse nextResponse;
/**
* Helper to keep on busy loop.
*/
private volatile Object lock = new Object();
/**
* Helper to keep on busy loop.
*/
private volatile Object lock2 = new Object();
/**
* Keeps a flag indicating that we were able to communicate successfully with the shell at least once
* (if we haven't we may retry more than once the first time, as jython can take a while to initialize
* the communication)
*/
private volatile boolean firstCommWorked = false;
/**
* Called when the server is requesting some input from this class.
@@ -165,7 +158,7 @@ public class PydevConsoleCommunication implements IScriptConsoleCommunication, X
* @return a Pair with (null, more) or (error, false)
* @throws XmlRpcException
*/
private Pair<String, Boolean> exec(final String command) throws XmlRpcException {
protected Pair<String, Boolean> exec(final String command) throws XmlRpcException {
Object execute = client.execute("addExec", new Object[]{command});
Object object;
@@ -178,28 +171,35 @@ public class PydevConsoleCommunication implements IScriptConsoleCommunication, X
else {
object = execute;
}
boolean more;
String errorContents = null;
if (object instanceof Boolean) {
more = (Boolean)object;
return new Pair<String, Boolean>(null, (Boolean)object);
}
else {
String str = object.toString();
String lower = str.toLowerCase();
if (lower.equals("true") || lower.equals("1")) {
more = true;
}
else if (lower.equals("false") || lower.equals("0")) {
more = false;
}
else {
more = false;
errorContents = str;
}
return parseExecResponseString(object.toString());
}
return new Pair<String, Boolean>(errorContents, more);
}
/**
* @return completions from the client
*/
public List<PydevCompletionVariant> getCompletions(final String prefix) throws Exception {
if (waitingForInput) {
return Collections.emptyList();
}
final Object fromServer = client.execute("getCompletions", new Object[]{prefix});
return PydevXmlUtils.decodeCompletions(fromServer);
}
/**
* @return the description of the given attribute in the shell
*/
public String getDescription(String text) throws Exception {
if (waitingForInput) {
return "Unable to get description: waiting for input.";
}
return client.execute("getDescription", new Object[]{text}).toString();
}
/**
@@ -318,75 +318,4 @@ public class PydevConsoleCommunication implements IScriptConsoleCommunication, X
}, "Waiting for REPL response", true, myProject);
}
}
/**
* @return completions from the client
*/
public List<PydevCompletionVariant> getCompletions(final String prefix) throws Exception {
if (waitingForInput) {
return Collections.emptyList();
}
final Object fromServer = client.execute("getCompletions", new Object[]{prefix});
final List<PydevCompletionVariant> ret = decodeCompletions(fromServer);
return ret;
}
public static List<PydevCompletionVariant> decodeCompletions(Object fromServer) {
final List<PydevCompletionVariant> ret = new ArrayList<PydevCompletionVariant>();
List complList = objectToList(fromServer);
for (Object o : complList) {
List comp = objectToList(o);
//name, doc, args, type
final int type = extractInt(comp.get(3));
final String args = AbstractPyCodeCompletion.getArgs((String)comp.get(2), type,
AbstractPyCodeCompletion.LOOKING_FOR_INSTANCED_VARIABLE);
ret.add(new PydevCompletionVariant((String)comp.get(0), (String)comp.get(1), args, type));
}
return ret;
}
private static List objectToList(Object object) {
List list;
if (object instanceof Collection) {
list = new ArrayList((Collection)object);
}
else if (object instanceof Object[]) {
list = Arrays.asList((Object[])object);
}
else {
throw new IllegalStateException("cant handle type of " + object);
}
return list;
}
/**
* Extracts an int from an object
*
* @param objToGetInt the object that should be gotten as an int
* @return int with the int the object represents
*/
private static int extractInt(Object objToGetInt) {
if (objToGetInt instanceof Integer) {
return (Integer)objToGetInt;
}
return Integer.parseInt(objToGetInt.toString());
}
/**
* @return the description of the given attribute in the shell
*/
public String getDescription(String text) throws Exception {
if (waitingForInput) {
return "Unable to get description: waiting for input.";
}
return client.execute("getDescription", new Object[]{text}).toString();
}
}

View File

@@ -1,5 +1,7 @@
package com.jetbrains.python.debugger;
import com.jetbrains.python.console.pydev.AbstractPyCodeCompletion;
import com.jetbrains.python.console.pydev.PydevCompletionVariant;
import org.jetbrains.annotations.Nullable;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
@@ -11,6 +13,8 @@ import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/**
@@ -21,6 +25,9 @@ public class PydevXmlUtils {
static SAXParserFactory parserFactory = SAXParserFactory.newInstance();
private PydevXmlUtils() {
}
static SAXParser getSAXParser() throws Exception {
SAXParser parser = null;
@@ -44,6 +51,51 @@ public class PydevXmlUtils {
return null;
}
public static List<PydevCompletionVariant> decodeCompletions(Object fromServer) {
final List<PydevCompletionVariant> ret = new ArrayList<PydevCompletionVariant>();
List completionList = objectToList(fromServer);
for (Object o : completionList) {
List comp = objectToList(o);
//name, doc, args, type
final int type = extractInt(comp.get(3));
final String args = AbstractPyCodeCompletion.getArgs((String)comp.get(2), type,
AbstractPyCodeCompletion.LOOKING_FOR_INSTANCED_VARIABLE);
ret.add(new PydevCompletionVariant((String)comp.get(0), (String)comp.get(1), args, type));
}
return ret;
}
public static List objectToList(Object object) {
List list;
if (object instanceof Collection) {
list = new ArrayList((Collection)object);
}
else if (object instanceof Object[]) {
list = Arrays.asList((Object[])object);
}
else {
throw new IllegalStateException("cant handle type of " + object);
}
return list;
}
/**
* Extracts an int from an object
*
* @param objToGetInt the object that should be gotten as an int
* @return int with the int the object represents
*/
public static int extractInt(Object objToGetInt) {
if (objToGetInt instanceof Integer) {
return (Integer)objToGetInt;
}
return Integer.parseInt(objToGetInt.toString());
}
/**
* Processes CMD_GET_COMPLETIONS return
*/
@@ -76,11 +128,11 @@ public class PydevXmlUtils {
}
public static List<Object[]> xmlToCompletions(String payload) throws Exception {
public static List<PydevCompletionVariant> xmlToCompletions(String payload) throws Exception {
SAXParser parser = getSAXParser();
XMLToCompletionsInfo info = new XMLToCompletionsInfo();
parser.parse(new ByteArrayInputStream(payload.getBytes()), info);
return info.getCompletions();
return decodeCompletions(info.getCompletions());
}
}

View File

@@ -22,6 +22,7 @@ public abstract class AbstractCommand {
public static final int EXECUTE = 115;
public static final int CHANGE_VARIABLE = 117;
public static final int GET_COMPLETIONS = 120;
public static final int CONSOLE_EXEC = 121;
public static final int VERSION = 501;
public static final String NEW_LINE_CHAR = "@_@NEW_LINE_CHAR@_@";
public static final String TAB_CHAR = "@_@TAB_CHAR@_@";

View File

@@ -0,0 +1,37 @@
package com.jetbrains.python.debugger.pydev;
import com.jetbrains.python.debugger.PyDebugValue;
import com.jetbrains.python.debugger.PyDebuggerException;
public class ConsoleExecCommand extends AbstractFrameCommand {
private final String myExpression;
private String myValue = null;
public ConsoleExecCommand(final RemoteDebugger debugger, final String threadId, final String frameId, final String expression) {
super(debugger, CONSOLE_EXEC, threadId, frameId);
myExpression = expression;
}
public String getPayload() {
return new StringBuilder().append(myThreadId).append('\t').append(myFrameId).append('\t').append("FRAME\t")
.append(ProtocolParser.encodeExpression(myExpression)).toString();
}
@Override
public boolean isResponseExpected() {
return true;
}
@Override
protected void processResponse(final ProtocolFrame response) throws PyDebuggerException {
super.processResponse(response);
final PyDebugValue value = ProtocolParser.parseValue(response.getPayload());
myValue = value.getValue();
}
public String getValue() {
return myValue;
}
}

View File

@@ -1,7 +1,6 @@
package com.jetbrains.python.debugger.pydev;
import com.jetbrains.python.console.pydev.PydevCompletionVariant;
import com.jetbrains.python.console.pydev.PydevConsoleCommunication;
import com.jetbrains.python.debugger.PyDebuggerException;
import com.jetbrains.python.debugger.PydevXmlUtils;
import org.jetbrains.annotations.Nullable;
@@ -14,9 +13,6 @@ import java.util.List;
public class GetCompletionsCommand extends AbstractFrameCommand {
private String myActionToken;
private boolean isError = false;
private int responseCode;
private String payload;
private List<PydevCompletionVariant> myCompletions = null;
public GetCompletionsCommand(final RemoteDebugger debugger,
@@ -37,7 +33,7 @@ public class GetCompletionsCommand extends AbstractFrameCommand {
protected void processResponse(ProtocolFrame response) throws PyDebuggerException {
super.processResponse(response);
try {
myCompletions = PydevConsoleCommunication.decodeCompletions(PydevXmlUtils.xmlToCompletions(response.getPayload()));
myCompletions = PydevXmlUtils.xmlToCompletions(response.getPayload());
}
catch (Exception e) {
throw new PyDebuggerException("cant obtain completions", e);

View File

@@ -1,410 +1,418 @@
/*
* Author: atotic
* Created on Mar 23, 2004
* License: Common Public License v1.0
*/
package com.jetbrains.python.debugger.pydev;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
import com.jetbrains.python.debugger.IPyDebugProcess;
import com.jetbrains.python.debugger.PyDebugValue;
import com.jetbrains.python.debugger.PyDebuggerException;
import com.jetbrains.python.debugger.PyThreadInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.nio.charset.Charset;
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class RemoteDebugger {
private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.pydev.remote.RemoteDebugger");
private static final String LOCAL_VERSION = "0.1";
public static final String TEMP_VAR_PREFIX = "__py_debug_temp_var_";
private static final SecureRandom ourRandom = new SecureRandom();
private final IPyDebugProcess myDebugProcess;
private final ServerSocket myServerSocket;
private final int myTimeout;
private Socket mySocket;
private volatile boolean myConnected = false;
private int mySequence = -1;
private final Map<String, PyThreadInfo> myThreads = new ConcurrentHashMap<String, PyThreadInfo>();
private final Map<Integer, ProtocolFrame> myResponseQueue = new HashMap<Integer, ProtocolFrame>();
private final TempVarsHolder myTempVars = new TempVarsHolder();
public RemoteDebugger(final IPyDebugProcess debugProcess, final ServerSocket serverSocket, final int timeout) {
myDebugProcess = debugProcess;
myServerSocket = serverSocket;
myTimeout = timeout * 1000; // to milliseconds
}
public IPyDebugProcess getDebugProcess() {
return myDebugProcess;
}
public boolean isConnected() {
return myConnected;
}
public void waitForConnect() throws Exception {
try {
//noinspection SocketOpenedButNotSafelyClosed
mySocket = myServerSocket.accept();
}
finally {
myServerSocket.close();
}
try {
final DebuggerReader reader = new DebuggerReader();
ApplicationManager.getApplication().executeOnPooledThread(reader);
}
catch (Exception e) {
mySocket.close();
throw e;
}
myConnected = true;
}
public void disconnect() {
myConnected = false;
if (mySocket != null && !mySocket.isClosed()) {
try {
mySocket.close();
}
catch (IOException ignore) {
}
mySocket = null;
}
}
public String handshake() throws PyDebuggerException {
final VersionCommand command = new VersionCommand(this, LOCAL_VERSION);
command.execute();
return command.getRemoteVersion();
}
public PyDebugValue evaluate(final String threadId, final String frameId, final String expression, final boolean execute)
throws PyDebuggerException {
final EvaluateCommand command = new EvaluateCommand(this, threadId, frameId, expression, execute);
command.execute();
return command.getValue();
}
public List<PyDebugValue> loadFrame(final String threadId, final String frameId) throws PyDebuggerException {
final GetFrameCommand command = new GetFrameCommand(this, threadId, frameId);
command.execute();
return command.getVariables();
}
// todo: don't generate temp variables for qualified expressions - just split 'em
public List<PyDebugValue> loadVariable(final String threadId, final String frameId, final PyDebugValue var) throws PyDebuggerException {
setTempVariable(threadId, frameId, var);
final GetVariableCommand command = new GetVariableCommand(this, threadId, frameId, composeName(var), var);
command.execute();
return command.getVariables();
}
public PyDebugValue changeVariable(final String threadId, final String frameId, final PyDebugValue var, final String value)
throws PyDebuggerException {
setTempVariable(threadId, frameId, var);
return doChangeVariable(threadId, frameId, var.getEvaluationExpression(), value);
}
private PyDebugValue doChangeVariable(final String threadId, final String frameId, final String varName, final String value)
throws PyDebuggerException {
final ChangeVariableCommand command = new ChangeVariableCommand(this, threadId, frameId, varName, value);
command.execute();
return command.getNewValue();
}
private static String composeName(final PyDebugValue var) {
final StringBuilder sb = new StringBuilder(var.getTempName());
PyDebugValue p = var;
while ((p = p.getParent()) != null) {
sb.insert(0, '\t').insert(0, p.getTempName());
}
return sb.toString();
}
// todo: change variable in lists doesn't work - either fix in pydevd or format var name appropriately
private void setTempVariable(final String threadId, final String frameId, final PyDebugValue var) {
final PyDebugValue topVar = var.getTopParent();
if (myDebugProcess.isVariable(topVar.getName())) {
return;
}
if (myTempVars.contains(threadId, frameId, topVar.getTempName())) {
return;
}
topVar.setTempName(generateTempName());
try {
doChangeVariable(threadId, frameId, topVar.getTempName(), topVar.getName());
myTempVars.put(threadId, frameId, topVar.getTempName());
}
catch (PyDebuggerException e) {
LOG.error(e);
topVar.setTempName(null);
}
}
private void clearTempVariables(final String threadId) {
final Map<String, Set<String>> threadVars = myTempVars.get(threadId);
if (threadVars == null || threadVars.size() == 0) return;
for (Map.Entry<String, Set<String>> entry : threadVars.entrySet()) {
final Set<String> frameVars = entry.getValue();
if (frameVars == null || frameVars.size() == 0) continue;
final String expression = "del " + StringUtil.join(frameVars, ",");
try {
evaluate(threadId, entry.getKey(), expression, true);
}
catch (PyDebuggerException e) {
LOG.error(e);
}
}
myTempVars.clear(threadId);
}
private static String generateTempName() {
return new StringBuilder(32).append(TEMP_VAR_PREFIX).append(ourRandom.nextInt(Integer.MAX_VALUE)).toString();
}
public Collection<PyThreadInfo> getThreads() {
return Collections.unmodifiableCollection(new ArrayList<PyThreadInfo>(myThreads.values()));
}
int getNextSequence() {
mySequence += 2;
return mySequence;
}
void placeResponse(final int sequence, final ProtocolFrame response) {
synchronized (myResponseQueue) {
if (response == null || myResponseQueue.containsKey(sequence)) {
myResponseQueue.put(sequence, response);
}
if (response != null) {
myResponseQueue.notifyAll();
}
}
}
@Nullable
ProtocolFrame waitForResponse(final int sequence) {
ProtocolFrame response;
long until = System.currentTimeMillis() + myTimeout;
synchronized (myResponseQueue) {
do {
try {
myResponseQueue.wait(1000);
}
catch (InterruptedException ignore) {
}
response = myResponseQueue.get(sequence);
}
while (response == null && System.currentTimeMillis() < until);
myResponseQueue.remove(sequence);
}
return response;
}
public void execute(@NotNull final AbstractCommand command) {
if (command instanceof ResumeCommand) {
final String threadId = ((ResumeCommand)command).getThreadId();
clearTempVariables(threadId);
}
try {
command.execute();
}
catch (PyDebuggerException e) {
LOG.error(e);
}
}
void sendFrame(final ProtocolFrame frame) {
logFrame(frame, true);
try {
final byte[] packed = frame.pack();
final OutputStream os = mySocket.getOutputStream();
os.write(packed);
os.write('\n');
os.flush();
}
catch (IOException e) {
LOG.error(e);
}
}
private static void logFrame(ProtocolFrame frame, boolean out) {
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("%1$tH:%1$tM:%1$tS.%1$tL %2$s %3$s\n", new Date(), (out ? "<<<" : ">>>"), frame));
}
}
public void suspendAllThreads() {
for (PyThreadInfo thread : getThreads()) {
suspendThread(thread.getId());
}
}
public void suspendThread(String threadId) {
final SuspendCommand command = new SuspendCommand(this, threadId);
execute(command);
}
private class DebuggerReader implements Runnable {
private final InputStream myInputStream;
private DebuggerReader() throws IOException {
this.myInputStream = mySocket.getInputStream();
}
public void run() {
final BufferedReader reader = new BufferedReader(new InputStreamReader(myInputStream, Charset.forName("UTF-8")));
try {
String line;
while ((line = reader.readLine()) != null) {
processResponse(line);
}
}
catch (SocketException ignore) {
// disconnected
}
catch (Exception e) {
LOG.error(e);
}
finally {
closeReader(reader);
}
}
private void processResponse(final String line) {
try {
final ProtocolFrame frame = new ProtocolFrame(line);
logFrame(frame, false);
if (AbstractThreadCommand.isThreadCommand(frame.getCommand())) {
processThreadEvent(frame);
}
else {
placeResponse(frame.getSequence(), frame);
}
}
catch (Throwable t) {
// shouldn't interrupt reader thread
LOG.error(t);
}
}
// todo: extract response processing
private void processThreadEvent(ProtocolFrame frame) throws PyDebuggerException {
switch (frame.getCommand()) {
case AbstractCommand.CREATE_THREAD: {
final PyThreadInfo thread = parseThreadEvent(frame);
if (!thread.isPydevThread()) { // ignore pydevd threads
myThreads.put(thread.getId(), thread);
}
break;
}
case AbstractCommand.SUSPEND_THREAD: {
final PyThreadInfo event = parseThreadEvent(frame);
final PyThreadInfo thread = myThreads.get(event.getId());
if (thread != null) {
thread.updateState(PyThreadInfo.State.SUSPENDED, event.getFrames());
myDebugProcess.threadSuspended(thread);
}
break;
}
case AbstractCommand.RESUME_THREAD: {
final String id = ProtocolParser.getThreadId(frame.getPayload());
final PyThreadInfo thread = myThreads.get(id);
if (thread != null) {
thread.updateState(PyThreadInfo.State.RUNNING, null);
myDebugProcess.threadResumed(thread);
}
break;
}
case AbstractCommand.KILL_THREAD: {
final String id = frame.getPayload();
final PyThreadInfo thread = myThreads.get(id);
if (thread != null) {
thread.updateState(PyThreadInfo.State.KILLED, null);
myThreads.remove(id);
}
break;
}
}
}
private PyThreadInfo parseThreadEvent(ProtocolFrame frame) throws PyDebuggerException {
return ProtocolParser.parseThread(frame.getPayload(), myDebugProcess.getPositionConverter());
}
private void closeReader(BufferedReader reader) {
try {
reader.close();
}
catch (IOException ignore) {
}
}
}
private static class TempVarsHolder {
private final Map<String, Map<String, Set<String>>> myData = new HashMap<String, Map<String, Set<String>>>();
public boolean contains(final String threadId, final String frameId, final String name) {
final Map<String, Set<String>> threadVars = myData.get(threadId);
if (threadVars == null) return false;
final Set<String> frameVars = threadVars.get(frameId);
if (frameVars == null) return false;
return frameVars.contains(name);
}
private void put(final String threadId, final String frameId, final String name) {
Map<String, Set<String>> threadVars = myData.get(threadId);
if (threadVars == null) myData.put(threadId, (threadVars = new HashMap<String, Set<String>>()));
Set<String> frameVars = threadVars.get(frameId);
if (frameVars == null) threadVars.put(frameId, (frameVars = new HashSet<String>()));
frameVars.add(name);
}
private Map<String, Set<String>> get(final String threadId) {
return myData.get(threadId);
}
private void clear(final String threadId) {
final Map<String, Set<String>> threadVars = myData.get(threadId);
if (threadVars != null) {
threadVars.clear();
}
}
}
}
/*
* Author: atotic
* Created on Mar 23, 2004
* License: Common Public License v1.0
*/
package com.jetbrains.python.debugger.pydev;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
import com.jetbrains.python.debugger.IPyDebugProcess;
import com.jetbrains.python.debugger.PyDebugValue;
import com.jetbrains.python.debugger.PyDebuggerException;
import com.jetbrains.python.debugger.PyThreadInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.nio.charset.Charset;
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class RemoteDebugger {
private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.pydev.remote.RemoteDebugger");
private static final String LOCAL_VERSION = "0.1";
public static final String TEMP_VAR_PREFIX = "__py_debug_temp_var_";
private static final SecureRandom ourRandom = new SecureRandom();
private final IPyDebugProcess myDebugProcess;
private final ServerSocket myServerSocket;
private final int myTimeout;
private Socket mySocket;
private volatile boolean myConnected = false;
private int mySequence = -1;
private final Map<String, PyThreadInfo> myThreads = new ConcurrentHashMap<String, PyThreadInfo>();
private final Map<Integer, ProtocolFrame> myResponseQueue = new HashMap<Integer, ProtocolFrame>();
private final TempVarsHolder myTempVars = new TempVarsHolder();
public RemoteDebugger(final IPyDebugProcess debugProcess, final ServerSocket serverSocket, final int timeout) {
myDebugProcess = debugProcess;
myServerSocket = serverSocket;
myTimeout = timeout * 1000; // to milliseconds
}
public IPyDebugProcess getDebugProcess() {
return myDebugProcess;
}
public boolean isConnected() {
return myConnected;
}
public void waitForConnect() throws Exception {
try {
//noinspection SocketOpenedButNotSafelyClosed
mySocket = myServerSocket.accept();
}
finally {
myServerSocket.close();
}
try {
final DebuggerReader reader = new DebuggerReader();
ApplicationManager.getApplication().executeOnPooledThread(reader);
}
catch (Exception e) {
mySocket.close();
throw e;
}
myConnected = true;
}
public void disconnect() {
myConnected = false;
if (mySocket != null && !mySocket.isClosed()) {
try {
mySocket.close();
}
catch (IOException ignore) {
}
mySocket = null;
}
}
public String handshake() throws PyDebuggerException {
final VersionCommand command = new VersionCommand(this, LOCAL_VERSION);
command.execute();
return command.getRemoteVersion();
}
public PyDebugValue evaluate(final String threadId, final String frameId, final String expression, final boolean execute)
throws PyDebuggerException {
final EvaluateCommand command = new EvaluateCommand(this, threadId, frameId, expression, execute);
command.execute();
return command.getValue();
}
public String consoleExec(String threadId, String frameId, String expression) throws PyDebuggerException {
final ConsoleExecCommand command = new ConsoleExecCommand(this, threadId, frameId, expression);
command.execute();
return command.getValue();
}
public List<PyDebugValue> loadFrame(final String threadId, final String frameId) throws PyDebuggerException {
final GetFrameCommand command = new GetFrameCommand(this, threadId, frameId);
command.execute();
return command.getVariables();
}
// todo: don't generate temp variables for qualified expressions - just split 'em
public List<PyDebugValue> loadVariable(final String threadId, final String frameId, final PyDebugValue var) throws PyDebuggerException {
setTempVariable(threadId, frameId, var);
final GetVariableCommand command = new GetVariableCommand(this, threadId, frameId, composeName(var), var);
command.execute();
return command.getVariables();
}
public PyDebugValue changeVariable(final String threadId, final String frameId, final PyDebugValue var, final String value)
throws PyDebuggerException {
setTempVariable(threadId, frameId, var);
return doChangeVariable(threadId, frameId, var.getEvaluationExpression(), value);
}
private PyDebugValue doChangeVariable(final String threadId, final String frameId, final String varName, final String value)
throws PyDebuggerException {
final ChangeVariableCommand command = new ChangeVariableCommand(this, threadId, frameId, varName, value);
command.execute();
return command.getNewValue();
}
private static String composeName(final PyDebugValue var) {
final StringBuilder sb = new StringBuilder(var.getTempName());
PyDebugValue p = var;
while ((p = p.getParent()) != null) {
sb.insert(0, '\t').insert(0, p.getTempName());
}
return sb.toString();
}
// todo: change variable in lists doesn't work - either fix in pydevd or format var name appropriately
private void setTempVariable(final String threadId, final String frameId, final PyDebugValue var) {
final PyDebugValue topVar = var.getTopParent();
if (myDebugProcess.isVariable(topVar.getName())) {
return;
}
if (myTempVars.contains(threadId, frameId, topVar.getTempName())) {
return;
}
topVar.setTempName(generateTempName());
try {
doChangeVariable(threadId, frameId, topVar.getTempName(), topVar.getName());
myTempVars.put(threadId, frameId, topVar.getTempName());
}
catch (PyDebuggerException e) {
LOG.error(e);
topVar.setTempName(null);
}
}
private void clearTempVariables(final String threadId) {
final Map<String, Set<String>> threadVars = myTempVars.get(threadId);
if (threadVars == null || threadVars.size() == 0) return;
for (Map.Entry<String, Set<String>> entry : threadVars.entrySet()) {
final Set<String> frameVars = entry.getValue();
if (frameVars == null || frameVars.size() == 0) continue;
final String expression = "del " + StringUtil.join(frameVars, ",");
try {
evaluate(threadId, entry.getKey(), expression, true);
}
catch (PyDebuggerException e) {
LOG.error(e);
}
}
myTempVars.clear(threadId);
}
private static String generateTempName() {
return new StringBuilder(32).append(TEMP_VAR_PREFIX).append(ourRandom.nextInt(Integer.MAX_VALUE)).toString();
}
public Collection<PyThreadInfo> getThreads() {
return Collections.unmodifiableCollection(new ArrayList<PyThreadInfo>(myThreads.values()));
}
int getNextSequence() {
mySequence += 2;
return mySequence;
}
void placeResponse(final int sequence, final ProtocolFrame response) {
synchronized (myResponseQueue) {
if (response == null || myResponseQueue.containsKey(sequence)) {
myResponseQueue.put(sequence, response);
}
if (response != null) {
myResponseQueue.notifyAll();
}
}
}
@Nullable
ProtocolFrame waitForResponse(final int sequence) {
ProtocolFrame response;
long until = System.currentTimeMillis() + myTimeout;
synchronized (myResponseQueue) {
do {
try {
myResponseQueue.wait(1000);
}
catch (InterruptedException ignore) {
}
response = myResponseQueue.get(sequence);
}
while (response == null && System.currentTimeMillis() < until);
myResponseQueue.remove(sequence);
}
return response;
}
public void execute(@NotNull final AbstractCommand command) {
if (command instanceof ResumeCommand) {
final String threadId = ((ResumeCommand)command).getThreadId();
clearTempVariables(threadId);
}
try {
command.execute();
}
catch (PyDebuggerException e) {
LOG.error(e);
}
}
void sendFrame(final ProtocolFrame frame) {
logFrame(frame, true);
try {
final byte[] packed = frame.pack();
final OutputStream os = mySocket.getOutputStream();
os.write(packed);
os.write('\n');
os.flush();
}
catch (IOException e) {
LOG.error(e);
}
}
private static void logFrame(ProtocolFrame frame, boolean out) {
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("%1$tH:%1$tM:%1$tS.%1$tL %2$s %3$s\n", new Date(), (out ? "<<<" : ">>>"), frame));
}
}
public void suspendAllThreads() {
for (PyThreadInfo thread : getThreads()) {
suspendThread(thread.getId());
}
}
public void suspendThread(String threadId) {
final SuspendCommand command = new SuspendCommand(this, threadId);
execute(command);
}
private class DebuggerReader implements Runnable {
private final InputStream myInputStream;
private DebuggerReader() throws IOException {
this.myInputStream = mySocket.getInputStream();
}
public void run() {
final BufferedReader reader = new BufferedReader(new InputStreamReader(myInputStream, Charset.forName("UTF-8")));
try {
String line;
while ((line = reader.readLine()) != null) {
processResponse(line);
}
}
catch (SocketException ignore) {
// disconnected
}
catch (Exception e) {
LOG.error(e);
}
finally {
closeReader(reader);
}
}
private void processResponse(final String line) {
try {
final ProtocolFrame frame = new ProtocolFrame(line);
logFrame(frame, false);
if (AbstractThreadCommand.isThreadCommand(frame.getCommand())) {
processThreadEvent(frame);
}
else {
placeResponse(frame.getSequence(), frame);
}
}
catch (Throwable t) {
// shouldn't interrupt reader thread
LOG.error(t);
}
}
// todo: extract response processing
private void processThreadEvent(ProtocolFrame frame) throws PyDebuggerException {
switch (frame.getCommand()) {
case AbstractCommand.CREATE_THREAD: {
final PyThreadInfo thread = parseThreadEvent(frame);
if (!thread.isPydevThread()) { // ignore pydevd threads
myThreads.put(thread.getId(), thread);
}
break;
}
case AbstractCommand.SUSPEND_THREAD: {
final PyThreadInfo event = parseThreadEvent(frame);
final PyThreadInfo thread = myThreads.get(event.getId());
if (thread != null) {
thread.updateState(PyThreadInfo.State.SUSPENDED, event.getFrames());
myDebugProcess.threadSuspended(thread);
}
break;
}
case AbstractCommand.RESUME_THREAD: {
final String id = ProtocolParser.getThreadId(frame.getPayload());
final PyThreadInfo thread = myThreads.get(id);
if (thread != null) {
thread.updateState(PyThreadInfo.State.RUNNING, null);
myDebugProcess.threadResumed(thread);
}
break;
}
case AbstractCommand.KILL_THREAD: {
final String id = frame.getPayload();
final PyThreadInfo thread = myThreads.get(id);
if (thread != null) {
thread.updateState(PyThreadInfo.State.KILLED, null);
myThreads.remove(id);
}
break;
}
}
}
private PyThreadInfo parseThreadEvent(ProtocolFrame frame) throws PyDebuggerException {
return ProtocolParser.parseThread(frame.getPayload(), myDebugProcess.getPositionConverter());
}
private void closeReader(BufferedReader reader) {
try {
reader.close();
}
catch (IOException ignore) {
}
}
}
private static class TempVarsHolder {
private final Map<String, Map<String, Set<String>>> myData = new HashMap<String, Map<String, Set<String>>>();
public boolean contains(final String threadId, final String frameId, final String name) {
final Map<String, Set<String>> threadVars = myData.get(threadId);
if (threadVars == null) return false;
final Set<String> frameVars = threadVars.get(frameId);
if (frameVars == null) return false;
return frameVars.contains(name);
}
private void put(final String threadId, final String frameId, final String name) {
Map<String, Set<String>> threadVars = myData.get(threadId);
if (threadVars == null) myData.put(threadId, (threadVars = new HashMap<String, Set<String>>()));
Set<String> frameVars = threadVars.get(frameId);
if (frameVars == null) threadVars.put(frameId, (frameVars = new HashSet<String>()));
frameVars.add(name);
}
private Map<String, Set<String>> get(final String threadId) {
return myData.get(threadId);
}
private void clear(final String threadId) {
final Map<String, Set<String>> threadVars = myData.get(threadId);
if (threadVars != null) {
threadVars.clear();
}
}
}
}

View File

@@ -1,13 +0,0 @@
package com.jetbrains.python.console;
import com.jetbrains.python.console.pydev.ICallback;
import com.jetbrains.python.console.pydev.InterpreterResponse;
/**
* @author traff
*/
public interface ConsoleCommandExecutor {
boolean isWaitingForInput();
void execInterpreter(String s, ICallback<Object, InterpreterResponse> callback);
}

View File

@@ -1,59 +1,58 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.console;
import com.google.common.collect.Lists;
import com.intellij.execution.filters.Filter;
import com.intellij.execution.filters.TextConsoleBuilder;
import com.intellij.execution.impl.ConsoleViewImpl;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.openapi.project.Project;
import java.util.ArrayList;
/**
* @author traff
*/
public class PyDebugConsoleBuilder extends TextConsoleBuilder {
private final Project myProject;
private final ArrayList<Filter> myFilters = Lists.newArrayList();
public PyDebugConsoleBuilder(final Project project) {
myProject = project;
}
public ConsoleView getConsole() {
final ConsoleView consoleView = createConsole();
for (final Filter filter : myFilters) {
consoleView.addMessageFilter(filter);
}
return consoleView;
}
protected ConsoleViewImpl createConsole() {
return new PydevLanguageConsoleView(myProject, "");
}
public void addFilter(final Filter filter) {
myFilters.add(filter);
}
@Override
public void setViewer(boolean isViewer) {
}
}
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.console;
import com.google.common.collect.Lists;
import com.intellij.execution.filters.Filter;
import com.intellij.execution.filters.TextConsoleBuilder;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.openapi.project.Project;
import java.util.ArrayList;
/**
* @author traff
*/
public class PyDebugConsoleBuilder extends TextConsoleBuilder {
private final Project myProject;
private final ArrayList<Filter> myFilters = Lists.newArrayList();
public PyDebugConsoleBuilder(final Project project) {
myProject = project;
}
public ConsoleView getConsole() {
final ConsoleView consoleView = createConsole();
for (final Filter filter : myFilters) {
consoleView.addMessageFilter(filter);
}
return consoleView;
}
protected ConsoleView createConsole() {
return new PythonDebugLanguageConsoleView(myProject);
}
public void addFilter(final Filter filter) {
myFilters.add(filter);
}
@Override
public void setViewer(boolean isViewer) {
}
}

View File

@@ -1,19 +1,28 @@
package com.jetbrains.python.console;
import com.jetbrains.python.console.pydev.ConsoleCommunication;
import com.jetbrains.python.console.pydev.PydevCompletionVariant;
import com.intellij.execution.impl.ConsoleViewImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.jetbrains.python.console.pydev.*;
import com.jetbrains.python.debugger.PyDebugProcess;
import com.jetbrains.python.debugger.PyDebuggerException;
import java.util.List;
/**
* @author traff
*/
public class PyDebugConsoleCommunication implements ConsoleCommunication {
public class PyDebugConsoleCommunication extends AbstractConsoleCommunication {
private final PyDebugProcess myDebugProcess;
private final ConsoleViewImpl myTextConsoleView;
public PyDebugConsoleCommunication(PyDebugProcess debugProcess) {
private final StringBuilder myExpression = new StringBuilder();
public PyDebugConsoleCommunication(Project project, PyDebugProcess debugProcess, ConsoleViewImpl textConsoleView) {
super(project);
myDebugProcess = debugProcess;
myTextConsoleView = textConsoleView;
}
@Override
@@ -25,4 +34,37 @@ public class PyDebugConsoleCommunication implements ConsoleCommunication {
public String getDescription(String text) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public boolean isWaitingForInput() {
return false; //To change body of implemented methods use File | Settings | File Templates.
}
protected Pair<String, Boolean> exec(final String command) throws PyDebuggerException {
String value = myDebugProcess.consoleExec(command);
return parseExecResponseString(value);
}
public void execInterpreter(
String s,
ICallback<Object, InterpreterResponse> callback) {
try {
myExpression.append(s);
Pair<String, Boolean> executed = exec(myExpression.toString());
String errorContents = executed.first;
if ("None".equals(errorContents)) {
errorContents = null;
}
boolean more = executed.second;
if (!more) {
myExpression.setLength(0);
}
callback.call(new InterpreterResponse("", errorContents, more, isWaitingForInput()));
}
catch (PyDebuggerException e) {
callback.call(new InterpreterResponse(null, "", false, isWaitingForInput()));
}
}
}

View File

@@ -1,179 +1,180 @@
package com.jetbrains.python.console;
import com.intellij.execution.console.LanguageConsoleImpl;
import com.intellij.execution.console.LanguageConsoleViewImpl;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.process.ProcessOutputTypes;
import com.intellij.execution.runners.ConsoleExecuteActionHandler;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorModificationUtil;
import com.intellij.openapi.fileTypes.PlainTextLanguage;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.impl.source.codeStyle.HelperFactory;
import com.intellij.psi.impl.source.codeStyle.IndentHelper;
import com.jetbrains.python.PythonFileType;
import com.jetbrains.python.PythonLanguage;
import com.jetbrains.python.console.pydev.ICallback;
import com.jetbrains.python.console.pydev.InterpreterResponse;
/**
* @author traff
*/
public class PydevConsoleExecuteActionHandler extends ConsoleExecuteActionHandler {
private static final String DOUBLE_QUOTE_MULTILINE = "\"\"\"";
private static final String SINGLE_QUOTE_MULTILINE = "'''";
private final LanguageConsoleViewImpl myConsoleView;
private String myInMultilineStringState = null;
private StringBuilder myInputBuffer;
private int myCurrentIndentSize = -1;
private IndentHelper myIndentHelper;
private final ConsoleCommandExecutor myConsoleCommandExecutor;
public PydevConsoleExecuteActionHandler(LanguageConsoleViewImpl consoleView,
ProcessHandler myProcessHandler,
ConsoleCommandExecutor consoleCommandExecutor) {
super(myProcessHandler);
myConsoleView = consoleView;
myConsoleCommandExecutor = consoleCommandExecutor;
myIndentHelper = HelperFactory.createHelper(PythonFileType.INSTANCE, consoleView.getConsole().getProject());
}
@Override
public void processLine(final String line) {
final LanguageConsoleImpl console = myConsoleView.getConsole();
final Editor currentEditor = console.getCurrentEditor();
if (myInputBuffer == null) {
myInputBuffer = new StringBuilder();
}
myInputBuffer.append(line).append("\n");
// multiline strings handling
if (myInMultilineStringState != null) {
if (line.contains(myInMultilineStringState)) {
myInMultilineStringState = null;
// restore language
console.setLanguage(PythonLanguage.getInstance());
console.setPrompt(PyConsoleHighlightingUtil.ORDINARY_PROMPT);
}
else {
return;
}
}
else {
if (line.contains(DOUBLE_QUOTE_MULTILINE)) {
myInMultilineStringState = DOUBLE_QUOTE_MULTILINE;
}
else if (line.contains(SINGLE_QUOTE_MULTILINE)) {
myInMultilineStringState = SINGLE_QUOTE_MULTILINE;
}
if (myInMultilineStringState != null) {
// change language
console.setLanguage(PlainTextLanguage.INSTANCE);
console.setPrompt(PyConsoleHighlightingUtil.INDENT_PROMPT);
return;
}
}
// Process line continuation
if (line.endsWith("\\")) {
console.setPrompt(PyConsoleHighlightingUtil.INDENT_PROMPT);
return;
}
if (myCurrentIndentSize != -1) {
final int indent = myIndentHelper.getIndent(line, false);
if (indent >= myCurrentIndentSize) {
indentEditor(currentEditor, indent);
scrollDown(currentEditor);
return;
}
}
if (myConsoleCommandExecutor != null) {
final boolean waitedForInputBefore = myConsoleCommandExecutor.isWaitingForInput();
myConsoleCommandExecutor.execInterpreter(myInputBuffer.toString(), new ICallback<Object, InterpreterResponse>() {
public Object call(final InterpreterResponse interpreterResponse) {
// clear
myInputBuffer = null;
// Handle prompt
if (interpreterResponse.need_input) {
if (!PyConsoleHighlightingUtil.INPUT_PROMPT.equals(console.getPrompt())) {
console.setPrompt(PyConsoleHighlightingUtil.INPUT_PROMPT);
scrollDown(currentEditor);
}
myCurrentIndentSize = -1;
}
else if (interpreterResponse.more) {
if (!PyConsoleHighlightingUtil.INDENT_PROMPT.equals(console.getPrompt())) {
console.setPrompt(PyConsoleHighlightingUtil.INDENT_PROMPT);
scrollDown(currentEditor);
}
if (myCurrentIndentSize == -1) {
// compute current indentation
myCurrentIndentSize = myIndentHelper.getIndent(line, false) + getPythonIndent();
// In this case we can insert indent automatically
indentEditor(currentEditor, myCurrentIndentSize);
}
}
else {
if (!PyConsoleHighlightingUtil.ORDINARY_PROMPT.equals(console.getPrompt())) {
console.setPrompt(PyConsoleHighlightingUtil.ORDINARY_PROMPT);
scrollDown(currentEditor);
}
myCurrentIndentSize = -1;
}
// Handle output
if (!StringUtil.isEmpty(interpreterResponse.err)) {
PyConsoleHighlightingUtil.processOutput(console, interpreterResponse.err, ProcessOutputTypes.STDERR);
}
else if (!StringUtil.isEmpty(interpreterResponse.out)) {
PyConsoleHighlightingUtil.processOutput(console, interpreterResponse.out, ProcessOutputTypes.STDOUT);
}
scrollDown(currentEditor);
return null;
}
});
// After requesting input we got no call back to change prompt, change it manually
if (waitedForInputBefore && !myConsoleCommandExecutor.isWaitingForInput()) {
console.setPrompt(PyConsoleHighlightingUtil.ORDINARY_PROMPT);
scrollDown(currentEditor);
}
}
}
private int getPythonIndent() {
return CodeStyleSettingsManager.getSettings(getProject()).getIndentSize(PythonFileType.INSTANCE);
}
private void indentEditor(final Editor editor, final int indentSize) {
new WriteCommandAction(getProject()) {
@Override
protected void run(Result result) throws Throwable {
EditorModificationUtil.insertStringAtCaret(editor, myIndentHelper.fillIndent(indentSize));
}
}.execute();
}
private Project getProject() {
return myConsoleView.getConsole().getProject();
}
private void scrollDown(final Editor currentEditor) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
currentEditor.getCaretModel().moveToOffset(currentEditor.getDocument().getTextLength());
}
});
}
}
package com.jetbrains.python.console;
import com.intellij.execution.console.LanguageConsoleImpl;
import com.intellij.execution.console.LanguageConsoleViewImpl;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.process.ProcessOutputTypes;
import com.intellij.execution.runners.ConsoleExecuteActionHandler;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorModificationUtil;
import com.intellij.openapi.fileTypes.PlainTextLanguage;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.impl.source.codeStyle.HelperFactory;
import com.intellij.psi.impl.source.codeStyle.IndentHelper;
import com.jetbrains.python.PythonFileType;
import com.jetbrains.python.PythonLanguage;
import com.jetbrains.python.console.pydev.ConsoleCommunication;
import com.jetbrains.python.console.pydev.ICallback;
import com.jetbrains.python.console.pydev.InterpreterResponse;
/**
* @author traff
*/
public class PydevConsoleExecuteActionHandler extends ConsoleExecuteActionHandler {
private static final String DOUBLE_QUOTE_MULTILINE = "\"\"\"";
private static final String SINGLE_QUOTE_MULTILINE = "'''";
private final LanguageConsoleViewImpl myConsoleView;
private String myInMultilineStringState = null;
private StringBuilder myInputBuffer;
private int myCurrentIndentSize = -1;
private IndentHelper myIndentHelper;
private final ConsoleCommunication myConsoleCommunication;
public PydevConsoleExecuteActionHandler(LanguageConsoleViewImpl consoleView,
ProcessHandler myProcessHandler,
ConsoleCommunication consoleCommunication) {
super(myProcessHandler);
myConsoleView = consoleView;
myConsoleCommunication = consoleCommunication;
myIndentHelper = HelperFactory.createHelper(PythonFileType.INSTANCE, consoleView.getConsole().getProject());
}
@Override
public void processLine(final String line) {
final LanguageConsoleImpl console = myConsoleView.getConsole();
final Editor currentEditor = console.getCurrentEditor();
if (myInputBuffer == null) {
myInputBuffer = new StringBuilder();
}
myInputBuffer.append(line).append("\n");
// multiline strings handling
if (myInMultilineStringState != null) {
if (line.contains(myInMultilineStringState)) {
myInMultilineStringState = null;
// restore language
console.setLanguage(PythonLanguage.getInstance());
console.setPrompt(PyConsoleHighlightingUtil.ORDINARY_PROMPT);
}
else {
return;
}
}
else {
if (line.contains(DOUBLE_QUOTE_MULTILINE)) {
myInMultilineStringState = DOUBLE_QUOTE_MULTILINE;
}
else if (line.contains(SINGLE_QUOTE_MULTILINE)) {
myInMultilineStringState = SINGLE_QUOTE_MULTILINE;
}
if (myInMultilineStringState != null) {
// change language
console.setLanguage(PlainTextLanguage.INSTANCE);
console.setPrompt(PyConsoleHighlightingUtil.INDENT_PROMPT);
return;
}
}
// Process line continuation
if (line.endsWith("\\")) {
console.setPrompt(PyConsoleHighlightingUtil.INDENT_PROMPT);
return;
}
if (myCurrentIndentSize != -1) {
final int indent = myIndentHelper.getIndent(line, false);
if (indent >= myCurrentIndentSize) {
indentEditor(currentEditor, indent);
scrollDown(currentEditor);
return;
}
}
if (myConsoleCommunication != null) {
final boolean waitedForInputBefore = myConsoleCommunication.isWaitingForInput();
myConsoleCommunication.execInterpreter(myInputBuffer.toString(), new ICallback<Object, InterpreterResponse>() {
public Object call(final InterpreterResponse interpreterResponse) {
// clear
myInputBuffer = null;
// Handle prompt
if (interpreterResponse.need_input) {
if (!PyConsoleHighlightingUtil.INPUT_PROMPT.equals(console.getPrompt())) {
console.setPrompt(PyConsoleHighlightingUtil.INPUT_PROMPT);
scrollDown(currentEditor);
}
myCurrentIndentSize = -1;
}
else if (interpreterResponse.more) {
if (!PyConsoleHighlightingUtil.INDENT_PROMPT.equals(console.getPrompt())) {
console.setPrompt(PyConsoleHighlightingUtil.INDENT_PROMPT);
scrollDown(currentEditor);
}
if (myCurrentIndentSize == -1) {
// compute current indentation
myCurrentIndentSize = myIndentHelper.getIndent(line, false) + getPythonIndent();
// In this case we can insert indent automatically
indentEditor(currentEditor, myCurrentIndentSize);
}
}
else {
if (!PyConsoleHighlightingUtil.ORDINARY_PROMPT.equals(console.getPrompt())) {
console.setPrompt(PyConsoleHighlightingUtil.ORDINARY_PROMPT);
scrollDown(currentEditor);
}
myCurrentIndentSize = -1;
}
// Handle output
if (!StringUtil.isEmpty(interpreterResponse.err)) {
PyConsoleHighlightingUtil.processOutput(console, interpreterResponse.err, ProcessOutputTypes.STDERR);
}
else if (!StringUtil.isEmpty(interpreterResponse.out)) {
PyConsoleHighlightingUtil.processOutput(console, interpreterResponse.out, ProcessOutputTypes.STDOUT);
}
scrollDown(currentEditor);
return null;
}
});
// After requesting input we got no call back to change prompt, change it manually
if (waitedForInputBefore && !myConsoleCommunication.isWaitingForInput()) {
console.setPrompt(PyConsoleHighlightingUtil.ORDINARY_PROMPT);
scrollDown(currentEditor);
}
}
}
private int getPythonIndent() {
return CodeStyleSettingsManager.getSettings(getProject()).getIndentSize(PythonFileType.INSTANCE);
}
private void indentEditor(final Editor editor, final int indentSize) {
new WriteCommandAction(getProject()) {
@Override
protected void run(Result result) throws Throwable {
EditorModificationUtil.insertStringAtCaret(editor, myIndentHelper.fillIndent(indentSize));
}
}.execute();
}
private Project getProject() {
return myConsoleView.getConsole().getProject();
}
private void scrollDown(final Editor currentEditor) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
currentEditor.getCaretModel().moveToOffset(currentEditor.getDocument().getTextLength());
}
});
}
}

View File

@@ -21,8 +21,6 @@ import com.intellij.util.net.NetUtils;
import com.jetbrains.django.run.Runner;
import com.jetbrains.python.PythonHelpersLocator;
import com.jetbrains.python.console.pydev.ConsoleCommunication;
import com.jetbrains.python.console.pydev.ICallback;
import com.jetbrains.python.console.pydev.InterpreterResponse;
import com.jetbrains.python.console.pydev.PydevConsoleCommunication;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -181,17 +179,7 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory {
@NotNull
@Override
protected ConsoleExecuteActionHandler createConsoleExecuteActionHandler() {
return new PydevConsoleExecuteActionHandler(getConsoleView(), getProcessHandler(), new ConsoleCommandExecutor() {
@Override
public boolean isWaitingForInput() {
return myPydevConsoleCommunication.waitingForInput;
}
@Override
public void execInterpreter(String s, ICallback<Object, InterpreterResponse> callback) {
myPydevConsoleCommunication.execInterpreter(s, callback);
}
});
return new PydevConsoleExecuteActionHandler(getConsoleView(), getProcessHandler(), myPydevConsoleCommunication);
}

View File

@@ -0,0 +1,211 @@
package com.jetbrains.python.console;
import com.google.common.collect.Lists;
import com.intellij.execution.ExecutionBundle;
import com.intellij.execution.filters.Filter;
import com.intellij.execution.filters.HyperlinkInfo;
import com.intellij.execution.filters.TextConsoleBuilderFactory;
import com.intellij.execution.impl.ConsoleViewImpl;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.execution.ui.ObservableConsoleView;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.ToggleAction;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.IconLoader;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.util.List;
/**
* @author traff
*/
public class PythonDebugLanguageConsoleView extends JPanel implements ConsoleView, ObservableConsoleView {
private final static String TEXT_CONSOLE_PANEL = "TEXT_CONSOLE_PANEL";
private final static String PYDEV_CONSOLE_PANEL = "PYDEV_CONSOLE_PANEL";
private final PydevLanguageConsoleView myPydevConsoleView;
private final ConsoleViewImpl myTextConsole;
private Project myProject;
public boolean myIsDebugConsole = false;
public PythonDebugLanguageConsoleView(final Project project) {
super(new CardLayout());
myProject = project;
myPydevConsoleView = new PydevLanguageConsoleView(myProject, "");
myTextConsole = (ConsoleViewImpl)TextConsoleBuilderFactory.getInstance().createBuilder(myProject).getConsole();
add(myTextConsole.getComponent(), TEXT_CONSOLE_PANEL);
add(myPydevConsoleView.getComponent(), PYDEV_CONSOLE_PANEL);
showDebugConsole(false);
}
private void doShowConsole(String type) {
CardLayout cl = (CardLayout)(getLayout());
cl.show(this, type);
}
public boolean isDebugConsole() {
return myIsDebugConsole;
}
public void showDebugConsole(boolean flag) {
if (flag) {
doShowConsole(PYDEV_CONSOLE_PANEL);
}
else {
doShowConsole(TEXT_CONSOLE_PANEL);
}
myIsDebugConsole = flag;
}
public PydevLanguageConsoleView getPydevConsoleView() {
return myPydevConsoleView;
}
public ConsoleViewImpl getTextConsole() {
return myTextConsole;
}
@Override
public JComponent getComponent() {
return this;
}
@Override
public JComponent getPreferredFocusableComponent() {
return this;
}
@Override
public void dispose() {
}
@Override
public void print(String s, ConsoleViewContentType contentType) {
myPydevConsoleView.print(s, contentType);
myTextConsole.print(s, contentType);
}
@Override
public void clear() {
myPydevConsoleView.clear();
myTextConsole.clear();
}
@Override
public void scrollTo(int offset) {
myPydevConsoleView.getConsole().getHistoryViewer().getCaretModel().moveToOffset(offset);
myPydevConsoleView.getConsole().getHistoryViewer().getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
myTextConsole.scrollTo(offset);
}
@Override
public void attachToProcess(ProcessHandler processHandler) {
myPydevConsoleView.attachToProcess(processHandler);
myTextConsole.attachToProcess(processHandler);
}
@Override
public void setOutputPaused(boolean value) {
myPydevConsoleView.setOutputPaused(value);
myTextConsole.setOutputPaused(value);
}
@Override
public boolean isOutputPaused() {
return false;
}
@Override
public boolean hasDeferredOutput() {
return myPydevConsoleView.hasDeferredOutput() && myTextConsole.hasDeferredOutput();
}
@Override
public void performWhenNoDeferredOutput(Runnable runnable) {
}
@Override
public void setHelpId(String helpId) {
myPydevConsoleView.setHelpId(helpId);
myTextConsole.setHelpId(helpId);
}
@Override
public void addMessageFilter(Filter filter) {
myPydevConsoleView.addMessageFilter(filter);
myTextConsole.addMessageFilter(filter);
}
@Override
public void printHyperlink(String hyperlinkText, HyperlinkInfo info) {
myPydevConsoleView.printHyperlink(hyperlinkText, info);
myTextConsole.printHyperlink(hyperlinkText, info);
}
@Override
public int getContentSize() {
return myTextConsole.getContentSize();
}
@Override
public boolean canPause() {
return false;
}
@NotNull
@Override
public AnAction[] createConsoleActions() {
List<AnAction> actions = Lists.newArrayList(myTextConsole.createConsoleActions());
actions.add(new ShowDebugConsoleAction(this));
return actions.toArray(new AnAction[actions.size()]);
}
@Override
public void addChangeListener(ChangeListener listener, Disposable parent) {
myPydevConsoleView.addChangeListener(listener, parent);
myTextConsole.addChangeListener(listener, parent);
}
private static class ShowDebugConsoleAction extends ToggleAction implements DumbAware {
private final PythonDebugLanguageConsoleView myConsole;
public ShowDebugConsoleAction(final PythonDebugLanguageConsoleView console) {
super(ExecutionBundle.message("run.configuration.show.command.line.action.name"), null, IconLoader.getIcon("/actions/pause.png"));
myConsole = console;
}
public boolean isSelected(final AnActionEvent event) {
return myConsole.isDebugConsole();
}
public void setSelected(final AnActionEvent event, final boolean flag) {
myConsole.showDebugConsole(flag);
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
update(event);
}
});
}
}
}

View File

@@ -185,6 +185,12 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess {
return myDebugger.evaluate(frame.getThreadId(), frame.getFrameId(), expression, execute);
}
public String consoleExec(String command) throws PyDebuggerException {
dropFrameCaches();
final PyStackFrame frame = currentFrame();
return myDebugger.consoleExec(frame.getThreadId(), frame.getFrameId(), command);
}
public List<PyDebugValue> loadFrame() throws PyDebuggerException {
final PyStackFrame frame = currentFrame();
//do not reload frame every time it is needed, because due to bug in pdb, reloading frame clears all variable changes
@@ -319,4 +325,5 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess {
}
return null;
}
}

View File

@@ -4,7 +4,6 @@ import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionResult;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.*;
import com.intellij.execution.console.LanguageConsoleViewImpl;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.runners.AbstractConsoleRunnerWithHistory;
@@ -23,6 +22,7 @@ import com.jetbrains.python.PythonHelpersLocator;
import com.jetbrains.python.console.PyDebugConsoleCommunication;
import com.jetbrains.python.console.PydevConsoleExecuteActionHandler;
import com.jetbrains.python.console.PydevLanguageConsoleView;
import com.jetbrains.python.console.PythonDebugLanguageConsoleView;
import com.jetbrains.python.console.pydev.ConsoleCommunication;
import com.jetbrains.python.run.AbstractPythonRunConfiguration;
import com.jetbrains.python.run.CommandLinePatcher;
@@ -51,7 +51,7 @@ public class PyDebugRunner extends GenericProgramRunner {
return DefaultDebugExecutor.EXECUTOR_ID.equals(executorId) && profile instanceof AbstractPythonRunConfiguration;
}
protected RunContentDescriptor doExecute(Project project, Executor executor, RunProfileState state,
protected RunContentDescriptor doExecute(final Project project, Executor executor, RunProfileState profileState,
RunContentDescriptor contentToReuse,
ExecutionEnvironment env) throws ExecutionException {
FileDocumentManager.getInstance().saveAllDocuments();
@@ -65,7 +65,7 @@ public class PyDebugRunner extends GenericProgramRunner {
throw new ExecutionException("Failed to find free socket port", e);
}
final PythonCommandLineState pyState = (PythonCommandLineState)state;
final PythonCommandLineState pyState = (PythonCommandLineState)profileState;
final int serverLocalPort = serverSocket.getLocalPort();
RunProfile profile = env.getRunProfile();
final ExecutionResult result = pyState.execute(executor, createCommandLinePatchers(pyState, profile, serverLocalPort));
@@ -77,9 +77,7 @@ public class PyDebugRunner extends GenericProgramRunner {
PyDebugProcess pyDebugProcess =
new PyDebugProcess(session, serverSocket, result.getExecutionConsole(), result.getProcessHandler());
registerConsoleEvaluateActions(result, pyDebugProcess);
setDebugConsoleCommunication(result, pyDebugProcess);
createConsoleCommunicationAndSetupActions(project, result, pyDebugProcess);
return pyDebugProcess;
}
@@ -87,28 +85,27 @@ public class PyDebugRunner extends GenericProgramRunner {
return session.getRunContentDescriptor();
}
private static void registerConsoleEvaluateActions(final ExecutionResult result, PyDebugProcess pyDebugProcess) {
private static void createConsoleCommunicationAndSetupActions(@NotNull final Project project, @NotNull final ExecutionResult result, @NotNull PyDebugProcess debugProcess) {
ExecutionConsole console = result.getExecutionConsole();
ProcessHandler processHandler = result.getProcessHandler();
if (console instanceof LanguageConsoleViewImpl) {
LanguageConsoleViewImpl consoleView = (LanguageConsoleViewImpl)console;
PythonCommandLineState.PyDebugProcessConsoleCommandExecutor consoleCommandExecutor = new PythonCommandLineState.PyDebugProcessConsoleCommandExecutor(pyDebugProcess);
List<AnAction> actions = AbstractConsoleRunnerWithHistory
.createConsoleExecActions(consoleView.getConsole(), processHandler, new PydevConsoleExecuteActionHandler(consoleView,
processHandler,
consoleCommandExecutor))
.getActionsAsList();
AbstractConsoleRunnerWithHistory.registerActionShortcuts(actions.toArray(new AnAction[actions.size()]), consoleView.getComponent());
}
}
if (console instanceof PythonDebugLanguageConsoleView) {
PydevLanguageConsoleView pydevConsoleView = ((PythonDebugLanguageConsoleView)console).getPydevConsoleView();
//consoleView.getConsole().setFullEditorMode(true);
private static void setDebugConsoleCommunication(final ExecutionResult result, final PyDebugProcess debugProcess) {
ExecutionConsole console = result.getExecutionConsole();
if (console instanceof PydevLanguageConsoleView) {
PydevLanguageConsoleView consoleView = (PydevLanguageConsoleView)console;
ConsoleCommunication communication = new PyDebugConsoleCommunication(debugProcess);
consoleView.setConsoleCommunication(communication);
ConsoleCommunication consoleCommunication = new PyDebugConsoleCommunication(project, debugProcess, ((PythonDebugLanguageConsoleView)console).getTextConsole());
pydevConsoleView.setConsoleCommunication(consoleCommunication);
List<AnAction> actions = AbstractConsoleRunnerWithHistory
.createConsoleExecActions(
pydevConsoleView.getConsole(), processHandler, new PydevConsoleExecuteActionHandler(pydevConsoleView,
processHandler,
consoleCommunication))
.getActionsAsList();
AbstractConsoleRunnerWithHistory.registerActionShortcuts(actions.toArray(new AnAction[actions.size()]), pydevConsoleView.getComponent());
}
}

View File

@@ -1,250 +1,220 @@
package com.jetbrains.python.run;
import com.google.common.collect.Lists;
import com.intellij.execution.DefaultExecutionResult;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionResult;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.CommandLineState;
import com.intellij.execution.configurations.ConfigurationPerRunnerSettings;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.configurations.ParametersList;
import com.intellij.execution.filters.Filter;
import com.intellij.execution.filters.TextConsoleBuilder;
import com.intellij.execution.filters.TextConsoleBuilderFactory;
import com.intellij.execution.process.ColoredProcessHandler;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.process.ProcessTerminatedListener;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.ProgramRunner;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.HashMap;
import com.jetbrains.python.console.ConsoleCommandExecutor;
import com.jetbrains.python.console.PyDebugConsoleBuilder;
import com.jetbrains.python.console.pydev.ICallback;
import com.jetbrains.python.console.pydev.InterpreterResponse;
import com.jetbrains.python.debugger.PyDebugProcess;
import com.jetbrains.python.debugger.PyDebugRunner;
import com.jetbrains.python.debugger.PyDebuggerException;
import com.jetbrains.python.sdk.PythonEnvUtil;
import com.jetbrains.python.sdk.PythonSdkFlavor;
import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map;
/**
* @author Leonid Shalupov
*/
public abstract class PythonCommandLineState extends CommandLineState {
// command line has a number of fixed groups of parameters; patchers should only operate on them and not the raw list.
public static final String GROUP_EXE_OPTIONS = "Exe Options";
public static final String GROUP_DEBUGGER = "Debugger";
public static final String GROUP_SCRIPT = "Script";
private final AbstractPythonRunConfiguration myConfig;
private final List<Filter> myFilters;
public boolean isDebug() {
return isDebug(getConfigurationSettings());
}
protected static boolean isDebug(ConfigurationPerRunnerSettings configurationSettings) {
return PyDebugRunner.PY_DEBUG_RUNNER.equals(configurationSettings.getRunnerId());
}
public AbstractPythonRunConfiguration getConfig() {
return myConfig;
}
public PythonCommandLineState(AbstractPythonRunConfiguration runConfiguration, ExecutionEnvironment env, List<Filter> filters) {
super(env);
myConfig = runConfiguration;
myFilters = filters;
}
@Override
public ExecutionResult execute(@NotNull Executor executor, @NotNull ProgramRunner runner) throws ExecutionException {
return execute(executor, (CommandLinePatcher[])null);
}
public ExecutionResult execute(Executor executor, CommandLinePatcher... patchers) throws ExecutionException {
final ColoredProcessHandler processHandler = startProcess(patchers);
final ConsoleView console = createAndAttachConsole(getConfig().getProject(), processHandler, executor);
List<AnAction> actions = Lists.newArrayList(createActions(console, processHandler));
return new DefaultExecutionResult(console, processHandler, actions.toArray(new AnAction[actions.size()]));
}
@NotNull
protected ConsoleView createAndAttachConsole(Project project, ProcessHandler processHandler, Executor executor)
throws ExecutionException {
final TextConsoleBuilder consoleBuilder = createConsoleBuilder(project);
for (Filter filter : myFilters) {
consoleBuilder.addFilter(filter);
}
final ConsoleView consoleView = consoleBuilder.getConsole();
consoleView.attachToProcess(processHandler);
return consoleView;
}
private TextConsoleBuilder createConsoleBuilder(Project project) {
if (isDebug()) {
return new PyDebugConsoleBuilder(project);
}
else {
return TextConsoleBuilderFactory.getInstance().createBuilder(project);
}
}
protected ColoredProcessHandler startProcess() throws ExecutionException {
return startProcess(null);
}
/**
* Patches the command line parameters applying patchers from first to last, and then runs it.
*
* @param patchers any number of patchers; any patcher may be null, and the whole argument may be null.
* @return handler of the started process
* @throws ExecutionException
*/
protected ColoredProcessHandler startProcess(CommandLinePatcher... patchers) throws ExecutionException {
GeneralCommandLine commandLine = generateCommandLine(patchers);
final ColoredProcessHandler processHandler = doCreateProcess(commandLine);
ProcessTerminatedListener.attach(processHandler);
return processHandler;
}
public GeneralCommandLine generateCommandLine(CommandLinePatcher[] patchers) throws ExecutionException {
GeneralCommandLine commandLine = generateCommandLine();
if (patchers != null) {
for (CommandLinePatcher patcher : patchers) {
if (patcher != null) patcher.patchCommandLine(commandLine);
}
}
return commandLine;
}
protected ColoredProcessHandler doCreateProcess(GeneralCommandLine commandLine) throws ExecutionException {
return PythonProcessHandler.createProcessHandler(commandLine);
}
public GeneralCommandLine generateCommandLine() throws ExecutionException {
GeneralCommandLine commandLine = new GeneralCommandLine();
setRunnerPath(commandLine);
// define groups
createStandardGroupsIn(commandLine);
buildCommandLineParameters(commandLine);
initEnvironment(commandLine);
return commandLine;
}
/**
* Creates a number of parameter groups in the command line:
* GROUP_EXE_OPTIONS, GROUP_DEBUGGER, GROUP_SCRIPT.
* These are necessary for command line patchers to work properly.
*
* @param commandLine
*/
public static void createStandardGroupsIn(GeneralCommandLine commandLine) {
ParametersList params = commandLine.getParametersList();
params.addParamsGroup(GROUP_EXE_OPTIONS);
params.addParamsGroup(GROUP_DEBUGGER);
params.addParamsGroup(GROUP_SCRIPT);
}
protected void initEnvironment(GeneralCommandLine commandLine) {
Map<String, String> envs = myConfig.getEnvs();
if (envs == null) {
envs = new HashMap<String, String>();
}
else {
envs = new HashMap<String, String>(envs);
}
addPredefinedEnvironmentVariables(envs, myConfig.isPassParentEnvs());
addCommonEnvironmentVariables(envs);
commandLine.setEnvParams(envs);
commandLine.setPassParentEnvs(myConfig.isPassParentEnvs());
}
protected static void addCommonEnvironmentVariables(Map<String, String> envs) {
PythonEnvUtil.setPythonUnbuffered(envs);
}
protected void addPredefinedEnvironmentVariables(Map<String, String> envs, boolean passParentEnvs) {
final PythonSdkFlavor flavor = PythonSdkFlavor.getFlavor(myConfig.getInterpreterPath());
if (flavor != null) {
flavor.addPredefinedEnvironmentVariables(envs);
}
Sdk pythonSdk = PythonSdkType.findSdkByPath(myConfig.getSdkHome());
if (pythonSdk != null) {
VirtualFile[] paths = pythonSdk.getRootProvider().getFiles(OrderRootType.CLASSES);
List<String> pathList = Lists.newArrayList();
for (VirtualFile file : paths) {
pathList.add(FileUtil.toSystemDependentName(file.getPath()));
}
PythonSdkFlavor.initPythonPath(envs, passParentEnvs, pathList);
}
}
protected void setRunnerPath(GeneralCommandLine commandLine) throws ExecutionException {
String interpreterPath = getInterpreterPath();
commandLine.setExePath(FileUtil.toSystemDependentName(interpreterPath));
}
protected String getInterpreterPath() throws ExecutionException {
String interpreterPath = myConfig.getInterpreterPath();
if (interpreterPath == null) {
throw new ExecutionException("Cannot find Python interpreter for this run configuration");
}
return interpreterPath;
}
protected void buildCommandLineParameters(GeneralCommandLine commandLine) {
}
public static class PyDebugProcessConsoleCommandExecutor implements ConsoleCommandExecutor {
@NotNull
private final PyDebugProcess myDebugProcess;
public PyDebugProcessConsoleCommandExecutor(@NotNull PyDebugProcess debugProcess) {
myDebugProcess = debugProcess;
}
@Override
public boolean isWaitingForInput() {
return false;
}
@Override
public void execInterpreter(
String s,
ICallback<Object, InterpreterResponse> callback) {
try {
myDebugProcess.evaluate(s, true);
callback.call(new InterpreterResponse("", null, false, isWaitingForInput()));
}
catch (PyDebuggerException e) {
callback.call(new InterpreterResponse(null, "", false, isWaitingForInput()));
}
}
}
}
package com.jetbrains.python.run;
import com.google.common.collect.Lists;
import com.intellij.execution.DefaultExecutionResult;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionResult;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.CommandLineState;
import com.intellij.execution.configurations.ConfigurationPerRunnerSettings;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.configurations.ParametersList;
import com.intellij.execution.filters.Filter;
import com.intellij.execution.filters.TextConsoleBuilder;
import com.intellij.execution.filters.TextConsoleBuilderFactory;
import com.intellij.execution.process.ColoredProcessHandler;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.process.ProcessTerminatedListener;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.ProgramRunner;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.HashMap;
import com.jetbrains.python.console.PyDebugConsoleBuilder;
import com.jetbrains.python.debugger.PyDebugRunner;
import com.jetbrains.python.sdk.PythonEnvUtil;
import com.jetbrains.python.sdk.PythonSdkFlavor;
import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map;
/**
* @author Leonid Shalupov
*/
public abstract class PythonCommandLineState extends CommandLineState {
// command line has a number of fixed groups of parameters; patchers should only operate on them and not the raw list.
public static final String GROUP_EXE_OPTIONS = "Exe Options";
public static final String GROUP_DEBUGGER = "Debugger";
public static final String GROUP_SCRIPT = "Script";
private final AbstractPythonRunConfiguration myConfig;
private final List<Filter> myFilters;
public boolean isDebug() {
return isDebug(getConfigurationSettings());
}
protected static boolean isDebug(ConfigurationPerRunnerSettings configurationSettings) {
return PyDebugRunner.PY_DEBUG_RUNNER.equals(configurationSettings.getRunnerId());
}
public AbstractPythonRunConfiguration getConfig() {
return myConfig;
}
public PythonCommandLineState(AbstractPythonRunConfiguration runConfiguration, ExecutionEnvironment env, List<Filter> filters) {
super(env);
myConfig = runConfiguration;
myFilters = filters;
}
@Override
public ExecutionResult execute(@NotNull Executor executor, @NotNull ProgramRunner runner) throws ExecutionException {
return execute(executor, (CommandLinePatcher[])null);
}
public ExecutionResult execute(Executor executor, CommandLinePatcher... patchers) throws ExecutionException {
final ColoredProcessHandler processHandler = startProcess(patchers);
final ConsoleView console = createAndAttachConsole(getConfig().getProject(), processHandler, executor);
List<AnAction> actions = Lists.newArrayList(createActions(console, processHandler));
return new DefaultExecutionResult(console, processHandler, actions.toArray(new AnAction[actions.size()]));
}
@NotNull
protected ConsoleView createAndAttachConsole(Project project, ProcessHandler processHandler, Executor executor)
throws ExecutionException {
final TextConsoleBuilder consoleBuilder = createConsoleBuilder(project);
for (Filter filter : myFilters) {
consoleBuilder.addFilter(filter);
}
final ConsoleView consoleView = consoleBuilder.getConsole();
consoleView.attachToProcess(processHandler);
return consoleView;
}
private TextConsoleBuilder createConsoleBuilder(Project project) {
if (isDebug()) {
return new PyDebugConsoleBuilder(project);
}
else {
return TextConsoleBuilderFactory.getInstance().createBuilder(project);
}
}
protected ColoredProcessHandler startProcess() throws ExecutionException {
return startProcess(null);
}
/**
* Patches the command line parameters applying patchers from first to last, and then runs it.
*
* @param patchers any number of patchers; any patcher may be null, and the whole argument may be null.
* @return handler of the started process
* @throws ExecutionException
*/
protected ColoredProcessHandler startProcess(CommandLinePatcher... patchers) throws ExecutionException {
GeneralCommandLine commandLine = generateCommandLine(patchers);
final ColoredProcessHandler processHandler = doCreateProcess(commandLine);
ProcessTerminatedListener.attach(processHandler);
return processHandler;
}
public GeneralCommandLine generateCommandLine(CommandLinePatcher[] patchers) throws ExecutionException {
GeneralCommandLine commandLine = generateCommandLine();
if (patchers != null) {
for (CommandLinePatcher patcher : patchers) {
if (patcher != null) patcher.patchCommandLine(commandLine);
}
}
return commandLine;
}
protected ColoredProcessHandler doCreateProcess(GeneralCommandLine commandLine) throws ExecutionException {
return PythonProcessHandler.createProcessHandler(commandLine);
}
public GeneralCommandLine generateCommandLine() throws ExecutionException {
GeneralCommandLine commandLine = new GeneralCommandLine();
setRunnerPath(commandLine);
// define groups
createStandardGroupsIn(commandLine);
buildCommandLineParameters(commandLine);
initEnvironment(commandLine);
return commandLine;
}
/**
* Creates a number of parameter groups in the command line:
* GROUP_EXE_OPTIONS, GROUP_DEBUGGER, GROUP_SCRIPT.
* These are necessary for command line patchers to work properly.
*
* @param commandLine
*/
public static void createStandardGroupsIn(GeneralCommandLine commandLine) {
ParametersList params = commandLine.getParametersList();
params.addParamsGroup(GROUP_EXE_OPTIONS);
params.addParamsGroup(GROUP_DEBUGGER);
params.addParamsGroup(GROUP_SCRIPT);
}
protected void initEnvironment(GeneralCommandLine commandLine) {
Map<String, String> envs = myConfig.getEnvs();
if (envs == null) {
envs = new HashMap<String, String>();
}
else {
envs = new HashMap<String, String>(envs);
}
addPredefinedEnvironmentVariables(envs, myConfig.isPassParentEnvs());
addCommonEnvironmentVariables(envs);
commandLine.setEnvParams(envs);
commandLine.setPassParentEnvs(myConfig.isPassParentEnvs());
}
protected static void addCommonEnvironmentVariables(Map<String, String> envs) {
PythonEnvUtil.setPythonUnbuffered(envs);
}
protected void addPredefinedEnvironmentVariables(Map<String, String> envs, boolean passParentEnvs) {
final PythonSdkFlavor flavor = PythonSdkFlavor.getFlavor(myConfig.getInterpreterPath());
if (flavor != null) {
flavor.addPredefinedEnvironmentVariables(envs);
}
Sdk pythonSdk = PythonSdkType.findSdkByPath(myConfig.getSdkHome());
if (pythonSdk != null) {
VirtualFile[] paths = pythonSdk.getRootProvider().getFiles(OrderRootType.CLASSES);
List<String> pathList = Lists.newArrayList();
for (VirtualFile file : paths) {
pathList.add(FileUtil.toSystemDependentName(file.getPath()));
}
PythonSdkFlavor.initPythonPath(envs, passParentEnvs, pathList);
}
}
protected void setRunnerPath(GeneralCommandLine commandLine) throws ExecutionException {
String interpreterPath = getInterpreterPath();
commandLine.setExePath(FileUtil.toSystemDependentName(interpreterPath));
}
protected String getInterpreterPath() throws ExecutionException {
String interpreterPath = myConfig.getInterpreterPath();
if (interpreterPath == null) {
throw new ExecutionException("Cannot find Python interpreter for this run configuration");
}
return interpreterPath;
}
protected void buildCommandLineParameters(GeneralCommandLine commandLine) {
}
}