mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-06 11:50:54 +07:00
1)added show command-line action to debug console
2)fixed multi-line command-line console input in debug
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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@_@";
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user