Merge branch 'liana/simplified_debugger'

Conflicts:
	python/edu/src/META-INF/PyCharmEduPlugin.xml
	python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java
This commit is contained in:
Ekaterina Tuzova
2015-08-24 11:33:01 +03:00
21 changed files with 588 additions and 165 deletions
@@ -0,0 +1,18 @@
package icons;
import com.intellij.openapi.util.IconLoader;
import javax.swing.*;
/**
* NOTE THIS FILE IS AUTO-GENERATED
* DO NOT EDIT IT BY HAND, run build/scripts/icons.gant instead
*/
public class PythonEducationalIcons {
private static Icon load(String path) {
return IconLoader.getIcon(path, PythonEducationalIcons.class);
}
public static final Icon Field = load("/icons/com/jetbrains/edu/Field.png"); // 16x16
public static final Icon SpecialVar = load("/icons/com/jetbrains/edu/specialVar.png"); // 16x16
}
+1
View File
@@ -0,0 +1 @@
skip: tips
Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 B

+3 -8
View File
@@ -29,17 +29,12 @@
<extensions defaultExtensionNs="com.intellij">
<codeInsight.lineMarkerProvider language="Python" implementationClass="com.jetbrains.python.edu.PyExecuteFileLineMarkerProvider"/>
<programRunner implementation="com.jetbrains.python.edu.debugger.PyEduDebugRunner"/>
<executor implementation="com.jetbrains.python.edu.debugger.PyEduDebugExecutor" order="first,after run"/>
<consoleInputFilterProvider implementation="com.jetbrains.python.edu.debugger.PyEduConsoleInputFilterProvider"/>
</extensions>
<extensions defaultExtensionNs="Edu">
<executeFile implementation="com.jetbrains.python.edu.PyEduAddParametersAction"/>
</extensions>
<actions>
<group id="PyRunMenu">
<action id="runCurrentFile" class="com.jetbrains.python.edu.PyRunCurrentFileAction"/>
<add-to-group group-id="RunMenu" anchor="first"/>
</group>
</actions>
</idea-plugin>
@@ -19,6 +19,9 @@ import com.google.common.collect.Sets;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.intention.IntentionActionBean;
import com.intellij.codeInsight.intention.IntentionManager;
import com.intellij.execution.Executor;
import com.intellij.execution.ExecutorRegistryImpl;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.ide.AppLifecycleListener;
import com.intellij.ide.GeneralSettings;
import com.intellij.ide.SelectInTarget;
@@ -31,6 +34,9 @@ import com.intellij.ide.ui.customization.CustomizationUtil;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.ide.util.TipAndTrickBean;
import com.intellij.notification.EventLog;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
@@ -313,6 +319,10 @@ public class PyCharmEduInitialConfigurator {
}
private static void patchProjectAreaExtensions(@NotNull final Project project) {
Executor debugExecutor = DefaultDebugExecutor.getDebugExecutorInstance();
unregisterAction(debugExecutor.getId(), ExecutorRegistryImpl.RUNNERS_GROUP);
unregisterAction(debugExecutor.getContextActionId(), ExecutorRegistryImpl.RUN_CONTEXT_GROUP);
ExtensionsArea projectArea = Extensions.getArea(project);
for (SelectInTarget target : Extensions.getExtensions(SelectInTarget.EP_NAME, project)) {
@@ -330,6 +340,18 @@ public class PyCharmEduInitialConfigurator {
}
}
private static void unregisterAction(String actionId, String groupId) {
ActionManager actionManager = ActionManager.getInstance();
AnAction action = actionManager.getAction(actionId);
if (action != null) {
AnAction actionGroup = actionManager.getAction(groupId);
if (actionGroup != null && actionGroup instanceof DefaultActionGroup) {
((DefaultActionGroup)actionGroup).remove(action);
actionManager.unregisterAction(actionId);
}
}
}
private static void patchKeymap() {
Set<String> droppedActions = ContainerUtil.newHashSet(
"AddToFavoritesPopup",
@@ -0,0 +1,16 @@
package com.jetbrains.python.edu;
import com.intellij.execution.actions.RunContextAction;
import com.intellij.openapi.actionSystem.AnAction;
import com.jetbrains.python.edu.debugger.PyEduDebugExecutor;
import org.jetbrains.annotations.Nullable;
public class PyDebugCurrentFile implements PyExecuteFileExtensionPoint {
@Nullable
@Override
public AnAction getRunAction() {
return new RunContextAction(PyEduDebugExecutor.getInstance());
}
}
@@ -17,13 +17,13 @@ package com.jetbrains.python.edu;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.extensions.ExtensionPointName;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface PyExecuteFileExtensionPoint {
ExtensionPointName<PyExecuteFileExtensionPoint> EP_NAME = ExtensionPointName.create("Edu.executeFile");
@NotNull
@Nullable
AnAction getRunAction();
}
@@ -3,22 +3,16 @@ package com.jetbrains.python.edu;
import com.intellij.codeHighlighting.Pass;
import com.intellij.codeInsight.daemon.LineMarkerInfo;
import com.intellij.codeInsight.daemon.LineMarkerProvider;
import com.intellij.execution.actions.RunContextAction;
import com.intellij.execution.executors.DefaultRunExecutor;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.ActionGroup;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.openapi.ui.popup.ListPopup;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.ui.popup.PopupFactoryImpl;
import com.intellij.util.Function;
import com.jetbrains.python.psi.PyFile;
import com.jetbrains.python.psi.PyImportStatement;
import com.jetbrains.python.psi.PyStatement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -37,90 +31,48 @@ public class PyExecuteFileLineMarkerProvider implements LineMarkerProvider {
@Override
public void collectSlowLineMarkers(@NotNull List<PsiElement> elements, @NotNull Collection<LineMarkerInfo> result) {
for (PsiElement element : elements) {
if (isFirstCodeLine(element)) {
final LineMarkerInfo<PsiElement> markerInfo = new LineMarkerInfo<PsiElement>(
element, element.getTextRange(), AllIcons.Actions.Execute, Pass.UPDATE_OVERRIDEN_MARKERS,
new Function<PsiElement, String>() {
@Override
public String fun(PsiElement e) {
return "Execute '" + e.getContainingFile().getName() + "'";
}
}, null,
GutterIconRenderer.Alignment.RIGHT) {
if (elements.isEmpty()) {
return;
}
PsiElement element = elements.get(0).getContainingFile();
final RunContextAction runAction = new RunContextAction(DefaultRunExecutor.getRunExecutorInstance());
final LineMarkerInfo<PsiElement> markerInfo = new LineMarkerInfo<PsiElement>(
element, 0, AllIcons.Actions.Execute, Pass.UPDATE_OVERRIDEN_MARKERS,
new Function<PsiElement, String>() {
@Override
public String fun(PsiElement e) {
return "Execute '" + e.getContainingFile().getName() + "'";
}
}, null,
GutterIconRenderer.Alignment.RIGHT) {
@Nullable
@Override
public GutterIconRenderer createGutterRenderer() {
return new LineMarkerGutterIconRenderer<PsiElement>(this) {
@Override
public AnAction getClickAction() {
return runAction;
}
@Nullable
@Override
public GutterIconRenderer createGutterRenderer() {
return new LineMarkerGutterIconRenderer<PsiElement>(this){
@Override
public AnAction getClickAction() {
return new AnAction() {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
final DefaultActionGroup group = new DefaultActionGroup();
group.add(new PyRunCurrentFileAction());
final PyExecuteFileExtensionPoint[] extensions =
ApplicationManager.getApplication().getExtensions(PyExecuteFileExtensionPoint.EP_NAME);
for (PyExecuteFileExtensionPoint extension : extensions) {
final AnAction action = extension.getRunAction();
action.update(e);
if (e.getPresentation().isEnabled())
group.add(action);
}
if (group.getChildrenCount() == 1) {
new PyRunCurrentFileAction().actionPerformed(e);
}
else {
final ListPopup popup =
new PopupFactoryImpl().createActionGroupPopup(null, group, e.getDataContext(), false, false, false, null, 5);
popup.showInBestPositionFor(e.getDataContext());
}
}
};
public ActionGroup getPopupMenuActions() {
final DefaultActionGroup group = new DefaultActionGroup();
group.add(runAction);
final PyExecuteFileExtensionPoint[] extensions =
ApplicationManager.getApplication().getExtensions(PyExecuteFileExtensionPoint.EP_NAME);
for (PyExecuteFileExtensionPoint extension : extensions) {
final AnAction action = extension.getRunAction();
if (action == null) {
continue;
}
@Nullable
@Override
public ActionGroup getPopupMenuActions() {
final DefaultActionGroup group = new DefaultActionGroup();
group.add(new PyRunCurrentFileAction());
final PyExecuteFileExtensionPoint[] extensions =
ApplicationManager.getApplication().getExtensions(PyExecuteFileExtensionPoint.EP_NAME);
for (PyExecuteFileExtensionPoint extension : extensions) {
final AnAction action = extension.getRunAction();
group.add(action);
}
return group;
}
};
group.add(action);
}
return group;
}
};
result.add(markerInfo);
}
}
}
private static boolean isFirstCodeLine(PsiElement element) {
return element instanceof PyStatement &&
element.getParent() instanceof PyFile &&
!isNothing(element) &&
nothingBefore(element);
}
private static boolean nothingBefore(PsiElement element) {
element = element.getPrevSibling();
while (element != null) {
if (!isNothing(element)) {
return false;
}
element = element.getPrevSibling();
}
return true;
}
private static boolean isNothing(PsiElement element) {
return (element instanceof PsiComment) || (element instanceof PyImportStatement) || (element instanceof PsiWhiteSpace);
};
result.add(markerInfo);
}
}
@@ -1,56 +0,0 @@
package com.jetbrains.python.edu;
import com.intellij.execution.Location;
import com.intellij.execution.RunManagerEx;
import com.intellij.execution.RunnerAndConfigurationSettings;
import com.intellij.execution.actions.ConfigurationContext;
import com.intellij.execution.executors.DefaultRunExecutor;
import com.intellij.execution.runners.ExecutionUtil;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.jetbrains.python.PythonFileType;
import org.jetbrains.annotations.NotNull;
/**
* @author traff
*/
public class PyRunCurrentFileAction extends AnAction {
public PyRunCurrentFileAction() {
getTemplatePresentation().setIcon(AllIcons.Actions.Execute);
}
@Override
public void update(AnActionEvent e) {
Presentation presentation = e.getPresentation();
final ConfigurationContext context = ConfigurationContext.getFromContext(e.getDataContext());
Location location = context.getLocation();
if (location != null && location.getPsiElement().getContainingFile() != null && location.getPsiElement().getContainingFile().getFileType() == PythonFileType.INSTANCE) {
presentation.setEnabled(true);
presentation.setText("Run '" + location.getPsiElement().getContainingFile().getName() + "'");
}
}
@Override
public void actionPerformed(AnActionEvent e) {
final ConfigurationContext context = ConfigurationContext.getFromContext(e.getDataContext());
run(context);
}
public static void run(@NotNull ConfigurationContext context) {
RunnerAndConfigurationSettings configuration = context.findExisting();
final RunManagerEx runManager = (RunManagerEx)context.getRunManager();
if (configuration == null) {
configuration = context.getConfiguration();
if (configuration == null) {
return;
}
runManager.setTemporaryConfiguration(configuration);
}
runManager.setSelectedConfiguration(configuration);
ExecutionUtil.runConfiguration(configuration, DefaultRunExecutor.getRunExecutorInstance());
}
}
@@ -0,0 +1,32 @@
package com.jetbrains.python.edu.debugger;
import com.intellij.execution.filters.ConsoleInputFilterProvider;
import com.intellij.execution.filters.InputFilter;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
public class PyEduConsoleInputFilterProvider implements ConsoleInputFilterProvider {
@NotNull
@Override
public InputFilter[] getDefaultFilters(@NotNull Project project) {
return new InputFilter[]{new InputFilter() {
@Nullable
@Override
public List<Pair<String, ConsoleViewContentType>> applyFilter(String text, ConsoleViewContentType outputType) {
if (outputType.equals(ConsoleViewContentType.SYSTEM_OUTPUT) && !text.contains("exit code")) {
return Collections.emptyList();
}
if (text.startsWith("pydev debugger")) {
return Collections.emptyList();
}
return Collections.singletonList(Pair.create(text, outputType));
}
}};
}
}
@@ -0,0 +1,31 @@
package com.jetbrains.python.edu.debugger;
import com.intellij.execution.Executor;
import com.intellij.execution.ExecutorRegistry;
import com.intellij.execution.executors.DefaultDebugExecutor;
import org.jetbrains.annotations.NotNull;
public class PyEduDebugExecutor extends DefaultDebugExecutor {
public static final String ID = "EduExecutor";
@NotNull
@Override
public String getId() {
return ID;
}
public static Executor getInstance() {
return ExecutorRegistry.getInstance().getExecutorById(ID);
}
@Override
public String getContextActionId() {
return "EduDebug";
}
@NotNull
@Override
public String getStartActionText() {
return "Step Through";
}
}
@@ -0,0 +1,67 @@
package com.jetbrains.python.edu.debugger;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.ui.ExecutionConsole;
import com.intellij.xdebugger.XDebugSession;
import com.jetbrains.python.PythonHelpersLocator;
import com.jetbrains.python.debugger.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
class PyEduDebugProcess extends PyDebugProcess {
private final String myScriptName;
private final int myLine;
public PyEduDebugProcess(@NotNull XDebugSession session,
@NotNull ServerSocket serverSocket,
@NotNull ExecutionConsole executionConsole,
@Nullable ProcessHandler processHandler, boolean multiProcess,
String scriptName,
int line) {
super(session, serverSocket, executionConsole, processHandler, multiProcess);
myScriptName = scriptName;
myLine = line;
}
@Override
public PyStackFrame createStackFrame(PyStackFrameInfo frameInfo) {
return new PyEduStackFrame(getSession().getProject(), this, frameInfo,
getPositionConverter().convertFromPython(frameInfo.getPosition()));
}
@Override
public void init() {
super.init();
addTemporaryBreakpoint(PyLineBreakpointType.ID, myScriptName, myLine);
}
@NotNull
@Override
protected PySuspendContext createSuspendContext(PyThreadInfo threadInfo) {
threadInfo.updateState(threadInfo.getState(), new ArrayList<PyStackFrameInfo>(filterFrames(threadInfo.getFrames())));
return new PySuspendContext(this, threadInfo);
}
public Collection<PyStackFrameInfo> filterFrames(@Nullable List<PyStackFrameInfo> frames) {
if (frames == null) {
return Collections.emptyList();
}
final String debugger = PythonHelpersLocator.getHelperPath(PyDebugRunner.DEBUGGER_MAIN);
return Collections2.filter(frames, new Predicate<PyStackFrameInfo>() {
@Override
public boolean apply(PyStackFrameInfo frame) {
String file = frame.getPosition().getFile();
return !debugger.equals(file);
}
});
}
}
@@ -0,0 +1,191 @@
package com.jetbrains.python.edu.debugger;
import com.intellij.execution.ExecutionResult;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.filters.UrlFilter;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.ui.ExecutionConsole;
import com.intellij.execution.ui.RunnerLayoutUi;
import com.intellij.execution.ui.actions.CloseAction;
import com.intellij.execution.ui.layout.PlaceInGrid;
import com.intellij.icons.AllIcons;
import com.intellij.ide.actions.ContextHelpAction;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentManager;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.XDebuggerBundle;
import com.intellij.xdebugger.impl.XDebugSessionImpl;
import com.intellij.xdebugger.impl.actions.XDebuggerActions;
import com.intellij.xdebugger.impl.ui.XDebugSessionTab;
import com.jetbrains.python.console.PythonDebugLanguageConsoleView;
import com.jetbrains.python.debugger.PyDebugProcess;
import com.jetbrains.python.debugger.PyDebugRunner;
import com.jetbrains.python.debugger.PyLineBreakpointType;
import com.jetbrains.python.run.PythonCommandLineState;
import com.jetbrains.python.run.PythonRunConfiguration;
import com.jetbrains.python.run.PythonTracebackFilter;
import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.net.ServerSocket;
public class PyEduDebugRunner extends PyDebugRunner {
private static final Logger LOG = Logger.getInstance(PyEduDebugRunner.class);
public static final int NO_LINE = -1;
@Override
public boolean canRun(@NotNull String executorId, @NotNull RunProfile profile) {
return executorId.equals(PyEduDebugExecutor.ID);
}
@NotNull
@Override
protected PyDebugProcess createDebugProcess(@NotNull XDebugSession session,
ServerSocket serverSocket,
ExecutionResult result,
PythonCommandLineState pyState) {
ExecutionConsole executionConsole = result.getExecutionConsole();
ProcessHandler processHandler = result.getProcessHandler();
boolean isMultiProcess = pyState.isMultiprocessDebug();
String scriptName = getScriptName(pyState);
if (scriptName != null) {
VirtualFile file = VfsUtil.findFileByIoFile(new File(scriptName), true);
if (file != null) {
int line = getBreakpointLineNumber(file, session.getProject());
if (line != NO_LINE) {
return new PyEduDebugProcess(session, serverSocket,
executionConsole, processHandler,
isMultiProcess, scriptName, line + 1);
}
}
}
LOG.info("Failed to create PyEduDebugProcess. PyDebugProcess created instead.");
return new PyDebugProcess(session, serverSocket, executionConsole,
processHandler, isMultiProcess);
}
@Nullable
private static String getScriptName(PythonCommandLineState pyState) {
ExecutionEnvironment environment = pyState.getEnvironment();
if (environment == null) {
return null;
}
RunProfile runProfile = environment.getRunProfile();
if (runProfile instanceof PythonRunConfiguration) {
return ((PythonRunConfiguration)runProfile).getScriptName();
}
return null;
}
/**
* @return the smallest line (from 0 to line number) suitable to set breakpoint on it, NO_LINE if there is no such line in the file
*/
private static int getBreakpointLineNumber(@NotNull final VirtualFile file, @NotNull final Project project) {
Document document = FileDocumentManager.getInstance().getDocument(file);
if (document == null) {
return NO_LINE;
}
PyLineBreakpointType lineBreakpointType = new PyLineBreakpointType();
for (int line = 0; line < document.getLineCount(); line++) {
if (lineBreakpointType.canPutAt(file, line, project)) {
return line;
}
}
return NO_LINE;
}
@Override
protected void initSession(XDebugSession session, RunProfileState state, Executor executor) {
XDebugSessionTab tab = ((XDebugSessionImpl)session).getSessionTab();
if (tab != null) {
RunnerLayoutUi ui = tab.getUi();
ContentManager contentManager = ui.getContentManager();
Content content = findContent(contentManager, XDebuggerBundle.message("debugger.session.tab.watches.title"));
if (content != null) {
contentManager.removeContent(content, true);
}
content = findContent(contentManager, XDebuggerBundle.message("debugger.session.tab.console.content.name"));
if (content != null) {
contentManager.removeContent(content, true);
}
initEduConsole(session, ui);
}
}
private static void initEduConsole(@NotNull final XDebugSession session,
@NotNull final RunnerLayoutUi ui) {
Project project = session.getProject();
final Sdk sdk = PythonSdkType.findPythonSdk(ModuleManager.getInstance(project).getModules()[0]);
final PythonDebugLanguageConsoleView view = new PythonDebugLanguageConsoleView(project, sdk);
final ProcessHandler processHandler = session.getDebugProcess().getProcessHandler();
view.attachToProcess(processHandler);
view.addMessageFilter(new PythonTracebackFilter(project));
view.addMessageFilter(new UrlFilter());
view.enableConsole(false);
Content eduConsole =
ui.createContent("EduConsole", view.getComponent(),
XDebuggerBundle.message("debugger.session.tab.console.content.name"),
AllIcons.Debugger.ToolConsole, view.getPreferredFocusableComponent());
eduConsole.setCloseable(false);
ui.addContent(eduConsole, 0, PlaceInGrid.right, false);
PyDebugProcess process = (PyDebugProcess)session.getDebugProcess();
PyDebugRunner.initDebugConsoleView(project, process, view, processHandler, session);
patchLeftToolbar(session, ui);
}
private static void patchLeftToolbar(@NotNull XDebugSession session, @NotNull RunnerLayoutUi ui) {
DefaultActionGroup newLeftToolbar = new DefaultActionGroup();
DefaultActionGroup firstGroup = new DefaultActionGroup();
addActionToGroup(firstGroup, XDebuggerActions.RESUME);
addActionToGroup(firstGroup, IdeActions.ACTION_STOP_PROGRAM);
newLeftToolbar.addAll(firstGroup);
newLeftToolbar.addSeparator();
Executor executor = PyEduDebugExecutor.getInstance();
newLeftToolbar.add(new CloseAction(executor, session.getRunContentDescriptor(), session.getProject()));
//TODO: return proper helpID
newLeftToolbar.add(new ContextHelpAction(executor.getHelpId()));
ui.getOptions().setLeftToolbar(newLeftToolbar, ActionPlaces.DEBUGGER_TOOLBAR);
}
private static void addActionToGroup(DefaultActionGroup group, String actionId) {
AnAction action = ActionManager.getInstance().getAction(actionId);
if (action != null) {
action.getTemplatePresentation().setEnabled(true);
group.add(action, Constraints.LAST);
}
}
@Nullable
private static Content findContent(ContentManager manager, String name) {
for (Content content : manager.getContents()) {
if (content.getDisplayName().equals(name)) {
return content;
}
}
return null;
}
}
@@ -0,0 +1,113 @@
package com.jetbrains.python.edu.debugger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.ColoredTextContainer;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.xdebugger.XSourcePosition;
import com.intellij.xdebugger.frame.XCompositeNode;
import com.intellij.xdebugger.frame.XValue;
import com.intellij.xdebugger.frame.XValueChildrenList;
import com.intellij.xdebugger.frame.XValueGroup;
import com.jetbrains.python.debugger.PyFrameAccessor;
import com.jetbrains.python.debugger.PyStackFrame;
import com.jetbrains.python.debugger.PyStackFrameInfo;
import icons.PythonEducationalIcons;
import icons.PythonPsiApiIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.HashMap;
import java.util.Map;
public class PyEduStackFrame extends PyStackFrame {
public static final String MODULE = "<module>";
public static final String GLOBAL_FRAME = "Globals";
public static final String DOUBLE_UNDERSCORE = "__";
public static final String BUILTINS_VALUE_NAME = "__builtins__";
private final PyStackFrameInfo myFrameInfo;
private final XSourcePosition myPosition;
public PyEduStackFrame(@NotNull Project project,
@NotNull PyFrameAccessor debugProcess,
@NotNull PyStackFrameInfo frameInfo,
XSourcePosition position) {
super(project, debugProcess, frameInfo, position);
myFrameInfo = frameInfo;
myPosition = position;
}
@Override
public void customizePresentation(@NotNull ColoredTextContainer component) {
if (myPosition == null) {
component.append("<frame not available>", SimpleTextAttributes.GRAY_ATTRIBUTES);
return;
}
final VirtualFile file = myPosition.getFile();
String frameName = myFrameInfo.getName();
component.setIcon(MODULE.equals(frameName) ? PythonPsiApiIcons.PythonFile : PythonEducationalIcons.Field);
if (MODULE.equals(frameName)) {
component.append(GLOBAL_FRAME, SimpleTextAttributes.REGULAR_ATTRIBUTES);
component.append(" (" + file.getName() + ")", getGrayAttributes(SimpleTextAttributes.REGULAR_ATTRIBUTES));
}
else {
component.append(frameName, SimpleTextAttributes.REGULAR_ATTRIBUTES);
}
}
@Override
protected void addChildren(@NotNull final XCompositeNode node,
@Nullable final XValueChildrenList children) {
if (children == null) {
node.addChildren(XValueChildrenList.EMPTY, true);
return;
}
final Map<String, XValue> specialValues = new HashMap<String, XValue>();
XValueChildrenList filteredChildren = new XValueChildrenList();
for (int i = 0; i < children.size(); i++) {
String name = children.getName(i);
XValue value = children.getValue(i);
if (name.startsWith(DOUBLE_UNDERSCORE) && name.endsWith(DOUBLE_UNDERSCORE)) {
specialValues.put(name, value);
continue;
}
filteredChildren.add(name, value);
}
node.addChildren(filteredChildren, specialValues.isEmpty());
if (specialValues.isEmpty()) {
return;
}
addSpecialVars(node, specialValues);
}
private static void addSpecialVars(@NotNull XCompositeNode node, Map<String, XValue> specialValues) {
XValue builtins = specialValues.get(BUILTINS_VALUE_NAME);
if (builtins != null) {
specialValues.remove(BUILTINS_VALUE_NAME);
node.addChildren(XValueChildrenList.singleton("Builtins", builtins), false);
}
node.addChildren(XValueChildrenList.bottomGroup(createSpecialVarsGroup(specialValues)), true);
}
@NotNull
private static XValueGroup createSpecialVarsGroup(final Map<String, XValue> specialValues) {
return new XValueGroup("Special Variables") {
@Nullable
@Override
public Icon getIcon() {
return PythonEducationalIcons.SpecialVar;
}
@Override
public void computeChildren(@NotNull XCompositeNode node) {
XValueChildrenList list = new XValueChildrenList();
for (Map.Entry<String, XValue> entry : specialValues.entrySet()) {
list.add(entry.getKey(), entry.getValue());
}
node.addChildren(list, true);
}
};
}
}
@@ -56,6 +56,11 @@ public abstract class CCRunTestsAction extends AnAction {
@Override
public void update(@NotNull AnActionEvent e) {
if (!CCProjectService.setCCActionAvailable(e)) {
EduUtils.enableAction(e, false);
return;
}
Presentation presentation = e.getPresentation();
presentation.setText("");
presentation.setVisible(false);
@@ -668,6 +668,12 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
}
}
public void addTemporaryBreakpoint(String typeId, String file, int line) {
if (isConnected()) {
myDebugger.setTempBreakpoint(typeId, file, line);
}
}
public void removeBreakpoint(final PySourcePosition position) {
XLineBreakpoint breakpoint = myRegisteredBreakpoints.get(position);
if (breakpoint != null) {
@@ -703,7 +709,7 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
final List<PyStackFrameInfo> frames = threadInfo.getFrames();
if (frames != null) {
final PySuspendContext suspendContext = new PySuspendContext(this, threadInfo);
final PySuspendContext suspendContext = createSuspendContext(threadInfo);
XBreakpoint<?> breakpoint = null;
if (threadInfo.isStopOnBreakpoint()) {
@@ -733,6 +739,11 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
}
}
@NotNull
protected PySuspendContext createSuspendContext(PyThreadInfo threadInfo) {
return new PySuspendContext(this, threadInfo);
}
@Override
public void threadResumed(final PyThreadInfo threadInfo) {
mySuspendedThreads.remove(threadInfo);
@@ -18,6 +18,7 @@ package com.jetbrains.python.debugger;
import com.google.common.collect.Lists;
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.LanguageConsoleBuilder;
import com.intellij.execution.executors.DefaultDebugExecutor;
@@ -81,8 +82,8 @@ public class PyDebugRunner extends GenericProgramRunner {
((AbstractPythonRunConfiguration)profile).canRunWithCoverage();
}
@Override
protected RunContentDescriptor doExecute(@NotNull RunProfileState state, @NotNull final ExecutionEnvironment environment) throws ExecutionException {
protected XDebugSession createSession(@NotNull RunProfileState state, @NotNull final ExecutionEnvironment environment) throws ExecutionException {
FileDocumentManager.getInstance().saveAllDocuments();
final PythonCommandLineState pyState = (PythonCommandLineState)state;
@@ -91,24 +92,39 @@ public class PyDebugRunner extends GenericProgramRunner {
RunProfile profile = environment.getRunProfile();
final ExecutionResult result = pyState.execute(environment.getExecutor(), createCommandLinePatchers(environment.getProject(), pyState, profile, serverLocalPort));
final XDebugSession session = XDebuggerManager.getInstance(environment.getProject()).
return XDebuggerManager.getInstance(environment.getProject()).
startSession(environment, new XDebugProcessStarter() {
@Override
@NotNull
public XDebugProcess start(@NotNull final XDebugSession session) {
PyDebugProcess pyDebugProcess =
new PyDebugProcess(session, serverSocket, result.getExecutionConsole(), result.getProcessHandler(),
pyState.isMultiprocessDebug());
createDebugProcess(session, serverSocket, result, pyState);
createConsoleCommunicationAndSetupActions(environment.getProject(), result, pyDebugProcess, session);
return pyDebugProcess;
}
});
}
@NotNull
protected PyDebugProcess createDebugProcess(@NotNull XDebugSession session,
ServerSocket serverSocket,
ExecutionResult result,
PythonCommandLineState pyState) {
return new PyDebugProcess(session, serverSocket, result.getExecutionConsole(), result.getProcessHandler(),
pyState.isMultiprocessDebug());
}
@Override
protected RunContentDescriptor doExecute(@NotNull RunProfileState state, @NotNull final ExecutionEnvironment environment) throws ExecutionException {
XDebugSession session = createSession(state, environment);
initSession(session, state, environment.getExecutor());
return session.getRunContentDescriptor();
}
protected void initSession(XDebugSession session, RunProfileState state, Executor executor) {
}
public static int findIndex(List<String> paramList, String paramName) {
for (int i = 0; i < paramList.size(); i++) {
if (paramName.equals(paramList.get(i))) {
@@ -31,6 +31,7 @@ import com.intellij.xdebugger.frame.XCompositeNode;
import com.intellij.xdebugger.frame.XStackFrame;
import com.intellij.xdebugger.frame.XValueChildrenList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PyStackFrame extends XStackFrame {
@@ -96,11 +97,15 @@ public class PyStackFrame extends XStackFrame {
return attributes;
}
else {
return (attributes.getStyle() & SimpleTextAttributes.STYLE_ITALIC) != 0
? SimpleTextAttributes.GRAY_ITALIC_ATTRIBUTES : SimpleTextAttributes.GRAYED_ATTRIBUTES;
return getGrayAttributes(attributes);
}
}
protected static SimpleTextAttributes getGrayAttributes(SimpleTextAttributes attributes) {
return (attributes.getStyle() & SimpleTextAttributes.STYLE_ITALIC) != 0
? SimpleTextAttributes.GRAY_ITALIC_ATTRIBUTES : SimpleTextAttributes.GRAYED_ATTRIBUTES;
}
@Override
public void computeChildren(@NotNull final XCompositeNode node) {
if (node.isObsolete()) return;
@@ -108,9 +113,9 @@ public class PyStackFrame extends XStackFrame {
@Override
public void run() {
try {
final XValueChildrenList values = myDebugProcess.loadFrame();
XValueChildrenList values = myDebugProcess.loadFrame();
if (!node.isObsolete()) {
node.addChildren(values != null ? values : XValueChildrenList.EMPTY, true);
addChildren(node, values);
}
}
catch (PyDebuggerException e) {
@@ -123,6 +128,10 @@ public class PyStackFrame extends XStackFrame {
});
}
protected void addChildren(@NotNull final XCompositeNode node, @Nullable final XValueChildrenList children) {
node.addChildren(children != null ? children : XValueChildrenList.EMPTY, true);
}
public String getThreadId() {
return myFrameInfo.getThreadId();
}