mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
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:
@@ -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
|
||||
}
|
||||
@@ -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 |
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+5
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user