EDU-652 Debug input functions doesn't work for PyCharm Edu
[idea/community.git] / python / educational-python / src / com / jetbrains / python / edu / debugger / PyEduDebugRunner.java
1 package com.jetbrains.python.edu.debugger;
2
3 import com.intellij.execution.ExecutionResult;
4 import com.intellij.execution.Executor;
5 import com.intellij.execution.configurations.RunProfile;
6 import com.intellij.execution.configurations.RunProfileState;
7 import com.intellij.execution.process.ProcessHandler;
8 import com.intellij.execution.runners.ExecutionEnvironment;
9 import com.intellij.execution.ui.ExecutionConsole;
10 import com.intellij.execution.ui.RunnerLayoutUi;
11 import com.intellij.execution.ui.actions.CloseAction;
12 import com.intellij.ide.actions.ContextHelpAction;
13 import com.intellij.openapi.actionSystem.*;
14 import com.intellij.openapi.diagnostic.Logger;
15 import com.intellij.openapi.editor.Document;
16 import com.intellij.openapi.fileEditor.FileDocumentManager;
17 import com.intellij.openapi.project.Project;
18 import com.intellij.openapi.util.SystemInfo;
19 import com.intellij.openapi.util.io.FileUtil;
20 import com.intellij.openapi.vfs.VfsUtil;
21 import com.intellij.openapi.vfs.VirtualFile;
22 import com.intellij.ui.content.Content;
23 import com.intellij.ui.content.ContentManager;
24 import com.intellij.xdebugger.XDebugSession;
25 import com.intellij.xdebugger.XDebuggerBundle;
26 import com.intellij.xdebugger.impl.XDebugSessionImpl;
27 import com.intellij.xdebugger.impl.actions.XDebuggerActions;
28 import com.intellij.xdebugger.impl.ui.XDebugSessionTab;
29 import com.jetbrains.python.console.PythonDebugLanguageConsoleView;
30 import com.jetbrains.python.debugger.PyDebugProcess;
31 import com.jetbrains.python.debugger.PyDebugRunner;
32 import com.jetbrains.python.debugger.PyLineBreakpointType;
33 import com.jetbrains.python.run.PythonCommandLineState;
34 import com.jetbrains.python.run.PythonRunConfiguration;
35 import org.jetbrains.annotations.NotNull;
36 import org.jetbrains.annotations.Nullable;
37
38 import java.io.File;
39 import java.net.ServerSocket;
40
41 public class PyEduDebugRunner extends PyDebugRunner {
42   private static final Logger LOG = Logger.getInstance(PyEduDebugRunner.class);
43   public static final int NO_LINE = -1;
44
45   @Override
46   public boolean canRun(@NotNull String executorId, @NotNull RunProfile profile) {
47     return executorId.equals(PyEduDebugExecutor.ID);
48   }
49
50   @NotNull
51   @Override
52   protected PyDebugProcess createDebugProcess(@NotNull XDebugSession session,
53                                               ServerSocket serverSocket,
54                                               ExecutionResult result,
55                                               PythonCommandLineState pyState) {
56     ExecutionConsole executionConsole = result.getExecutionConsole();
57     ProcessHandler processHandler = result.getProcessHandler();
58     boolean isMultiProcess = pyState.isMultiprocessDebug();
59     String scriptName = getScriptName(pyState);
60     if (scriptName != null) {
61       VirtualFile file = VfsUtil.findFileByIoFile(new File(scriptName), true);
62       if (file != null) {
63         int line = getBreakpointLineNumber(file, session.getProject());
64         if (line != NO_LINE) {
65           return new PyEduDebugProcess(session, serverSocket,
66                                        executionConsole, processHandler,
67                                        isMultiProcess, scriptName, line + 1);
68         }
69       }
70     }
71     LOG.info("Failed to create PyEduDebugProcess. PyDebugProcess created instead.");
72     return new PyDebugProcess(session, serverSocket, executionConsole,
73                               processHandler, isMultiProcess);
74   }
75
76   @Nullable
77   private static String getScriptName(PythonCommandLineState pyState) {
78     ExecutionEnvironment environment = pyState.getEnvironment();
79     if (environment == null) {
80       return null;
81     }
82     RunProfile runProfile = environment.getRunProfile();
83     if (runProfile instanceof PythonRunConfiguration) {
84       String name = FileUtil.toSystemIndependentName(((PythonRunConfiguration)runProfile).getScriptName());
85       return SystemInfo.isWindows ? name.toLowerCase() : name;
86     }
87     return null;
88   }
89
90   /**
91    * @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
92    */
93   private static int getBreakpointLineNumber(@NotNull final VirtualFile file, @NotNull final Project project) {
94     Document document = FileDocumentManager.getInstance().getDocument(file);
95     if (document == null) {
96       return NO_LINE;
97     }
98     PyLineBreakpointType lineBreakpointType = new PyLineBreakpointType();
99     for (int line = 0; line < document.getLineCount(); line++) {
100       if (lineBreakpointType.canPutAt(file, line, project)) {
101         return line;
102       }
103     }
104     return NO_LINE;
105   }
106
107
108   @Override
109   protected void initSession(XDebugSession session, RunProfileState state, Executor executor) {
110     XDebugSessionTab tab = ((XDebugSessionImpl)session).getSessionTab();
111     if (tab != null) {
112       RunnerLayoutUi ui = tab.getUi();
113       ContentManager contentManager = ui.getContentManager();
114       Content content = findContent(contentManager, XDebuggerBundle.message("debugger.session.tab.watches.title"));
115       if (content != null) {
116         contentManager.removeContent(content, true);
117       }
118       content = findContent(contentManager, XDebuggerBundle.message("debugger.session.tab.console.content.name"));
119       if (content != null) {
120         ExecutionConsole console = session.getDebugProcess().createConsole();
121         PythonDebugLanguageConsoleView view = (PythonDebugLanguageConsoleView)console;
122         Presentation presentation = view.getSwitchConsoleActionPresentation();
123         ToggleAction action = new ToggleAction(presentation.getText(), presentation.getDescription(), presentation.getIcon()) {
124
125           @Override
126           public boolean isSelected(AnActionEvent e) {
127             return !view.isPrimaryConsoleEnabled();
128           }
129
130           @Override
131           public void setSelected(AnActionEvent e, boolean state) {
132             view.enableConsole(!state);
133           }
134         };
135         content.setActions(new DefaultActionGroup(action), ActionPlaces.DEBUGGER_TOOLBAR,
136                               view.getPreferredFocusableComponent());
137       }
138       patchLeftToolbar(session, ui);
139     }
140   }
141
142   private static void patchLeftToolbar(@NotNull XDebugSession session, @NotNull RunnerLayoutUi ui) {
143     DefaultActionGroup newLeftToolbar = new DefaultActionGroup();
144
145     DefaultActionGroup firstGroup = new DefaultActionGroup();
146     addActionToGroup(firstGroup, XDebuggerActions.RESUME);
147     addActionToGroup(firstGroup, IdeActions.ACTION_STOP_PROGRAM);
148     newLeftToolbar.addAll(firstGroup);
149
150     newLeftToolbar.addSeparator();
151
152     Executor executor = PyEduDebugExecutor.getInstance();
153     newLeftToolbar.add(new CloseAction(executor, session.getRunContentDescriptor(), session.getProject()));
154     //TODO: return proper helpID
155     newLeftToolbar.add(new ContextHelpAction(executor.getHelpId()));
156
157     ui.getOptions().setLeftToolbar(newLeftToolbar, ActionPlaces.DEBUGGER_TOOLBAR);
158   }
159
160   private static void addActionToGroup(DefaultActionGroup group, String actionId) {
161     AnAction action = ActionManager.getInstance().getAction(actionId);
162     if (action != null) {
163       action.getTemplatePresentation().setEnabled(true);
164       group.add(action, Constraints.LAST);
165     }
166   }
167
168   @Nullable
169   private static Content findContent(ContentManager manager, String name) {
170     for (Content content : manager.getContents()) {
171       if (content.getDisplayName().equals(name)) {
172         return content;
173       }
174     }
175     return null;
176   }
177 }