Added custom executor to make canRun method in pyDebugRunner return false. Also it can be useful to replace debug tool window with custom tool window
</extensionPoints>
<extensions defaultExtensionNs="com.intellij">
+ <codeInsight.lineMarkerProvider language="Python" implementationClass="com.jetbrains.python.edu.PyDebugFileLineMarkerProvider"/>
<codeInsight.lineMarkerProvider language="Python" implementationClass="com.jetbrains.python.edu.PyExecuteFileLineMarkerProvider"/>
+ <programRunner implementation="com.jetbrains.python.edu.PyEduDebugRunner"/>
+ <executor implementation="com.jetbrains.python.edu.PyEduDebugExecutor" order="first,after run"/>
</extensions>
<actions>
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.RecentProjectsManager;
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.ex.EditorSettingsExternalizable;
import com.intellij.openapi.extensions.Extensions;
}
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);
for (SelectInTarget target : Extensions.getExtensions(SelectInTarget.EP_NAME, project)) {
if (ToolWindowId.FAVORITES_VIEW.equals(target.getToolWindowId())) {
Extensions.getArea(project).getExtensionPoint(SelectInTarget.EP_NAME).unregisterExtension(target);
}
}
+ private static void unregisterAction(String actionId, String groupId) {
+ ActionManager actionManager = ActionManager.getInstance();
+ AnAction action = actionManager.getAction(actionId);
+ if (action != null) {
+ ((DefaultActionGroup)actionManager.getAction(groupId)).remove(action);
+ actionManager.unregisterAction(actionId);
+ }
+ }
+
private static void patchKeymap() {
Set<String> droppedActions = ContainerUtil.newHashSet(
"AddToFavoritesPopup",
--- /dev/null
+package com.jetbrains.python.edu;
+
+import com.intellij.execution.RunnerAndConfigurationSettings;
+import com.intellij.execution.runners.ExecutionUtil;
+import com.intellij.icons.AllIcons;
+
+public class PyDebugCurrentFileAction extends PyRunConfigurationForFileAction {
+
+ public PyDebugCurrentFileAction() {
+ getTemplatePresentation().setIcon(AllIcons.Actions.StartDebugger);
+ }
+
+ @Override
+ protected String getConfigurationType() {
+ return "Debug";
+ }
+
+ @Override
+ protected void runConfiguration(RunnerAndConfigurationSettings configuration) {
+ ExecutionUtil.runConfiguration(configuration, PyEduDebugExecutor.getInstance());
+ }
+}
--- /dev/null
+package com.jetbrains.python.edu;
+
+import com.intellij.codeHighlighting.Pass;
+import com.intellij.codeInsight.daemon.GutterIconNavigationHandler;
+import com.intellij.codeInsight.daemon.LineMarkerInfo;
+import com.intellij.codeInsight.daemon.LineMarkerProvider;
+import com.intellij.execution.actions.ConfigurationContext;
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.DataManager;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.markup.GutterIconRenderer;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.util.PsiUtilBase;
+import com.intellij.util.Function;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.event.MouseEvent;
+import java.util.Collection;
+import java.util.List;
+
+public class PyDebugFileLineMarkerProvider implements LineMarkerProvider {
+ @Nullable
+ @Override
+ public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) {
+ return null;
+ }
+
+ @Override
+ public void collectSlowLineMarkers(@NotNull List<PsiElement> elements, @NotNull Collection<LineMarkerInfo> result) {
+ for (final PsiElement element : elements) {
+ if (PyEduUtils.isFirstCodeLine(element)) {
+ result.add(new LineMarkerInfo<PsiElement>(element, element.getTextRange(), AllIcons.Actions.StartDebugger,
+ Pass.UPDATE_OVERRIDEN_MARKERS,
+ new Function<PsiElement, String>() {
+ @Override
+ public String fun(PsiElement e) {
+ return "Debug '" + e.getContainingFile().getName() + "'";
+ }
+ }, new GutterIconNavigationHandler<PsiElement>() {
+ @Override
+ public void navigate(MouseEvent e, PsiElement elt) {
+ final Editor editor = PsiUtilBase.findEditor(elt);
+ assert editor != null;
+ ConfigurationContext configurationContext =
+ ConfigurationContext.getFromContext(DataManager.getInstance().getDataContext(editor.getComponent()));
+ new PyDebugCurrentFileAction().run(configurationContext);
+ }
+ }, GutterIconRenderer.Alignment.RIGHT));
+ }
+ }
+ }
+}
--- /dev/null
+package com.jetbrains.python.edu;
+
+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 "EduDebugClass";
+ }
+
+ @NotNull
+ @Override
+ public String getStartActionText() {
+ return "Step Through ";
+ }
+}
--- /dev/null
+package com.jetbrains.python.edu;
+
+import com.intellij.codeInsight.daemon.impl.CollectHighlightsUtil;
+import com.intellij.execution.configurations.RunProfile;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiManager;
+import com.intellij.xdebugger.XDebuggerManager;
+import com.intellij.xdebugger.breakpoints.XBreakpointManager;
+import com.intellij.xdebugger.breakpoints.XBreakpointProperties;
+import com.intellij.xdebugger.impl.breakpoints.LineBreakpointState;
+import com.intellij.xdebugger.impl.breakpoints.XBreakpointManagerImpl;
+import com.intellij.xdebugger.impl.breakpoints.XLineBreakpointImpl;
+import com.jetbrains.python.debugger.PyDebugProcess;
+import com.jetbrains.python.debugger.PyDebugRunner;
+import com.jetbrains.python.debugger.PyLineBreakpointType;
+import com.jetbrains.python.debugger.PySourcePosition;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.util.List;
+
+public class PyEduDebugRunner extends PyDebugRunner {
+
+ @Override
+ public boolean canRun(@NotNull String executorId, @NotNull RunProfile profile) {
+ return executorId.equals(PyEduDebugExecutor.ID);
+ }
+
+ @Override
+ protected void initDebugProcess(String name, PyDebugProcess pyDebugProcess) {
+ VirtualFile file = VfsUtil.findFileByIoFile(new File(name), true);
+ assert file != null;
+
+ final Project project = pyDebugProcess.getProject();
+ PsiFile psiFile = PsiManager.getInstance(project).findFile(file);
+
+ assert psiFile != null;
+
+ List<PsiElement> psiElements = CollectHighlightsUtil.getElementsInRange(psiFile, 0, psiFile.getTextLength());
+ for (PsiElement element : psiElements) {
+ if (PyEduUtils.isFirstCodeLine(element)) {
+ int offset = element.getTextRange().getStartOffset();
+ Document document = FileDocumentManager.getInstance().getDocument(file);
+ assert document != null;
+ int line = document.getLineNumber(offset) + 1;
+ PySourcePosition sourcePosition = pyDebugProcess.getPositionConverter().create(file.getPath(), line);
+ XBreakpointManager breakpointManager = XDebuggerManager.getInstance(project).getBreakpointManager();
+ PyLineBreakpointType type = new PyLineBreakpointType();
+ XBreakpointProperties properties = type.createBreakpointProperties(file, line);
+ LineBreakpointState<XBreakpointProperties>
+ breakpointState = new LineBreakpointState<XBreakpointProperties>(true, type.getId(), file.getUrl(), line, false, file.getTimeStamp());
+ pyDebugProcess.addBreakpoint(sourcePosition, new XLineBreakpointImpl<XBreakpointProperties>(type,
+ ((XBreakpointManagerImpl)breakpointManager),
+ properties, breakpointState));
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+package com.jetbrains.python.edu;
+
+import com.intellij.psi.PsiComment;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiWhiteSpace;
+import com.jetbrains.python.psi.PyFile;
+import com.jetbrains.python.psi.PyImportStatement;
+import com.jetbrains.python.psi.PyStatement;
+
+public class PyEduUtils {
+ public 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);
+ }
+}
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;
@Override
public void collectSlowLineMarkers(@NotNull List<PsiElement> elements, @NotNull Collection<LineMarkerInfo> result) {
for (PsiElement element : elements) {
- if (isFirstCodeLine(element)) {
+ if (PyEduUtils.isFirstCodeLine(element)) {
final LineMarkerInfo<PsiElement> markerInfo = new LineMarkerInfo<PsiElement>(
element, element.getTextRange(), AllIcons.Actions.Execute, Pass.UPDATE_OVERRIDEN_MARKERS,
new Function<PsiElement, String>() {
}
}
}
-
- 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);
- }
}
--- /dev/null
+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.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;
+
+public abstract class PyRunConfigurationForFileAction extends AnAction {
+
+ @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(getConfigurationType() + " '" + location.getPsiElement().getContainingFile().getName() + "'");
+ }
+ }
+
+ protected abstract String getConfigurationType();
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final ConfigurationContext context = ConfigurationContext.getFromContext(e.getDataContext());
+
+ run(context);
+ }
+
+ public 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);
+
+ runConfiguration(configuration);
+ }
+
+ protected abstract void runConfiguration(RunnerAndConfigurationSettings configuration);
+}
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 class PyRunCurrentFileAction extends PyRunConfigurationForFileAction {
+
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() + "'");
- }
+ protected String getConfigurationType() {
+ return "Run";
}
@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);
-
+ protected void runConfiguration(RunnerAndConfigurationSettings configuration) {
ExecutionUtil.runConfiguration(configuration, DefaultRunExecutor.getRunExecutorInstance());
}
}
import com.jetbrains.python.run.AbstractPythonRunConfiguration;
import com.jetbrains.python.run.CommandLinePatcher;
import com.jetbrains.python.run.PythonCommandLineState;
+import com.jetbrains.python.run.PythonRunConfiguration;
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
((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;
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
pyState.isMultiprocessDebug());
createConsoleCommunicationAndSetupActions(environment.getProject(), result, pyDebugProcess, session);
-
-
+ initDebugProcess(((PythonRunConfiguration)environment.getRunProfile()).getScriptName(), pyDebugProcess);
return pyDebugProcess;
}
});
- return session.getRunContentDescriptor();
+ }
+
+ protected void initDebugProcess(String name, PyDebugProcess pyDebugProcess) {
+ }
+
+ @Override
+ protected RunContentDescriptor doExecute(@NotNull RunProfileState state, @NotNull final ExecutionEnvironment environment) throws ExecutionException {
+ return createSession(state, environment).getRunContentDescriptor();
}
public static int findIndex(List<String> paramList, String paramName) {