Merge remote-tracking branch 'origin/master' into pycharm/docker
[idea/community.git] / python / src / com / jetbrains / python / debugger / PyDebugProcess.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.jetbrains.python.debugger;
17
18 import com.google.common.collect.Lists;
19 import com.google.common.collect.Maps;
20 import com.intellij.execution.process.ProcessEvent;
21 import com.intellij.execution.process.ProcessHandler;
22 import com.intellij.execution.process.ProcessListener;
23 import com.intellij.execution.ui.ConsoleView;
24 import com.intellij.execution.ui.ConsoleViewContentType;
25 import com.intellij.execution.ui.ExecutionConsole;
26 import com.intellij.openapi.application.AccessToken;
27 import com.intellij.openapi.application.ApplicationInfo;
28 import com.intellij.openapi.application.ApplicationManager;
29 import com.intellij.openapi.editor.Document;
30 import com.intellij.openapi.extensions.Extensions;
31 import com.intellij.openapi.fileEditor.FileDocumentManager;
32 import com.intellij.openapi.progress.ProgressIndicator;
33 import com.intellij.openapi.progress.ProgressManager;
34 import com.intellij.openapi.progress.Task;
35 import com.intellij.openapi.project.Project;
36 import com.intellij.openapi.ui.Messages;
37 import com.intellij.openapi.util.Key;
38 import com.intellij.openapi.util.Ref;
39 import com.intellij.openapi.vfs.VirtualFile;
40 import com.intellij.psi.PsiElement;
41 import com.intellij.psi.PsiFile;
42 import com.intellij.psi.PsiManager;
43 import com.intellij.psi.ResolveState;
44 import com.intellij.psi.scope.PsiScopeProcessor;
45 import com.intellij.psi.util.PsiTreeUtil;
46 import com.intellij.remote.RemoteProcessHandlerBase;
47 import com.intellij.util.ui.UIUtil;
48 import com.intellij.xdebugger.*;
49 import com.intellij.xdebugger.breakpoints.XBreakpoint;
50 import com.intellij.xdebugger.breakpoints.XBreakpointHandler;
51 import com.intellij.xdebugger.breakpoints.XBreakpointType;
52 import com.intellij.xdebugger.breakpoints.XLineBreakpoint;
53 import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider;
54 import com.intellij.xdebugger.frame.XValueChildrenList;
55 import com.intellij.xdebugger.impl.XSourcePositionImpl;
56 import com.intellij.xdebugger.stepping.XSmartStepIntoHandler;
57 import com.jetbrains.python.PythonFileType;
58 import com.jetbrains.python.console.PythonDebugLanguageConsoleView;
59 import com.jetbrains.python.console.pydev.PydevCompletionVariant;
60 import com.jetbrains.python.debugger.pydev.*;
61 import com.jetbrains.python.psi.PyFunction;
62 import com.jetbrains.python.psi.PyImportElement;
63 import com.jetbrains.python.psi.resolve.PyResolveUtil;
64 import com.jetbrains.python.psi.types.PyClassType;
65 import com.jetbrains.python.psi.types.PyType;
66 import com.jetbrains.python.psi.types.PyTypeParser;
67 import com.jetbrains.python.run.PythonProcessHandler;
68 import org.jetbrains.annotations.NotNull;
69 import org.jetbrains.annotations.Nullable;
70
71 import java.io.IOException;
72 import java.net.ServerSocket;
73 import java.util.*;
74 import java.util.concurrent.ConcurrentHashMap;
75
76 import static javax.swing.SwingUtilities.invokeLater;
77
78 /**
79  * @author yole
80  */
81 // todo: bundle messages
82 // todo: pydevd supports module reloading - look for a way to use the feature
83 public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, ProcessListener {
84   private static final int CONNECTION_TIMEOUT = 60000;
85
86   private final ProcessDebugger myDebugger;
87   private final XBreakpointHandler[] myBreakpointHandlers;
88   private final PyDebuggerEditorsProvider myEditorsProvider;
89   private final ProcessHandler myProcessHandler;
90   private final ExecutionConsole myExecutionConsole;
91   private final Map<PySourcePosition, XLineBreakpoint> myRegisteredBreakpoints = new ConcurrentHashMap<PySourcePosition, XLineBreakpoint>();
92   private final Map<String, XBreakpoint<? extends ExceptionBreakpointProperties>> myRegisteredExceptionBreakpoints =
93     new ConcurrentHashMap<String, XBreakpoint<? extends ExceptionBreakpointProperties>>();
94
95   private final List<PyThreadInfo> mySuspendedThreads = Collections.synchronizedList(Lists.<PyThreadInfo>newArrayList());
96   private final Map<String, XValueChildrenList> myStackFrameCache = Maps.newHashMap();
97   private final Map<String, PyDebugValue> myNewVariableValue = Maps.newHashMap();
98   private boolean myDownloadSources = false;
99
100   private boolean myClosing = false;
101
102   private PyPositionConverter myPositionConverter;
103   private final XSmartStepIntoHandler<?> mySmartStepIntoHandler;
104   private boolean myWaitingForConnection = false;
105   private PyStackFrame myStackFrameBeforeResume;
106   private PyStackFrame myConsoleContextFrame = null;
107   private PyReferrersLoader myReferrersProvider;
108
109   public PyDebugProcess(final @NotNull XDebugSession session,
110                         @NotNull final ServerSocket serverSocket,
111                         @NotNull final ExecutionConsole executionConsole,
112                         @Nullable final ProcessHandler processHandler, boolean multiProcess) {
113     super(session);
114     session.setPauseActionSupported(true);
115     if (multiProcess) {
116       myDebugger = createMultiprocessDebugger(serverSocket);
117     }
118     else {
119       myDebugger = new RemoteDebugger(this, serverSocket, getConnectTimeout());
120     }
121
122     List<XBreakpointHandler> breakpointHandlers = new ArrayList<XBreakpointHandler>();
123     breakpointHandlers.add(new PyLineBreakpointHandler(this));
124     breakpointHandlers.add(new PyExceptionBreakpointHandler(this));
125     for (PyBreakpointHandlerFactory factory : Extensions.getExtensions(PyBreakpointHandlerFactory.EP_NAME)) {
126       breakpointHandlers.add(factory.createBreakpointHandler(this));
127     }
128     myBreakpointHandlers = breakpointHandlers.toArray(new XBreakpointHandler[breakpointHandlers.size()]);
129
130     myEditorsProvider = new PyDebuggerEditorsProvider();
131     mySmartStepIntoHandler = new PySmartStepIntoHandler(this);
132     myProcessHandler = processHandler;
133     myExecutionConsole = executionConsole;
134     if (myProcessHandler != null) {
135       myProcessHandler.addProcessListener(this);
136     }
137     if (processHandler instanceof PositionConverterProvider) {
138       myPositionConverter = ((PositionConverterProvider)processHandler).createPositionConverter(this);
139     }
140     else {
141       myPositionConverter = new PyLocalPositionConverter();
142     }
143     myDebugger.addCloseListener(new RemoteDebuggerCloseListener() {
144       @Override
145       public void closed() {
146         handleStop();
147       }
148
149       @Override
150       public void communicationError() {
151         detachDebuggedProcess();
152       }
153
154       @Override
155       public void detached() {
156         detachDebuggedProcess();
157       }
158     });
159
160     session.addSessionListener(new XDebugSessionAdapter() {
161       @Override
162       public void beforeSessionResume() {
163         if (session.getCurrentStackFrame() instanceof PyStackFrame) {
164           myStackFrameBeforeResume = (PyStackFrame)session.getCurrentStackFrame();
165         }
166         else {
167           myStackFrameBeforeResume = null;
168         }
169       }
170     });
171   }
172
173   private MultiProcessDebugger createMultiprocessDebugger(ServerSocket serverSocket) {
174     MultiProcessDebugger debugger = new MultiProcessDebugger(this, serverSocket, 10000);
175     debugger.addOtherDebuggerCloseListener(new MultiProcessDebugger.DebuggerProcessListener() {
176       @Override
177       public void threadsClosed(Set<String> threadIds) {
178         for (PyThreadInfo t : mySuspendedThreads) {
179           if (threadIds.contains(t.getId())) {
180             if (getSession().isSuspended()) {
181               getSession().resume();
182               break;
183             }
184           }
185         }
186       }
187     });
188     return debugger;
189   }
190
191   protected void detachDebuggedProcess() {
192     handleStop(); //in case of normal debug we stop the session
193   }
194
195   protected void handleStop() {
196     getSession().stop();
197   }
198
199   public void setPositionConverter(PyPositionConverter positionConverter) {
200     myPositionConverter = positionConverter;
201   }
202
203
204   @Override
205   public PyPositionConverter getPositionConverter() {
206     return myPositionConverter;
207   }
208
209   @NotNull
210   @Override
211   public XBreakpointHandler<?>[] getBreakpointHandlers() {
212     return myBreakpointHandlers;
213   }
214
215   @Override
216   @NotNull
217   public XDebuggerEditorsProvider getEditorsProvider() {
218     return myEditorsProvider;
219   }
220
221   @Override
222   @Nullable
223   protected ProcessHandler doGetProcessHandler() {
224     return myProcessHandler;
225   }
226
227   @Override
228   @NotNull
229   public ExecutionConsole createConsole() {
230     return myExecutionConsole;
231   }
232
233   @Override
234   public XSmartStepIntoHandler<?> getSmartStepIntoHandler() {
235     return mySmartStepIntoHandler;
236   }
237
238   @Override
239   public void sessionInitialized() {
240     waitForConnection(getConnectionMessage(), getConnectionTitle());
241   }
242
243   protected void waitForConnection(final String connectionMessage, String connectionTitle) {
244     ProgressManager.getInstance().run(new Task.Backgroundable(getSession().getProject(), connectionTitle, false) {
245       @Override
246       public void run(@NotNull final ProgressIndicator indicator) {
247         indicator.setText(connectionMessage);
248         try {
249           beforeConnect();
250           myWaitingForConnection = true;
251           myDebugger.waitForConnect();
252           myWaitingForConnection = false;
253           afterConnect();
254
255           handshake();
256           init();
257           myDebugger.run();
258         }
259         catch (final Exception e) {
260           myWaitingForConnection = false;
261           if (myProcessHandler != null) {
262             myProcessHandler.destroyProcess();
263           }
264           if (!myClosing) {
265             invokeLater(new Runnable() {
266               @Override
267               public void run() {
268                 Messages.showErrorDialog("Unable to establish connection with debugger:\n" + e.getMessage(), getConnectionTitle());
269               }
270             });
271           }
272         }
273       }
274     });
275   }
276
277   @Override
278   public void init() {
279     getSession().rebuildViews();
280     registerBreakpoints();
281   }
282
283   @Override
284   public int handleDebugPort(int localPort) throws IOException {
285     if (myProcessHandler instanceof RemoteProcessHandlerBase) {
286       return getRemoteTunneledPort(localPort, (RemoteProcessHandlerBase)myProcessHandler);
287     }
288     else {
289       return localPort;
290     }
291   }
292
293   protected static int getRemoteTunneledPort(int localPort, @NotNull RemoteProcessHandlerBase handler) throws IOException {
294     try {
295       return handler.getRemoteSocket(localPort).getSecond();
296     }
297     catch (Exception e) {
298       throw new IOException(e);
299     }
300   }
301
302   @Override
303   public void recordSignature(PySignature signature) {
304     PySignatureCacheManager.getInstance(getSession().getProject()).recordSignature(myPositionConverter.convertSignature(signature));
305   }
306
307   @Override
308   public void showConsole(PyThreadInfo thread) {
309     myConsoleContextFrame = new PyExecutionStack(this, thread).getTopFrame();
310     if (myExecutionConsole instanceof PythonDebugLanguageConsoleView) {
311       UIUtil.invokeLaterIfNeeded(new Runnable() {
312         @Override
313         public void run() {
314           ((PythonDebugLanguageConsoleView)myExecutionConsole).enableConsole(false);
315         }
316       });
317     }
318   }
319
320   protected void afterConnect() {
321   }
322
323   protected void beforeConnect() {
324   }
325
326   protected String getConnectionMessage() {
327     return "Waiting for connection...";
328   }
329
330   protected String getConnectionTitle() {
331     return "Connecting To Debugger";
332   }
333
334   private void handshake() throws PyDebuggerException {
335     String remoteVersion = myDebugger.handshake();
336     String currentBuild = ApplicationInfo.getInstance().getBuild().asStringWithoutProductCode();
337     if ("@@BUILD_NUMBER@@".equals(remoteVersion)) {
338       remoteVersion = currentBuild;
339     }
340     else if (remoteVersion.startsWith("PY-")) {
341       remoteVersion = remoteVersion.substring(3);
342     }
343     else {
344       remoteVersion = null;
345     }
346     printToConsole("Connected to pydev debugger (build " + remoteVersion + ")\n", ConsoleViewContentType.SYSTEM_OUTPUT);
347
348     if (remoteVersion != null) {
349       if (!remoteVersion.equals(currentBuild)) {
350         printToConsole("Warning: wrong debugger version. Use pycharm-debugger.egg from PyCharm installation folder.\n",
351                        ConsoleViewContentType.ERROR_OUTPUT);
352       }
353     }
354   }
355
356   @Override
357   public void printToConsole(String text, ConsoleViewContentType contentType) {
358     ((ConsoleView)myExecutionConsole).print(text, contentType);
359   }
360
361   private void registerBreakpoints() {
362     registerLineBreakpoints();
363     registerExceptionBreakpoints();
364   }
365
366   private void registerExceptionBreakpoints() {
367     for (XBreakpoint<? extends ExceptionBreakpointProperties> bp : myRegisteredExceptionBreakpoints.values()) {
368       addExceptionBreakpoint(bp);
369     }
370   }
371
372   public void registerLineBreakpoints() {
373     for (Map.Entry<PySourcePosition, XLineBreakpoint> entry : myRegisteredBreakpoints.entrySet()) {
374       addBreakpoint(entry.getKey(), entry.getValue());
375     }
376   }
377
378   @Override
379   public void startStepOver() {
380     passToCurrentThread(ResumeOrStepCommand.Mode.STEP_OVER);
381   }
382
383   @Override
384   public void startStepInto() {
385     passToCurrentThread(ResumeOrStepCommand.Mode.STEP_INTO);
386   }
387
388   public void startStepIntoMyCode() {
389     if (!checkCanPerformCommands()) return;
390     getSession().sessionResumed();
391     passToCurrentThread(ResumeOrStepCommand.Mode.STEP_INTO_MY_CODE);
392   }
393
394   @Override
395   public void startStepOut() {
396     passToCurrentThread(ResumeOrStepCommand.Mode.STEP_OUT);
397   }
398
399   public void startSmartStepInto(String functionName) {
400     dropFrameCaches();
401     if (isConnected()) {
402       for (PyThreadInfo suspendedThread : mySuspendedThreads) {
403         myDebugger.smartStepInto(suspendedThread.getId(), functionName);
404       }
405     }
406   }
407
408   @Override
409   public void stop() {
410     myDebugger.close();
411   }
412
413   @Override
414   public void resume() {
415     passToAllThreads(ResumeOrStepCommand.Mode.RESUME);
416   }
417
418   @Override
419   public void startPausing() {
420     if (isConnected()) {
421       myDebugger.suspendAllThreads();
422     }
423   }
424
425   private void passToAllThreads(final ResumeOrStepCommand.Mode mode) {
426     dropFrameCaches();
427     if (isConnected()) {
428       for (PyThreadInfo suspendedThread : Lists.newArrayList(mySuspendedThreads)) {
429         myDebugger.resumeOrStep(suspendedThread.getId(), mode);
430       }
431     }
432   }
433
434   private void passToCurrentThread(final ResumeOrStepCommand.Mode mode) {
435     dropFrameCaches();
436     if (isConnected()) {
437       String threadId = threadIdBeforeResumeOrStep();
438
439       for (PyThreadInfo suspendedThread : mySuspendedThreads) {
440         if (threadId == null || threadId.equals(suspendedThread.getId())) {
441           myDebugger.resumeOrStep(suspendedThread.getId(), mode);
442           break;
443         }
444       }
445     }
446   }
447
448   @Nullable
449   private String threadIdBeforeResumeOrStep() {
450     String threadId = null;
451     if (myStackFrameBeforeResume != null) {
452       threadId = myStackFrameBeforeResume.getThreadId();
453     }
454
455     return threadId;
456   }
457
458   protected boolean isConnected() {
459     return myDebugger.isConnected();
460   }
461
462   protected void disconnect() {
463     myDebugger.disconnect();
464     cleanUp();
465   }
466
467   public boolean isDownloadSources() {
468     return myDownloadSources;
469   }
470
471   public void setDownloadSources(boolean downloadSources) {
472     myDownloadSources = downloadSources;
473   }
474
475   protected void cleanUp() {
476     mySuspendedThreads.clear();
477     myDownloadSources = false;
478   }
479
480   @Override
481   public void runToPosition(@NotNull final XSourcePosition position) {
482     dropFrameCaches();
483     if (isConnected() && !mySuspendedThreads.isEmpty()) {
484       final PySourcePosition pyPosition = myPositionConverter.convertToPython(position);
485       String type = PyLineBreakpointType.ID;
486       AccessToken lock = ApplicationManager.getApplication().acquireReadActionLock();
487       try {
488         final Document document = FileDocumentManager.getInstance().getDocument(position.getFile());
489         if (document != null) {
490           for (XBreakpointType breakpointType : Extensions.getExtensions(XBreakpointType.EXTENSION_POINT_NAME)) {
491             if (breakpointType instanceof PyBreakpointType &&
492                 ((PyBreakpointType)breakpointType).canPutInDocument(getSession().getProject(), document)) {
493               type = breakpointType.getId();
494               break;
495             }
496           }
497         }
498       }
499       finally {
500         lock.finish();
501       }
502       myDebugger.setTempBreakpoint(type, pyPosition.getFile(), pyPosition.getLine());
503
504       passToCurrentThread(ResumeOrStepCommand.Mode.RESUME);
505     }
506   }
507
508   @Override
509   public PyDebugValue evaluate(final String expression, final boolean execute, boolean doTrunc) throws PyDebuggerException {
510     dropFrameCaches();
511     final PyStackFrame frame = currentFrame();
512     return evaluate(expression, execute, frame, doTrunc);
513   }
514
515   private PyDebugValue evaluate(String expression, boolean execute, PyStackFrame frame, boolean trimResult) throws PyDebuggerException {
516     return myDebugger.evaluate(frame.getThreadId(), frame.getFrameId(), expression, execute, trimResult);
517   }
518
519   public void consoleExec(String command, PyDebugCallback<String> callback) {
520     dropFrameCaches();
521     try {
522       final PyStackFrame frame = currentFrame();
523       myDebugger.consoleExec(frame.getThreadId(), frame.getFrameId(), command, callback);
524     }
525     catch (PyDebuggerException e) {
526       callback.error(e);
527     }
528   }
529
530   @Override
531   @Nullable
532   public XValueChildrenList loadFrame() throws PyDebuggerException {
533     final PyStackFrame frame = currentFrame();
534     //do not reload frame every time it is needed, because due to bug in pdb, reloading frame clears all variable changes
535     if (!myStackFrameCache.containsKey(frame.getThreadFrameId())) {
536       XValueChildrenList values = myDebugger.loadFrame(frame.getThreadId(), frame.getFrameId());
537       myStackFrameCache.put(frame.getThreadFrameId(), values);
538     }
539     return applyNewValue(myStackFrameCache.get(frame.getThreadFrameId()), frame.getThreadFrameId());
540   }
541
542   private XValueChildrenList applyNewValue(XValueChildrenList pyDebugValues, String threadFrameId) {
543     if (myNewVariableValue.containsKey(threadFrameId)) {
544       PyDebugValue newValue = myNewVariableValue.get(threadFrameId);
545       XValueChildrenList res = new XValueChildrenList();
546       for (int i = 0; i < pyDebugValues.size(); i++) {
547         final String name = pyDebugValues.getName(i);
548         if (name.equals(newValue.getName())) {
549           res.add(name, newValue);
550         }
551         else {
552           res.add(name, pyDebugValues.getValue(i));
553         }
554       }
555       return res;
556     }
557     else {
558       return pyDebugValues;
559     }
560   }
561
562   @Override
563   public XValueChildrenList loadVariable(final PyDebugValue var) throws PyDebuggerException {
564     final PyStackFrame frame = currentFrame();
565     return myDebugger.loadVariable(frame.getThreadId(), frame.getFrameId(), var);
566   }
567
568   @Override
569   public void loadReferrers(PyReferringObjectsValue var, PyDebugCallback<XValueChildrenList> callback) {
570     try {
571       final PyStackFrame frame = currentFrame();
572       myDebugger.loadReferrers(frame.getThreadId(), frame.getFrameId(), var, callback);
573     }
574     catch (PyDebuggerException e) {
575       callback.error(e);
576     }
577   }
578
579   @Override
580   public void changeVariable(final PyDebugValue var, final String value) throws PyDebuggerException {
581     final PyStackFrame frame = currentFrame();
582     PyDebugValue newValue = myDebugger.changeVariable(frame.getThreadId(), frame.getFrameId(), var, value);
583     myNewVariableValue.put(frame.getThreadFrameId(), newValue);
584   }
585
586   @Nullable
587   @Override
588   public PyReferrersLoader getReferrersLoader() {
589     if (myReferrersProvider == null) {
590       myReferrersProvider = new PyReferrersLoader(this);
591     }
592     return myReferrersProvider;
593   }
594
595   @Override
596   public ArrayChunk getArrayItems(PyDebugValue var, int rowOffset, int colOffset, int rows, int cols, String format)
597     throws PyDebuggerException {
598     final PyStackFrame frame = currentFrame();
599     return myDebugger.loadArrayItems(frame.getThreadId(), frame.getFrameId(), var, rowOffset, colOffset, rows, cols, format);
600   }
601
602   @Nullable
603   public String loadSource(String path) {
604     return myDebugger.loadSource(path);
605   }
606
607   @Override
608   public boolean canSaveToTemp(String name) {
609     final Project project = getSession().getProject();
610     return PyDebugSupportUtils.canSaveToTemp(project, name);
611   }
612
613   @Nullable
614   private PyStackFrame currentFrame() throws PyDebuggerException {
615     if (!isConnected()) {
616       throw new PyDebuggerException("Disconnected");
617     }
618
619     final PyStackFrame frame = (PyStackFrame)getSession().getCurrentStackFrame();
620
621     if (frame == null && myConsoleContextFrame != null) {
622       return myConsoleContextFrame;
623     }
624
625     if (frame == null) {
626       throw new PyDebuggerException("Process is running");
627     }
628
629     return frame;
630   }
631
632   private String getFunctionName(final XLineBreakpoint breakpoint) {
633     final VirtualFile file = breakpoint.getSourcePosition().getFile();
634     AccessToken lock = ApplicationManager.getApplication().acquireReadActionLock();
635     try {
636       final Document document = FileDocumentManager.getInstance().getDocument(file);
637       final Project project = getSession().getProject();
638       final String[] funcName = new String[1];
639       if (document != null) {
640         if (file.getFileType() == PythonFileType.INSTANCE) {
641           PsiElement psiElement = XDebuggerUtil.getInstance().findContextElement(file, breakpoint.getSourcePosition().getOffset(),
642                                                                                  project, false);
643           PyFunction function = PsiTreeUtil.getParentOfType(psiElement, PyFunction.class);
644           if (function != null) {
645             funcName[0] = function.getName();
646           }
647         }
648       }
649       return funcName[0];
650     }
651     finally {
652       lock.finish();
653     }
654   }
655
656   public void addBreakpoint(final PySourcePosition position, final XLineBreakpoint breakpoint) {
657     myRegisteredBreakpoints.put(position, breakpoint);
658     if (isConnected()) {
659       myDebugger.setBreakpointWithFuncName(breakpoint.getType().getId(), position.getFile(), position.getLine(),
660                                            breakpoint.getCondition(),
661                                            breakpoint.getLogExpression(),
662                                            getFunctionName(breakpoint));
663     }
664   }
665
666   public void addTemporaryBreakpoint(String typeId, String file, int line) {
667     if (isConnected()) {
668       myDebugger.setTempBreakpoint(typeId, file, line);
669     }
670   }
671
672   public void removeBreakpoint(final PySourcePosition position) {
673     XLineBreakpoint breakpoint = myRegisteredBreakpoints.get(position);
674     if (breakpoint != null) {
675       myRegisteredBreakpoints.remove(position);
676       if (isConnected()) {
677         myDebugger.removeBreakpoint(breakpoint.getType().getId(), position.getFile(), position.getLine());
678       }
679     }
680   }
681
682   public void addExceptionBreakpoint(XBreakpoint<? extends ExceptionBreakpointProperties> breakpoint) {
683     myRegisteredExceptionBreakpoints.put(breakpoint.getProperties().getException(), breakpoint);
684     if (isConnected()) {
685       myDebugger.addExceptionBreakpoint(breakpoint.getProperties());
686     }
687   }
688
689   public void removeExceptionBreakpoint(XBreakpoint<? extends ExceptionBreakpointProperties> breakpoint) {
690     myRegisteredExceptionBreakpoints.remove(breakpoint.getProperties().getException());
691     if (isConnected()) {
692       myDebugger.removeExceptionBreakpoint(breakpoint.getProperties());
693     }
694   }
695
696   public Collection<PyThreadInfo> getThreads() {
697     return myDebugger.getThreads();
698   }
699
700   @Override
701   public void threadSuspended(final PyThreadInfo threadInfo) {
702     if (!mySuspendedThreads.contains(threadInfo)) {
703       mySuspendedThreads.add(threadInfo);
704
705       final List<PyStackFrameInfo> frames = threadInfo.getFrames();
706       if (frames != null) {
707         final PySuspendContext suspendContext = createSuspendContext(threadInfo);
708
709         XBreakpoint<?> breakpoint = null;
710         if (threadInfo.isStopOnBreakpoint()) {
711           final PySourcePosition position = frames.get(0).getPosition();
712           breakpoint = myRegisteredBreakpoints.get(position);
713           if (breakpoint == null) {
714             myDebugger.removeTempBreakpoint(position.getFile(), position.getLine());
715           }
716         }
717         else if (threadInfo.isExceptionBreak()) {
718           String exceptionName = threadInfo.getMessage();
719           threadInfo.setMessage(null);
720           if (exceptionName != null) {
721             breakpoint = myRegisteredExceptionBreakpoints.get(exceptionName);
722           }
723         }
724
725         if (breakpoint != null) {
726           if (!getSession().breakpointReached(breakpoint, threadInfo.getMessage(), suspendContext)) {
727             resume();
728           }
729         }
730         else {
731           getSession().positionReached(suspendContext);
732         }
733       }
734     }
735   }
736
737   @NotNull
738   protected PySuspendContext createSuspendContext(PyThreadInfo threadInfo) {
739     return new PySuspendContext(this, threadInfo);
740   }
741
742   @Override
743   public void threadResumed(final PyThreadInfo threadInfo) {
744     mySuspendedThreads.remove(threadInfo);
745   }
746
747   private void dropFrameCaches() {
748     myStackFrameCache.clear();
749     myNewVariableValue.clear();
750   }
751
752   @NotNull
753   public List<PydevCompletionVariant> getCompletions(String prefix) throws Exception {
754     if (isConnected()) {
755       dropFrameCaches();
756       final PyStackFrame frame = currentFrame();
757       return myDebugger.getCompletions(frame.getThreadId(), frame.getFrameId(), prefix);
758     }
759     return Lists.newArrayList();
760   }
761
762   @Override
763   public void startNotified(ProcessEvent event) {
764   }
765
766   @Override
767   public void processTerminated(ProcessEvent event) {
768     myDebugger.close();
769   }
770
771   @Override
772   public void processWillTerminate(ProcessEvent event, boolean willBeDestroyed) {
773     myClosing = true;
774     setKillingStrategy();
775   }
776
777   private void setKillingStrategy() {
778     if (myProcessHandler instanceof PythonProcessHandler) {
779       ((PythonProcessHandler)myProcessHandler).setShouldKillProcessSoftly(false);
780       //while process is suspended it can't terminate softly,
781       //multiple processes in debug mode also can not terminate properly
782       //so its better to kill all the tree in a hard way
783     }
784   }
785
786   @Override
787   public void onTextAvailable(ProcessEvent event, Key outputType) {
788   }
789
790   public PyStackFrame createStackFrame(PyStackFrameInfo frameInfo) {
791     return new PyStackFrame(getSession().getProject(), this, frameInfo,
792                             getPositionConverter().convertFromPython(frameInfo.getPosition()));
793   }
794
795   @Override
796   public String getCurrentStateMessage() {
797     if (getSession().isStopped()) {
798       return XDebuggerBundle.message("debugger.state.message.disconnected");
799     }
800     else if (isConnected()) {
801       return XDebuggerBundle.message("debugger.state.message.connected");
802     }
803     else {
804       return getConnectionMessage();
805     }
806   }
807
808   public void addProcessListener(ProcessListener listener) {
809     ProcessHandler handler = doGetProcessHandler();
810     if (handler != null) {
811       handler.addProcessListener(listener);
812     }
813   }
814
815   public boolean isWaitingForConnection() {
816     return myWaitingForConnection;
817   }
818
819   public void setWaitingForConnection(boolean waitingForConnection) {
820     myWaitingForConnection = waitingForConnection;
821   }
822
823   public int getConnectTimeout() {
824     return CONNECTION_TIMEOUT;
825   }
826
827
828   @Nullable
829   private XSourcePosition getCurrentFrameSourcePosition() {
830     try {
831       PyStackFrame frame = currentFrame();
832
833       return frame != null ? frame.getSourcePosition() : null;
834     }
835     catch (PyDebuggerException e) {
836       return null;
837     }
838   }
839
840   public Project getProject() {
841     return getSession().getProject();
842   }
843
844   @Nullable
845   @Override
846   public XSourcePosition getSourcePositionForName(String name) {
847     XSourcePosition currentPosition = getCurrentFrameSourcePosition();
848
849     final PsiFile file = getPsiFile(currentPosition);
850
851     if (file == null) return null;
852
853     PsiElement currentElement = file.findElementAt(currentPosition.getOffset());
854
855     if (currentElement == null) {
856       return null;
857     }
858
859     final Ref<PsiElement> elementRef = Ref.create();
860
861     PyResolveUtil.scopeCrawlUp(new PsiScopeProcessor() {
862       @Override
863       public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
864         if (!(element instanceof PyImportElement)) {
865           if (elementRef.isNull()) {
866             elementRef.set(element);
867           }
868         }
869         return false;
870       }
871
872       @Nullable
873       @Override
874       public <T> T getHint(@NotNull Key<T> hintKey) {
875         return null;
876       }
877
878       @Override
879       public void handleEvent(@NotNull Event event, @Nullable Object associated) {
880
881       }
882     }, currentElement, name, null);
883
884     return elementRef.isNull() ? null
885                                : XSourcePositionImpl.createByElement(elementRef.get());
886   }
887
888   @Nullable
889   private PsiFile getPsiFile(XSourcePosition currentPosition) {
890     if (currentPosition == null) {
891       return null;
892     }
893
894     return PsiManager.getInstance(getProject()).findFile(currentPosition.getFile());
895   }
896
897
898   @Nullable
899   @Override
900   public XSourcePosition getSourcePositionForType(String typeName) {
901     XSourcePosition currentPosition = getCurrentFrameSourcePosition();
902
903     final PsiFile file = getPsiFile(currentPosition);
904
905     if (file == null) return null;
906
907
908     PyType type = PyTypeParser.getTypeByName(file, typeName);
909
910     if (type instanceof PyClassType) {
911       return XSourcePositionImpl.createByElement(((PyClassType)type).getPyClass());
912     }
913
914     return null;
915   }
916 }