1# -*- coding: utf-8 -*-
2
3# Copyright (c) 2002 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4#
5
6"""
7Module implementing the debugger UI.
8"""
9
10import os
11import copy
12import contextlib
13
14from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, Qt
15from PyQt5.QtGui import QKeySequence
16from PyQt5.QtWidgets import QMenu, QToolBar, QApplication, QDialog
17
18from E5Gui.E5Action import E5Action, createActionGroup
19from E5Gui import E5MessageBox
20
21from UI.Info import Program
22from UI.NotificationWidget import NotificationTypes
23
24from .DebugClientCapabilities import (
25    HasDebugger, HasInterpreter, HasProfiler, HasCoverage
26)
27
28import Preferences
29import Utilities
30import UI.PixmapCache
31import UI.Config
32
33from eric6config import getConfig
34
35
36class DebugUI(QObject):
37    """
38    Class implementing the debugger part of the UI.
39
40    @signal clientStack(stack, debuggerId) emitted at breaking after a reported
41        exception
42    @signal compileForms() emitted if changed project forms should be compiled
43    @signal compileResources() emitted if changed project resources should be
44        compiled
45    @signal executeMake() emitted if a project specific make run should be
46        performed
47    @signal debuggingStarted(filename) emitted when a debugging session was
48        started
49    @signal resetUI(full) emitted to reset the UI partially or fully
50    @signal exceptionInterrupt() emitted after the execution was interrupted
51        by an exception and acknowledged by the user
52    @signal appendStdout(msg) emitted when the client program has terminated
53        and the display of the termination dialog is suppressed
54    """
55    clientStack = pyqtSignal(list, str)
56    resetUI = pyqtSignal(bool)
57    exceptionInterrupt = pyqtSignal()
58    compileForms = pyqtSignal()
59    compileResources = pyqtSignal()
60    executeMake = pyqtSignal()
61    debuggingStarted = pyqtSignal(str)
62    appendStdout = pyqtSignal(str)
63
64    def __init__(self, ui, vm, debugServer, debugViewer, project):
65        """
66        Constructor
67
68        @param ui reference to the main UI
69        @param vm reference to the viewmanager
70        @param debugServer reference to the debug server
71        @param debugViewer reference to the debug viewer widget
72        @param project reference to the project object
73        """
74        super().__init__(ui)
75
76        self.ui = ui
77        self.viewmanager = vm
78        self.debugServer = debugServer
79        self.debugViewer = debugViewer
80        self.project = project
81
82        # Clear some variables
83        self.projectOpen = False
84        self.editorOpen = False
85
86        # read the saved debug info values
87        self.lastUsedVenvName = Preferences.Prefs.settings.value(
88            'DebugInfo/VirtualEnvironment', '')
89        self.argvHistory = Preferences.toList(
90            Preferences.Prefs.settings.value('DebugInfo/ArgumentsHistory'))
91        self.wdHistory = Preferences.toList(
92            Preferences.Prefs.settings.value(
93                'DebugInfo/WorkingDirectoryHistory'))
94        self.envHistory = Preferences.toList(
95            Preferences.Prefs.settings.value('DebugInfo/EnvironmentHistory'))
96        self.excList = Preferences.toList(
97            Preferences.Prefs.settings.value('DebugInfo/Exceptions'))
98        self.excIgnoreList = Preferences.toList(
99            Preferences.Prefs.settings.value('DebugInfo/IgnoredExceptions'))
100        self.exceptions = Preferences.toBool(
101            Preferences.Prefs.settings.value(
102                'DebugInfo/ReportExceptions', True))
103        self.autoClearShell = Preferences.toBool(
104            Preferences.Prefs.settings.value('DebugInfo/AutoClearShell', True))
105        self.tracePython = Preferences.toBool(
106            Preferences.Prefs.settings.value('DebugInfo/TracePython', False))
107        self.autoContinue = Preferences.toBool(
108            Preferences.Prefs.settings.value('DebugInfo/AutoContinue', True))
109        self.enableMultiprocess = Preferences.toBool(
110            Preferences.Prefs.settings.value(
111                'DebugInfo/EnableMultiprocess', False))
112        self.multiprocessNoDebugHistory = Preferences.toList(
113            Preferences.Prefs.settings.value(
114                'DebugInfo/MultiprocessNoDebugHistory'))
115        self.overrideGlobalConfig = {
116            "enable": Preferences.toBool(Preferences.Prefs.settings.value(
117                'DebugInfo/OverrideGlobal', False)),
118            "redirect": Preferences.toBool(Preferences.Prefs.settings.value(
119                'DebugInfo/RedirectStdinStdout', True)),
120        }
121
122        self.lastDebuggedFile = None
123        self.lastStartAction = 0    # 0=None, 1=Script, 2=Project
124        self.clientType = ""
125        self.lastAction = -1
126        self.debugActions = [
127            self.__continue, self.__step, self.__stepOver, self.__stepOut,
128            self.__stepQuit, self.__runToCursor, self.__runUntil,
129            self.__moveInstructionPointer
130        ]
131        self.__localsVarFilterList, self.__globalsVarFilterList = (
132            Preferences.getVarFilters())
133        self.debugViewer.setVariablesFilter(
134            self.__globalsVarFilterList, self.__localsVarFilterList)
135
136        self.__clientDebuggerIds = set()
137
138        # Connect the signals emitted by the debug-server
139        debugServer.clientGone.connect(self.__clientGone)
140        debugServer.clientLine.connect(self.__clientLine)
141        debugServer.clientDisconnected.connect(self.__clientDisconnected)
142        debugServer.clientExit.connect(self.__clientExit)
143        debugServer.lastClientExited.connect(self.__lastClientExited)
144        debugServer.clientSyntaxError.connect(self.__clientSyntaxError)
145        debugServer.clientException.connect(self.__clientException)
146        debugServer.clientSignal.connect(self.__clientSignal)
147        debugServer.clientVariables.connect(self.__clientVariables)
148        debugServer.clientVariable.connect(self.__clientVariable)
149        debugServer.clientBreakConditionError.connect(
150            self.__clientBreakConditionError)
151        debugServer.clientWatchConditionError.connect(
152            self.__clientWatchConditionError)
153        debugServer.passiveDebugStarted.connect(self.__passiveDebugStarted)
154        debugServer.clientThreadSet.connect(self.__clientThreadSet)
155        debugServer.clientDebuggerId.connect(self.__clientDebuggerId)
156
157        # Connect the signals emitted by the viewmanager
158        vm.editorOpened.connect(self.__editorOpened)
159        vm.lastEditorClosed.connect(self.__lastEditorClosed)
160        vm.checkActions.connect(self.__checkActions)
161        vm.cursorChanged.connect(self.__cursorChanged)
162        vm.breakpointToggled.connect(self.__cursorChanged)
163
164        # Connect the signals emitted by the project
165        project.projectOpened.connect(self.__projectOpened)
166        project.newProject.connect(self.__projectOpened)
167        project.projectClosed.connect(self.__projectClosed)
168
169        # Set a flag for the passive debug mode
170        self.passive = Preferences.getDebugger("PassiveDbgEnabled")
171
172    def showNotification(self, notification,
173                         kind=NotificationTypes.INFORMATION, timeout=None):
174        """
175        Public method to show some notification message.
176
177        @param notification message to be shown
178        @type str
179        @param kind kind of notification to be shown
180        @type NotificationTypes
181        @param timeout timeout for the notification (None = use configured
182            default, 0 = indefinitely)
183        @type int
184        """
185        self.ui.showNotification(
186            UI.PixmapCache.getPixmap("debug48"),
187            self.tr("Notification"), notification, kind=kind, timeout=timeout)
188
189    def variablesFilter(self, scope):
190        """
191        Public method to get the variables filter for a scope.
192
193        @param scope flag indicating global (True) or local (False) scope
194        @return filters list
195        @rtype list of str
196        """
197        if scope:
198            return self.__globalsVarFilterList[:]
199        else:
200            return self.__localsVarFilterList[:]
201
202    def initActions(self):
203        """
204        Public method defining the user interface actions.
205        """
206        self.actions = []
207
208        self.runAct = E5Action(
209            self.tr('Run Script'),
210            UI.PixmapCache.getIcon("runScript"),
211            self.tr('&Run Script...'),
212            Qt.Key.Key_F2, 0, self, 'dbg_run_script')
213        self.runAct.setStatusTip(self.tr('Run the current Script'))
214        self.runAct.setWhatsThis(self.tr(
215            """<b>Run Script</b>"""
216            """<p>Set the command line arguments and run the script outside"""
217            """ the debugger. If the file has unsaved changes it may be"""
218            """ saved first.</p>"""
219        ))
220        self.runAct.triggered.connect(self.__runScript)
221        self.actions.append(self.runAct)
222
223        self.runProjectAct = E5Action(
224            self.tr('Run Project'),
225            UI.PixmapCache.getIcon("runProject"),
226            self.tr('Run &Project...'),
227            Qt.Modifier.SHIFT + Qt.Key.Key_F2,
228            0, self, 'dbg_run_project')
229        self.runProjectAct.setStatusTip(self.tr('Run the current Project'))
230        self.runProjectAct.setWhatsThis(self.tr(
231            """<b>Run Project</b>"""
232            """<p>Set the command line arguments and run the current project"""
233            """ outside the debugger."""
234            """ If files of the current project have unsaved changes they"""
235            """ may be saved first.</p>"""
236        ))
237        self.runProjectAct.triggered.connect(self.__runProject)
238        self.actions.append(self.runProjectAct)
239
240        self.coverageAct = E5Action(
241            self.tr('Coverage run of Script'),
242            UI.PixmapCache.getIcon("coverageScript"),
243            self.tr('Coverage run of Script...'), 0, 0, self,
244            'dbg_coverage_script')
245        self.coverageAct.setStatusTip(
246            self.tr('Perform a coverage run of the current Script'))
247        self.coverageAct.setWhatsThis(self.tr(
248            """<b>Coverage run of Script</b>"""
249            """<p>Set the command line arguments and run the script under"""
250            """ the control of a coverage analysis tool. If the file has"""
251            """ unsaved changes it may be saved first.</p>"""
252        ))
253        self.coverageAct.triggered.connect(self.__coverageScript)
254        self.actions.append(self.coverageAct)
255
256        self.coverageProjectAct = E5Action(
257            self.tr('Coverage run of Project'),
258            UI.PixmapCache.getIcon("coverageProject"),
259            self.tr('Coverage run of Project...'), 0, 0, self,
260            'dbg_coverage_project')
261        self.coverageProjectAct.setStatusTip(
262            self.tr('Perform a coverage run of the current Project'))
263        self.coverageProjectAct.setWhatsThis(self.tr(
264            """<b>Coverage run of Project</b>"""
265            """<p>Set the command line arguments and run the current project"""
266            """ under the control of a coverage analysis tool."""
267            """ If files of the current project have unsaved changes"""
268            """ they may be saved first.</p>"""
269        ))
270        self.coverageProjectAct.triggered.connect(self.__coverageProject)
271        self.actions.append(self.coverageProjectAct)
272
273        self.profileAct = E5Action(
274            self.tr('Profile Script'),
275            UI.PixmapCache.getIcon("profileScript"),
276            self.tr('Profile Script...'), 0, 0, self, 'dbg_profile_script')
277        self.profileAct.setStatusTip(self.tr('Profile the current Script'))
278        self.profileAct.setWhatsThis(self.tr(
279            """<b>Profile Script</b>"""
280            """<p>Set the command line arguments and profile the script."""
281            """ If the file has unsaved changes it may be saved first.</p>"""
282        ))
283        self.profileAct.triggered.connect(self.__profileScript)
284        self.actions.append(self.profileAct)
285
286        self.profileProjectAct = E5Action(
287            self.tr('Profile Project'),
288            UI.PixmapCache.getIcon("profileProject"),
289            self.tr('Profile Project...'), 0, 0, self,
290            'dbg_profile_project')
291        self.profileProjectAct.setStatusTip(
292            self.tr('Profile the current Project'))
293        self.profileProjectAct.setWhatsThis(self.tr(
294            """<b>Profile Project</b>"""
295            """<p>Set the command line arguments and profile the current"""
296            """ project. If files of the current project have unsaved"""
297            """ changes they may be saved first.</p>"""
298        ))
299        self.profileProjectAct.triggered.connect(self.__profileProject)
300        self.actions.append(self.profileProjectAct)
301
302        self.debugAct = E5Action(
303            self.tr('Debug Script'),
304            UI.PixmapCache.getIcon("debugScript"),
305            self.tr('&Debug Script...'), Qt.Key.Key_F5, 0, self,
306            'dbg_debug_script')
307        self.debugAct.setStatusTip(self.tr('Debug the current Script'))
308        self.debugAct.setWhatsThis(self.tr(
309            """<b>Debug Script</b>"""
310            """<p>Set the command line arguments and set the current line"""
311            """ to be the first executable Python statement of the current"""
312            """ editor window. If the file has unsaved changes it may be"""
313            """ saved first.</p>"""
314        ))
315        self.debugAct.triggered.connect(self.__debugScript)
316        self.actions.append(self.debugAct)
317
318        self.debugProjectAct = E5Action(
319            self.tr('Debug Project'),
320            UI.PixmapCache.getIcon("debugProject"),
321            self.tr('Debug &Project...'),
322            Qt.Modifier.SHIFT + Qt.Key.Key_F5,
323            0, self, 'dbg_debug_project')
324        self.debugProjectAct.setStatusTip(self.tr(
325            'Debug the current Project'))
326        self.debugProjectAct.setWhatsThis(self.tr(
327            """<b>Debug Project</b>"""
328            """<p>Set the command line arguments and set the current line"""
329            """ to be the first executable Python statement of the main"""
330            """ script of the current project. If files of the current"""
331            """ project have unsaved changes they may be saved first.</p>"""
332        ))
333        self.debugProjectAct.triggered.connect(self.__debugProject)
334        self.actions.append(self.debugProjectAct)
335
336        self.restartAct = E5Action(
337            self.tr('Restart'),
338            UI.PixmapCache.getIcon("debugRestart"),
339            self.tr('Restart'), Qt.Key.Key_F4, 0, self, 'dbg_restart_script')
340        self.restartAct.setStatusTip(self.tr(
341            'Restart the last debugged script'))
342        self.restartAct.setWhatsThis(self.tr(
343            """<b>Restart</b>"""
344            """<p>Set the command line arguments and set the current line"""
345            """ to be the first executable Python statement of the script"""
346            """ that was debugged last. If there are unsaved changes, they"""
347            """ may be saved first.</p>"""
348        ))
349        self.restartAct.triggered.connect(self.__doRestart)
350        self.actions.append(self.restartAct)
351
352        self.stopAct = E5Action(
353            self.tr('Stop'),
354            UI.PixmapCache.getIcon("stopScript"),
355            self.tr('Stop'), Qt.Modifier.SHIFT + Qt.Key.Key_F10, 0,
356            self, 'dbg_stop_script')
357        self.stopAct.setStatusTip(self.tr("""Stop the running script."""))
358        self.stopAct.setWhatsThis(self.tr(
359            """<b>Stop</b>"""
360            """<p>This stops the script running in the debugger backend.</p>"""
361        ))
362        self.stopAct.triggered.connect(self.__stopScript)
363        self.actions.append(self.stopAct)
364
365        self.debugActGrp = createActionGroup(self)
366
367        act = E5Action(
368            self.tr('Continue'),
369            UI.PixmapCache.getIcon("continue"),
370            self.tr('&Continue'), Qt.Key.Key_F6, 0,
371            self.debugActGrp, 'dbg_continue')
372        act.setStatusTip(
373            self.tr('Continue running the program from the current line'))
374        act.setWhatsThis(self.tr(
375            """<b>Continue</b>"""
376            """<p>Continue running the program from the current line. The"""
377            """ program will stop when it terminates or when a breakpoint"""
378            """ is reached.</p>"""
379        ))
380        act.triggered.connect(self.__continue)
381        self.actions.append(act)
382
383        act = E5Action(
384            self.tr('Continue to Cursor'),
385            UI.PixmapCache.getIcon("continueToCursor"),
386            self.tr('Continue &To Cursor'),
387            Qt.Modifier.SHIFT + Qt.Key.Key_F6,
388            0, self.debugActGrp, 'dbg_continue_to_cursor')
389        act.setStatusTip(self.tr(
390            """Continue running the program from the"""
391            """ current line to the current cursor position"""))
392        act.setWhatsThis(self.tr(
393            """<b>Continue To Cursor</b>"""
394            """<p>Continue running the program from the current line to the"""
395            """ current cursor position.</p>"""
396        ))
397        act.triggered.connect(self.__runToCursor)
398        self.actions.append(act)
399
400        act = E5Action(
401            self.tr('Continue Until'),
402            UI.PixmapCache.getIcon("continueUntil"),
403            self.tr('Continue &Until'), Qt.Modifier.CTRL + Qt.Key.Key_F6, 0,
404            self.debugActGrp, 'dbg_continue_until')
405        act.setStatusTip(self.tr(
406            """Continue running the program from the current line to the"""
407            """ current cursor position or until leaving the current frame"""))
408        act.setWhatsThis(self.tr(
409            """<b>Continue Until</b>"""
410            """<p>Continue running the program from the current line to the"""
411            """ cursor position greater than the current line or until"""
412            """ leaving the current frame.</p>"""
413        ))
414        act.triggered.connect(self.__runUntil)
415        self.actions.append(act)
416
417        act = E5Action(
418            self.tr('Move Instruction Pointer to Cursor'),
419            UI.PixmapCache.getIcon("moveInstructionPointer"),
420            self.tr('&Jump To Cursor'), Qt.Key.Key_F12, 0,
421            self.debugActGrp, 'dbg_jump_to_cursor')
422        act.setStatusTip(self.tr(
423            """Skip the code from the"""
424            """ current line to the current cursor position"""))
425        act.setWhatsThis(self.tr(
426            """<b>Move Instruction Pointer to Cursor</b>"""
427            """<p>Move the Python internal instruction pointer to the"""
428            """ current cursor position without executing the code in"""
429            """ between.</p>"""
430            """<p>It's not possible to jump out of a function or jump"""
431            """ in a code block, e.g. a loop. In these cases, a error"""
432            """ message is printed to the log window.</p>"""
433        ))
434        act.triggered.connect(self.__moveInstructionPointer)
435        self.actions.append(act)
436
437        act = E5Action(
438            self.tr('Single Step'),
439            UI.PixmapCache.getIcon("step"),
440            self.tr('Sin&gle Step'), Qt.Key.Key_F7, 0,
441            self.debugActGrp, 'dbg_single_step')
442        act.setStatusTip(self.tr('Execute a single Python statement'))
443        act.setWhatsThis(self.tr(
444            """<b>Single Step</b>"""
445            """<p>Execute a single Python statement. If the statement"""
446            """ is an <tt>import</tt> statement, a class constructor, or a"""
447            """ method or function call then control is returned to the"""
448            """ debugger at the next statement.</p>"""
449        ))
450        act.triggered.connect(self.__step)
451        self.actions.append(act)
452
453        act = E5Action(
454            self.tr('Step Over'),
455            UI.PixmapCache.getIcon("stepOver"),
456            self.tr('Step &Over'), Qt.Key.Key_F8, 0,
457            self.debugActGrp, 'dbg_step_over')
458        act.setStatusTip(self.tr(
459            """Execute a single Python statement staying"""
460            """ in the current frame"""))
461        act.setWhatsThis(self.tr(
462            """<b>Step Over</b>"""
463            """<p>Execute a single Python statement staying in the same"""
464            """ frame. If the statement is an <tt>import</tt> statement,"""
465            """ a class constructor, or a method or function call then"""
466            """ control is returned to the debugger after the statement"""
467            """ has completed.</p>"""
468        ))
469        act.triggered.connect(self.__stepOver)
470        self.actions.append(act)
471
472        act = E5Action(
473            self.tr('Step Out'),
474            UI.PixmapCache.getIcon("stepOut"),
475            self.tr('Step Ou&t'), Qt.Key.Key_F9, 0,
476            self.debugActGrp, 'dbg_step_out')
477        act.setStatusTip(self.tr(
478            """Execute Python statements until leaving"""
479            """ the current frame"""))
480        act.setWhatsThis(self.tr(
481            """<b>Step Out</b>"""
482            """<p>Execute Python statements until leaving the current"""
483            """ frame. If the statements are inside an <tt>import</tt>"""
484            """ statement, a class constructor, or a method or function"""
485            """ call then control is returned to the debugger after the"""
486            """ current frame has been left.</p>"""
487        ))
488        act.triggered.connect(self.__stepOut)
489        self.actions.append(act)
490
491        act = E5Action(
492            self.tr('Stop'),
493            UI.PixmapCache.getIcon("stepQuit"),
494            self.tr('&Stop'), Qt.Key.Key_F10, 0,
495            self.debugActGrp, 'dbg_stop')
496        act.setStatusTip(self.tr('Stop debugging'))
497        act.setWhatsThis(self.tr(
498            """<b>Stop</b>"""
499            """<p>Stop the running debugging session.</p>"""
500        ))
501        act.triggered.connect(self.__stepQuit)
502        self.actions.append(act)
503
504        self.dbgFilterAct = E5Action(
505            self.tr('Variables Type Filter'),
506            self.tr('Varia&bles Type Filter...'), 0, 0, self,
507            'dbg_variables_filter')
508        self.dbgFilterAct.setStatusTip(self.tr(
509            'Configure variables type filter'))
510        self.dbgFilterAct.setWhatsThis(self.tr(
511            """<b>Variables Type Filter</b>"""
512            """<p>Configure the variables type filter. Only variable types"""
513            """ that are not selected are displayed in the global or local"""
514            """ variables window during a debugging session.</p>"""
515        ))
516        self.dbgFilterAct.triggered.connect(
517            self.__configureVariablesFilters)
518        self.actions.append(self.dbgFilterAct)
519
520        self.excFilterAct = E5Action(
521            self.tr('Exceptions Filter'),
522            self.tr('&Exceptions Filter...'), 0, 0, self,
523            'dbg_exceptions_filter')
524        self.excFilterAct.setStatusTip(self.tr(
525            'Configure exceptions filter'))
526        self.excFilterAct.setWhatsThis(self.tr(
527            """<b>Exceptions Filter</b>"""
528            """<p>Configure the exceptions filter. Only exception types"""
529            """ that are listed are highlighted during a debugging"""
530            """ session.</p><p>Please note, that all unhandled exceptions"""
531            """ are highlighted indepent from the filter list.</p>"""
532        ))
533        self.excFilterAct.triggered.connect(
534            self.__configureExceptionsFilter)
535        self.actions.append(self.excFilterAct)
536
537        self.excIgnoreFilterAct = E5Action(
538            self.tr('Ignored Exceptions'),
539            self.tr('&Ignored Exceptions...'), 0, 0,
540            self, 'dbg_ignored_exceptions')
541        self.excIgnoreFilterAct.setStatusTip(self.tr(
542            'Configure ignored exceptions'))
543        self.excIgnoreFilterAct.setWhatsThis(self.tr(
544            """<b>Ignored Exceptions</b>"""
545            """<p>Configure the ignored exceptions. Only exception types"""
546            """ that are not listed are highlighted during a debugging"""
547            """ session.</p><p>Please note, that unhandled exceptions"""
548            """ cannot be ignored.</p>"""
549        ))
550        self.excIgnoreFilterAct.triggered.connect(
551            self.__configureIgnoredExceptions)
552        self.actions.append(self.excIgnoreFilterAct)
553
554        self.dbgSetBpActGrp = createActionGroup(self)
555
556        self.dbgToggleBpAct = E5Action(
557            self.tr('Toggle Breakpoint'),
558            UI.PixmapCache.getIcon("breakpointToggle"),
559            self.tr('Toggle Breakpoint'),
560            QKeySequence(self.tr("Shift+F11", "Debug|Toggle Breakpoint")),
561            0, self.dbgSetBpActGrp, 'dbg_toggle_breakpoint')
562        self.dbgToggleBpAct.setStatusTip(self.tr('Toggle Breakpoint'))
563        self.dbgToggleBpAct.setWhatsThis(self.tr(
564            """<b>Toggle Breakpoint</b>"""
565            """<p>Toggles a breakpoint at the current line of the"""
566            """ current editor.</p>"""
567        ))
568        self.dbgToggleBpAct.triggered.connect(self.__toggleBreakpoint)
569        self.actions.append(self.dbgToggleBpAct)
570
571        self.dbgEditBpAct = E5Action(
572            self.tr('Edit Breakpoint'),
573            UI.PixmapCache.getIcon("cBreakpointToggle"),
574            self.tr('Edit Breakpoint...'),
575            QKeySequence(self.tr("Shift+F12", "Debug|Edit Breakpoint")), 0,
576            self.dbgSetBpActGrp, 'dbg_edit_breakpoint')
577        self.dbgEditBpAct.setStatusTip(self.tr('Edit Breakpoint'))
578        self.dbgEditBpAct.setWhatsThis(self.tr(
579            """<b>Edit Breakpoint</b>"""
580            """<p>Opens a dialog to edit the breakpoints properties."""
581            """ It works at the current line of the current editor.</p>"""
582        ))
583        self.dbgEditBpAct.triggered.connect(self.__editBreakpoint)
584        self.actions.append(self.dbgEditBpAct)
585
586        self.dbgNextBpAct = E5Action(
587            self.tr('Next Breakpoint'),
588            UI.PixmapCache.getIcon("breakpointNext"),
589            self.tr('Next Breakpoint'),
590            QKeySequence(
591                self.tr("Ctrl+Shift+PgDown", "Debug|Next Breakpoint")), 0,
592            self.dbgSetBpActGrp, 'dbg_next_breakpoint')
593        self.dbgNextBpAct.setStatusTip(self.tr('Next Breakpoint'))
594        self.dbgNextBpAct.setWhatsThis(self.tr(
595            """<b>Next Breakpoint</b>"""
596            """<p>Go to next breakpoint of the current editor.</p>"""
597        ))
598        self.dbgNextBpAct.triggered.connect(self.__nextBreakpoint)
599        self.actions.append(self.dbgNextBpAct)
600
601        self.dbgPrevBpAct = E5Action(
602            self.tr('Previous Breakpoint'),
603            UI.PixmapCache.getIcon("breakpointPrevious"),
604            self.tr('Previous Breakpoint'),
605            QKeySequence(
606                self.tr("Ctrl+Shift+PgUp", "Debug|Previous Breakpoint")),
607            0, self.dbgSetBpActGrp, 'dbg_previous_breakpoint')
608        self.dbgPrevBpAct.setStatusTip(self.tr('Previous Breakpoint'))
609        self.dbgPrevBpAct.setWhatsThis(self.tr(
610            """<b>Previous Breakpoint</b>"""
611            """<p>Go to previous breakpoint of the current editor.</p>"""
612        ))
613        self.dbgPrevBpAct.triggered.connect(self.__previousBreakpoint)
614        self.actions.append(self.dbgPrevBpAct)
615
616        act = E5Action(
617            self.tr('Clear Breakpoints'),
618            self.tr('Clear Breakpoints'),
619            0, 0,
620            self.dbgSetBpActGrp, 'dbg_clear_breakpoint')
621        act.setStatusTip(self.tr('Clear Breakpoints'))
622        act.setWhatsThis(self.tr(
623            """<b>Clear Breakpoints</b>"""
624            """<p>Clear breakpoints of all editors.</p>"""
625        ))
626        act.triggered.connect(self.__clearBreakpoints)
627        self.actions.append(act)
628
629        self.debugActGrp.setEnabled(False)
630        self.dbgSetBpActGrp.setEnabled(False)
631        self.runAct.setEnabled(False)
632        self.runProjectAct.setEnabled(False)
633        self.profileAct.setEnabled(False)
634        self.profileProjectAct.setEnabled(False)
635        self.coverageAct.setEnabled(False)
636        self.coverageProjectAct.setEnabled(False)
637        self.debugAct.setEnabled(False)
638        self.debugProjectAct.setEnabled(False)
639        self.restartAct.setEnabled(False)
640        self.stopAct.setEnabled(False)
641
642    def initMenus(self):
643        """
644        Public slot to initialize the project menu.
645
646        @return the generated menu
647        """
648        dmenu = QMenu(self.tr('&Debug'), self.parent())
649        dmenu.setTearOffEnabled(True)
650        smenu = QMenu(self.tr('Sta&rt'), self.parent())
651        smenu.setTearOffEnabled(True)
652        self.breakpointsMenu = QMenu(self.tr('&Breakpoints'), dmenu)
653
654        smenu.addAction(self.restartAct)
655        smenu.addAction(self.stopAct)
656        smenu.addSeparator()
657        smenu.addAction(self.runAct)
658        smenu.addAction(self.runProjectAct)
659        smenu.addSeparator()
660        smenu.addAction(self.debugAct)
661        smenu.addAction(self.debugProjectAct)
662        smenu.addSeparator()
663        smenu.addAction(self.profileAct)
664        smenu.addAction(self.profileProjectAct)
665        smenu.addSeparator()
666        smenu.addAction(self.coverageAct)
667        smenu.addAction(self.coverageProjectAct)
668
669        dmenu.addActions(self.debugActGrp.actions())
670        dmenu.addSeparator()
671        dmenu.addActions(self.dbgSetBpActGrp.actions())
672        self.menuBreakpointsAct = dmenu.addMenu(self.breakpointsMenu)
673        dmenu.addSeparator()
674        dmenu.addAction(self.dbgFilterAct)
675        dmenu.addAction(self.excFilterAct)
676        dmenu.addAction(self.excIgnoreFilterAct)
677
678        self.breakpointsMenu.aboutToShow.connect(self.__showBreakpointsMenu)
679        self.breakpointsMenu.triggered.connect(self.__breakpointSelected)
680        dmenu.aboutToShow.connect(self.__showDebugMenu)
681
682        return smenu, dmenu
683
684    def initToolbars(self, toolbarManager):
685        """
686        Public slot to initialize the debug toolbars.
687
688        @param toolbarManager reference to a toolbar manager object
689            (E5ToolBarManager)
690        @return the generated toolbars (list of QToolBar)
691        """
692        starttb = QToolBar(self.tr("Start"), self.ui)
693        starttb.setIconSize(UI.Config.ToolBarIconSize)
694        starttb.setObjectName("StartToolbar")
695        starttb.setToolTip(self.tr('Start'))
696
697        starttb.addAction(self.restartAct)
698        starttb.addAction(self.stopAct)
699        starttb.addSeparator()
700        starttb.addAction(self.runAct)
701        starttb.addAction(self.runProjectAct)
702        starttb.addSeparator()
703        starttb.addAction(self.debugAct)
704        starttb.addAction(self.debugProjectAct)
705
706        debugtb = QToolBar(self.tr("Debug"), self.ui)
707        debugtb.setIconSize(UI.Config.ToolBarIconSize)
708        debugtb.setObjectName("DebugToolbar")
709        debugtb.setToolTip(self.tr('Debug'))
710
711        debugtb.addActions(self.debugActGrp.actions())
712        debugtb.addSeparator()
713        debugtb.addAction(self.dbgToggleBpAct)
714        debugtb.addAction(self.dbgEditBpAct)
715        debugtb.addAction(self.dbgNextBpAct)
716        debugtb.addAction(self.dbgPrevBpAct)
717
718        toolbarManager.addToolBar(starttb, starttb.windowTitle())
719        toolbarManager.addToolBar(debugtb, debugtb.windowTitle())
720        toolbarManager.addAction(self.profileAct, starttb.windowTitle())
721        toolbarManager.addAction(self.profileProjectAct, starttb.windowTitle())
722        toolbarManager.addAction(self.coverageAct, starttb.windowTitle())
723        toolbarManager.addAction(self.coverageProjectAct,
724                                 starttb.windowTitle())
725
726        return [starttb, debugtb]
727
728    def setArgvHistory(self, argsStr, clearHistories=False, history=None):
729        """
730        Public slot to initialize the argv history.
731
732        @param argsStr the commandline arguments (string)
733        @param clearHistories flag indicating, that the list should
734            be cleared (boolean)
735        @param history list of history entries to be set (list of strings)
736        """
737        if clearHistories:
738            del self.argvHistory[1:]
739        elif history is not None:
740            self.argvHistory = history[:]
741        else:
742            if argsStr in self.argvHistory:
743                self.argvHistory.remove(argsStr)
744            self.argvHistory.insert(0, argsStr)
745
746    def setWdHistory(self, wdStr, clearHistories=False, history=None):
747        """
748        Public slot to initialize the wd history.
749
750        @param wdStr the working directory (string)
751        @param clearHistories flag indicating, that the list should
752            be cleared (boolean)
753        @param history list of history entries to be set (list of strings)
754        """
755        if clearHistories:
756            del self.wdHistory[1:]
757        elif history is not None:
758            self.wdHistory = history[:]
759        else:
760            if wdStr in self.wdHistory:
761                self.wdHistory.remove(wdStr)
762            self.wdHistory.insert(0, wdStr)
763
764    def setEnvHistory(self, envStr, clearHistories=False, history=None):
765        """
766        Public slot to initialize the env history.
767
768        @param envStr the environment settings (string)
769        @param clearHistories flag indicating, that the list should
770            be cleared (boolean)
771        @param history list of history entries to be set (list of strings)
772        """
773        if clearHistories:
774            del self.envHistory[1:]
775        elif history is not None:
776            self.envHistory = history[:]
777        else:
778            if envStr in self.envHistory:
779                self.envHistory.remove(envStr)
780            self.envHistory.insert(0, envStr)
781
782    def setExceptionReporting(self, exceptions):
783        """
784        Public slot to initialize the exception reporting flag.
785
786        @param exceptions flag indicating exception reporting status (boolean)
787        """
788        self.exceptions = exceptions
789
790    def setExcList(self, excList):
791        """
792        Public slot to initialize the exceptions type list.
793
794        @param excList list of exception types (list of strings)
795        """
796        self.excList = excList[:]   # keep a copy
797
798    def setExcIgnoreList(self, excIgnoreList):
799        """
800        Public slot to initialize the ignored exceptions type list.
801
802        @param excIgnoreList list of ignored exception types (list of strings)
803        """
804        self.excIgnoreList = excIgnoreList[:]   # keep a copy
805
806    def setAutoClearShell(self, autoClearShell):
807        """
808        Public slot to initialize the autoClearShell flag.
809
810        @param autoClearShell flag indicating, that the interpreter window
811            should be cleared (boolean)
812        """
813        self.autoClearShell = autoClearShell
814
815    def setTracePython(self, tracePython):
816        """
817        Public slot to initialize the trace Python flag.
818
819        @param tracePython flag indicating if the Python library should be
820            traced as well (boolean)
821        """
822        self.tracePython = tracePython
823
824    def setAutoContinue(self, autoContinue):
825        """
826        Public slot to initialize the autoContinue flag.
827
828        @param autoContinue flag indicating, that the debugger should not
829            stop at the first executable line (boolean)
830        """
831        self.autoContinue = autoContinue
832
833    def __editorOpened(self, fn):
834        """
835        Private slot to handle the editorOpened signal.
836
837        @param fn filename of the opened editor
838        """
839        self.editorOpen = True
840
841        editor = self.viewmanager.getOpenEditor(fn) if fn else None
842        self.__checkActions(editor)
843
844    def __lastEditorClosed(self):
845        """
846        Private slot to handle the closeProgram signal.
847        """
848        self.editorOpen = False
849        self.debugAct.setEnabled(False)
850        self.runAct.setEnabled(False)
851        self.profileAct.setEnabled(False)
852        self.coverageAct.setEnabled(False)
853        self.debugActGrp.setEnabled(False)
854        self.dbgSetBpActGrp.setEnabled(False)
855        self.lastAction = -1
856        if not self.projectOpen:
857            self.restartAct.setEnabled(False)
858            self.lastDebuggedFile = None
859            self.lastStartAction = 0
860            self.clientType = ""
861
862    def __checkActions(self, editor):
863        """
864        Private slot to check some actions for their enable/disable status.
865
866        @param editor editor window
867        """
868        fn = editor.getFileName() if editor else None
869
870        cap = 0
871        if fn:
872            for language in self.debugServer.getSupportedLanguages():
873                exts = self.debugServer.getExtensions(language)
874                if fn.endswith(exts):
875                    cap = self.debugServer.getClientCapabilities(language)
876                    break
877            else:
878                if editor.isPy3File():
879                    cap = self.debugServer.getClientCapabilities('Python3')
880                elif editor.isRubyFile():
881                    cap = self.debugServer.getClientCapabilities('Ruby')
882
883            if not self.passive:
884                self.runAct.setEnabled(cap & HasInterpreter)
885                self.coverageAct.setEnabled(cap & HasCoverage)
886                self.profileAct.setEnabled(cap & HasProfiler)
887                self.debugAct.setEnabled(cap & HasDebugger)
888            self.dbgSetBpActGrp.setEnabled(cap & HasDebugger)
889            if editor.curLineHasBreakpoint():
890                self.dbgEditBpAct.setEnabled(True)
891            else:
892                self.dbgEditBpAct.setEnabled(False)
893            if editor.hasBreakpoints():
894                self.dbgNextBpAct.setEnabled(True)
895                self.dbgPrevBpAct.setEnabled(True)
896            else:
897                self.dbgNextBpAct.setEnabled(False)
898                self.dbgPrevBpAct.setEnabled(False)
899        else:
900            self.runAct.setEnabled(False)
901            self.coverageAct.setEnabled(False)
902            self.profileAct.setEnabled(False)
903            self.debugAct.setEnabled(False)
904            self.dbgSetBpActGrp.setEnabled(False)
905
906    def __cursorChanged(self, editor):
907        """
908        Private slot handling the cursorChanged signal of the viewmanager.
909
910        @param editor editor window
911        """
912        if editor is None:
913            return
914
915        if editor.isPyFile() or editor.isRubyFile():
916            if editor.curLineHasBreakpoint():
917                self.dbgEditBpAct.setEnabled(True)
918            else:
919                self.dbgEditBpAct.setEnabled(False)
920            if editor.hasBreakpoints():
921                self.dbgNextBpAct.setEnabled(True)
922                self.dbgPrevBpAct.setEnabled(True)
923            else:
924                self.dbgNextBpAct.setEnabled(False)
925                self.dbgPrevBpAct.setEnabled(False)
926
927    def __projectOpened(self):
928        """
929        Private slot to handle the projectOpened signal.
930        """
931        self.projectOpen = True
932        cap = self.debugServer.getClientCapabilities(
933            self.project.getProjectLanguage())
934        if not self.passive:
935            self.debugProjectAct.setEnabled(cap & HasDebugger)
936            self.runProjectAct.setEnabled(cap & HasInterpreter)
937            self.profileProjectAct.setEnabled(cap & HasProfiler)
938            self.coverageProjectAct.setEnabled(cap & HasCoverage)
939
940    def __projectClosed(self):
941        """
942        Private slot to handle the projectClosed signal.
943        """
944        self.projectOpen = False
945        self.runProjectAct.setEnabled(False)
946        self.profileProjectAct.setEnabled(False)
947        self.coverageProjectAct.setEnabled(False)
948        self.debugProjectAct.setEnabled(False)
949
950        if not self.editorOpen:
951            self.restartAct.setEnabled(False)
952            self.lastDebuggedFile = None
953            self.lastStartAction = 0
954            self.clientType = ""
955
956    def clearHistories(self):
957        """
958        Public method to clear the various debug histories.
959        """
960        self.argvHistory = []
961        self.wdHistory = []
962        self.envHistory = []
963        self.multiprocessNoDebugHistory = []
964
965        Preferences.Prefs.settings.setValue(
966            'DebugInfo/ArgumentsHistory', self.argvHistory)
967        Preferences.Prefs.settings.setValue(
968            'DebugInfo/WorkingDirectoryHistory', self.wdHistory)
969        Preferences.Prefs.settings.setValue(
970            'DebugInfo/EnvironmentHistory', self.envHistory)
971        Preferences.Prefs.settings.setValue(
972            'DebugInfo/MultiprocessNoDebugHistory',
973            self.multiprocessNoDebugHistory)
974
975    def shutdown(self):
976        """
977        Public method to perform shutdown actions.
978        """
979        # Just save the 10 most recent entries
980        del self.argvHistory[10:]
981        del self.wdHistory[10:]
982        del self.envHistory[10:]
983
984        Preferences.Prefs.settings.setValue(
985            'DebugInfo/VirtualEnvironment', self.lastUsedVenvName)
986        Preferences.Prefs.settings.setValue(
987            'DebugInfo/ArgumentsHistory', self.argvHistory)
988        Preferences.Prefs.settings.setValue(
989            'DebugInfo/WorkingDirectoryHistory', self.wdHistory)
990        Preferences.Prefs.settings.setValue(
991            'DebugInfo/EnvironmentHistory', self.envHistory)
992        Preferences.Prefs.settings.setValue(
993            'DebugInfo/Exceptions', self.excList)
994        Preferences.Prefs.settings.setValue(
995            'DebugInfo/IgnoredExceptions', self.excIgnoreList)
996        Preferences.Prefs.settings.setValue(
997            'DebugInfo/ReportExceptions', self.exceptions)
998        Preferences.Prefs.settings.setValue(
999            'DebugInfo/AutoClearShell', self.autoClearShell)
1000        Preferences.Prefs.settings.setValue(
1001            'DebugInfo/TracePython', self.tracePython)
1002        Preferences.Prefs.settings.setValue(
1003            'DebugInfo/AutoContinue', self.autoContinue)
1004        Preferences.Prefs.settings.setValue(
1005            'DebugInfo/EnableMultiprocess', self.enableMultiprocess)
1006        Preferences.Prefs.settings.setValue(
1007            'DebugInfo/MultiprocessNoDebugHistory',
1008            self.multiprocessNoDebugHistory)
1009        Preferences.Prefs.settings.setValue(
1010            'DebugInfo/OverrideGlobal',
1011            self.overrideGlobalConfig["enable"])
1012        Preferences.Prefs.settings.setValue(
1013            'DebugInfo/RedirectStdinStdout',
1014            self.overrideGlobalConfig["redirect"])
1015
1016    def shutdownServer(self):
1017        """
1018        Public method to shut down the debug server.
1019
1020        This is needed to cleanly close the sockets on Win OS.
1021
1022        @return always true
1023        """
1024        self.debugServer.shutdownServer()
1025        return True
1026
1027    def __resetUI(self, fullReset=True):
1028        """
1029        Private slot to reset the user interface.
1030
1031        @param fullReset flag indicating a full reset is required
1032        @type bool
1033        """
1034        self.lastAction = -1
1035        self.debugActGrp.setEnabled(False)
1036        self.__clientDebuggerIds.clear()
1037
1038        if not self.passive:
1039            if self.editorOpen:
1040                editor = self.viewmanager.activeWindow()
1041            else:
1042                editor = None
1043            self.__checkActions(editor)
1044
1045            self.debugProjectAct.setEnabled(self.projectOpen)
1046            self.runProjectAct.setEnabled(self.projectOpen)
1047            self.profileProjectAct.setEnabled(self.projectOpen)
1048            self.coverageProjectAct.setEnabled(self.projectOpen)
1049            if (
1050                self.lastDebuggedFile is not None and
1051                (self.editorOpen or self.projectOpen)
1052            ):
1053                self.restartAct.setEnabled(True)
1054            else:
1055                self.restartAct.setEnabled(False)
1056            self.stopAct.setEnabled(False)
1057
1058        self.resetUI.emit(fullReset)
1059
1060    def __clientDebuggerId(self, debuggerId):
1061        """
1062        Private slot to track the list of connected debuggers.
1063
1064        @param debuggerId ID of the debugger backend
1065        @type str
1066        """
1067        self.__clientDebuggerIds.add(debuggerId)
1068
1069    def __clientLine(self, fn, line, debuggerId, threadName, forStack):
1070        """
1071        Private method to handle a change to the current line.
1072
1073        @param fn filename
1074        @type str
1075        @param line linenumber
1076        @type int
1077        @param debuggerId ID of the debugger backend
1078        @type str
1079        @param threadName name of the thread signaling the event
1080        @type str
1081        @param forStack flag indicating this is for a stack dump
1082        @type bool
1083        """
1084        self.ui.raise_()
1085        self.ui.activateWindow()
1086        if self.ui.getViewProfile() != "debug":
1087            self.ui.setDebugProfile()
1088        self.viewmanager.setFileLine(fn, line)
1089        if not forStack:
1090            self.__getThreadList(debuggerId)
1091            self.__getClientVariables(debuggerId)
1092
1093        self.debugActGrp.setEnabled(True)
1094
1095    @pyqtSlot(str)
1096    def __clientDisconnected(self, debuggerId):
1097        """
1098        Private slot to handle a debug client disconnecting its control
1099        socket.
1100
1101        @param debuggerId ID of the debugger backend
1102        @type str
1103        """
1104        self.__clientDebuggerIds.discard(debuggerId)
1105
1106        if len(self.__clientDebuggerIds) == 0:
1107            self.viewmanager.exit()
1108            self.__resetUI(fullReset=False)
1109
1110    @pyqtSlot(str, int, str, bool, str)
1111    def __clientExit(self, program, status, message, quiet, debuggerId):
1112        """
1113        Private slot to handle the debugged program terminating.
1114
1115        @param program name of the exited program
1116        @type str
1117        @param status exit code of the debugged program
1118        @type int
1119        @param message exit message of the debugged program
1120        @type str
1121        @param quiet flag indicating to suppress exit info display
1122        @type bool
1123        @param debuggerId ID of the debugger backend
1124        @type str
1125        """
1126        self.__clientDisconnected(debuggerId)
1127
1128        if not quiet:
1129            if not program:
1130                program = self.ui.currentProg
1131
1132            if message:
1133                info = self.tr("Message: {0}").format(
1134                    Utilities.html_uencode(message))
1135            else:
1136                info = ""
1137            if program is None:
1138                msg = self.tr(
1139                    '<p>The program has terminated with an exit status of'
1140                    ' {0}.</p><p>{1}</p>').format(status, info)
1141            else:
1142                msg = self.tr(
1143                    '<p><b>{0}</b> has terminated with an exit status of'
1144                    ' {1}.</p><p>{2}</p>').format(
1145                    os.path.basename(program), status, info)
1146            if status != 0:
1147                timeout = 0
1148                kind = NotificationTypes.WARNING
1149            else:
1150                timeout = None
1151                kind = NotificationTypes.INFORMATION
1152            self.ui.showNotification(
1153                UI.PixmapCache.getPixmap("debug48"),
1154                self.tr("Program terminated"), msg, kind=kind,
1155                timeout=timeout)
1156
1157    def __lastClientExited(self):
1158        """
1159        Private slot handling the exit of the last client.
1160        """
1161        self.viewmanager.exit()
1162        self.__resetUI()
1163
1164    def __clientSyntaxError(self, message, filename, lineNo, characterNo):
1165        """
1166        Private method to handle a syntax error in the debugged program.
1167
1168        @param message message of the syntax error (string)
1169        @param filename translated filename of the syntax error position
1170            (string)
1171        @param lineNo line number of the syntax error position (integer)
1172        @param characterNo character number of the syntax error position
1173            (integer)
1174        """
1175        self.__resetUI()
1176        self.ui.raise_()
1177        self.ui.activateWindow()
1178
1179        if message is None:
1180            E5MessageBox.critical(
1181                self.ui, Program,
1182                self.tr(
1183                    'The program being debugged contains an unspecified'
1184                    ' syntax error.'))
1185            return
1186
1187        if not os.path.isabs(filename):
1188            if os.path.exists(os.path.join(self.project.getProjectPath(),
1189                              filename)):
1190                filename = os.path.join(self.project.getProjectPath(),
1191                                        filename)
1192            else:
1193                ms = self.project.getMainScript(normalized=True)
1194                if ms is not None:
1195                    d = os.path.dirname(ms)
1196                    if os.path.exists(os.path.join(d, filename)):
1197                        filename = os.path.join(d, filename)
1198        self.viewmanager.setFileLine(filename, lineNo, True, True)
1199        E5MessageBox.critical(
1200            self.ui, Program,
1201            self.tr('<p>The file <b>{0}</b> contains the syntax error'
1202                    ' <b>{1}</b> at line <b>{2}</b>, character <b>{3}</b>.'
1203                    '</p>')
1204                .format(filename, message, lineNo, characterNo))
1205
1206    def __clientException(self, exceptionType, exceptionMessage, stackTrace,
1207                          debuggerId):
1208        """
1209        Private method to handle an exception of the debugged program.
1210
1211        @param exceptionType type of exception raised
1212        @type str
1213        @param exceptionMessage message given by the exception
1214        @type (str
1215        @param stackTrace list of stack entries
1216        @type list of str
1217        @param debuggerId ID of the debugger backend
1218        @type str
1219        """
1220        self.ui.raise_()
1221        QApplication.processEvents()
1222        if not exceptionType:
1223            E5MessageBox.critical(
1224                self.ui, Program,
1225                self.tr('An unhandled exception occured.'
1226                        ' See the shell window for details.'))
1227            return
1228
1229        if (
1230            (self.exceptions and
1231             exceptionType not in self.excIgnoreList and
1232             (not len(self.excList) or
1233              (len(self.excList) and exceptionType in self.excList)
1234              )
1235             ) or exceptionType.startswith('unhandled')
1236        ):
1237            res = None
1238            if stackTrace:
1239                with contextlib.suppress(UnicodeError, OSError):
1240                    file, line = stackTrace[0][:2]
1241                    source, encoding = Utilities.readEncodedFile(file)
1242                    source = source.splitlines(True)
1243                    if len(source) >= line:
1244                        lineFlags = Utilities.extractLineFlags(
1245                            source[line - 1].strip())
1246                        with contextlib.suppress(IndexError):
1247                            lineFlags += Utilities.extractLineFlags(
1248                                source[line].strip(), flagsLine=True)
1249                        if "__IGNORE_EXCEPTION__" in lineFlags:
1250                            res = E5MessageBox.No
1251                if res != E5MessageBox.No:
1252                    self.viewmanager.setFileLine(
1253                        stackTrace[0][0], stackTrace[0][1], True)
1254            if res != E5MessageBox.No:
1255                self.ui.activateWindow()
1256                if Preferences.getDebugger("BreakAlways"):
1257                    res = E5MessageBox.Yes
1258                else:
1259                    if stackTrace:
1260                        if exceptionType.startswith('unhandled'):
1261                            buttons = E5MessageBox.StandardButtons(
1262                                E5MessageBox.No |
1263                                E5MessageBox.Yes)
1264                        else:
1265                            buttons = E5MessageBox.StandardButtons(
1266                                E5MessageBox.No |
1267                                E5MessageBox.Yes |
1268                                E5MessageBox.Ignore)
1269                        res = E5MessageBox.critical(
1270                            self.ui, Program,
1271                            self.tr(
1272                                '<p>The debugged program raised the exception'
1273                                ' <b>{0}</b><br>"<b>{1}</b>"<br>'
1274                                'File: <b>{2}</b>, Line: <b>{3}</b></p>'
1275                                '<p>Break here?</p>')
1276                            .format(
1277                                exceptionType,
1278                                Utilities.html_encode(exceptionMessage),
1279                                stackTrace[0][0],
1280                                stackTrace[0][1]),
1281                            buttons,
1282                            E5MessageBox.No)
1283                    else:
1284                        res = E5MessageBox.critical(
1285                            self.ui, Program,
1286                            self.tr(
1287                                '<p>The debugged program raised the exception'
1288                                ' <b>{0}</b><br>"<b>{1}</b>"</p>')
1289                            .format(
1290                                exceptionType,
1291                                Utilities.html_encode(exceptionMessage)))
1292            if res == E5MessageBox.Yes:
1293                self.debugServer.setDebugging(True)
1294                self.exceptionInterrupt.emit()
1295                stack = []
1296                for fn, ln, func, args in stackTrace:
1297                    stack.append((fn, ln, func, args))
1298                self.clientStack.emit(stack, debuggerId)
1299                self.__getClientVariables(debuggerId)
1300                self.__getClientDisassembly(debuggerId)
1301                self.ui.setDebugProfile()
1302                self.debugActGrp.setEnabled(True)
1303                return
1304            elif (
1305                res == E5MessageBox.Ignore and
1306                exceptionType not in self.excIgnoreList
1307            ):
1308                self.excIgnoreList.append(exceptionType)
1309
1310        if self.lastAction != -1:
1311            if self.lastAction == 2:
1312                self.__specialContinue(debuggerId)
1313            else:
1314                self.debugActions[self.lastAction](debuggerId)
1315        else:
1316            self.__continue(debuggerId)
1317
1318    def __clientSignal(self, message, filename, lineNo, funcName, funcArgs,
1319                       debuggerId):
1320        """
1321        Private method to handle a signal generated on the client side.
1322
1323        @param message message of the syntax error
1324        @type str
1325        @param filename translated filename of the syntax error position
1326        @type str
1327        @param lineNo line number of the syntax error position
1328        @type int
1329        @param funcName name of the function causing the signal
1330        @type str
1331        @param funcArgs function arguments
1332        @type str
1333        @param debuggerId ID of the debugger backend
1334        @type str
1335        """
1336        self.ui.raise_()
1337        self.ui.activateWindow()
1338        QApplication.processEvents()
1339        self.viewmanager.setFileLine(filename, lineNo, True)
1340        E5MessageBox.critical(
1341            self.ui, Program,
1342            self.tr("""<p>The program generate the signal "{0}".<br/>"""
1343                    """File: <b>{1}</b>, Line: <b>{2}</b></p>""").format(
1344                message, filename, lineNo))
1345
1346    def __clientGone(self, unplanned):
1347        """
1348        Private method to handle the disconnection of the debugger client.
1349
1350        @param unplanned True if the client died, False otherwise
1351        """
1352        self.__resetUI()
1353        if unplanned:
1354            E5MessageBox.information(
1355                self.ui, Program,
1356                self.tr('The program being debugged has terminated'
1357                        ' unexpectedly.'))
1358
1359    def __getThreadList(self, debuggerId):
1360        """
1361        Private method to get the list of threads from the client.
1362
1363        @param debuggerId ID of the debugger backend
1364        @type str
1365        """
1366        self.debugServer.remoteThreadList(debuggerId)
1367
1368    def __clientThreadSet(self, debuggerId):
1369        """
1370        Private method to handle a change of the client's current thread.
1371
1372        @param debuggerId ID of the debugger backend
1373        @type str
1374        """
1375        if self.debugServer.isDebugging():
1376            self.debugServer.remoteClientVariables(
1377                debuggerId, 0, self.__localsVarFilterList)
1378
1379    def __getClientVariables(self, debuggerId):
1380        """
1381        Private method to request the global and local variables.
1382
1383        In the first step, the global variables are requested from the client.
1384        Once these have been received, the local variables are requested.
1385        This happens in the method '__clientVariables'.
1386
1387        @param debuggerId ID of the debugger backend
1388        @type str
1389        """
1390        # get globals first
1391        self.debugServer.remoteClientVariables(
1392            debuggerId, 1, self.__globalsVarFilterList)
1393        # the local variables are requested once we have received the globals
1394
1395    def __clientVariables(self, scope, variables, debuggerId):
1396        """
1397        Private method to write the clients variables to the user interface.
1398
1399        @param scope scope of the variables
1400            (-2 = no frame found, -1 = empty locals, 1 = global, 0 = local)
1401        @type int
1402        @param variables the list of variables from the client
1403        @type list
1404        @param debuggerId ID of the debugger backend
1405        @type str
1406        """
1407        if debuggerId == self.getSelectedDebuggerId() and scope > -2:
1408            self.ui.activateDebugViewer()
1409            if scope > 0:
1410                self.debugViewer.showVariables(variables, True)
1411                if scope == 1:
1412                    # now get the local variables
1413                    self.debugServer.remoteClientVariables(
1414                        self.getSelectedDebuggerId(),
1415                        0, self.__localsVarFilterList)
1416            elif scope == 0:
1417                self.debugViewer.showVariables(variables, False)
1418            elif scope == -1:
1419                vlist = [(self.tr('No locals available.'), '', '')]
1420                self.debugViewer.showVariables(vlist, False)
1421
1422    def __clientVariable(self, scope, variables, debuggerId):
1423        """
1424        Private method to write the contents of a clients classvariable to
1425        the user interface.
1426
1427        @param scope scope of the variables (-1 = empty locals, 1 = global,
1428            0 = local)
1429        @type int
1430        @param variables the list of variables from the client
1431        @type list
1432        @param debuggerId ID of the debugger backend
1433        @type str
1434        """
1435        if debuggerId == self.getSelectedDebuggerId():
1436            self.ui.activateDebugViewer()
1437            if scope == 1:
1438                self.debugViewer.showVariable(variables, True)
1439            elif scope == 0:
1440                self.debugViewer.showVariable(variables, False)
1441
1442    def __getClientDisassembly(self, debuggerId):
1443        """
1444        Private method to ask the client for the latest traceback disassembly.
1445
1446        @param debuggerId ID of the debugger backend
1447        @type str
1448        """
1449        self.debugServer.remoteClientDisassembly(debuggerId)
1450
1451    def __clientBreakConditionError(self, filename, lineno, debuggerId):
1452        """
1453        Private method to handle a condition error of a breakpoint.
1454
1455        @param filename filename of the breakpoint
1456        @type str
1457        @param lineno line umber of the breakpoint
1458        @type int
1459        @param debuggerId ID of the debugger backend
1460        @type str
1461        """
1462        E5MessageBox.critical(
1463            self.ui,
1464            self.tr("Breakpoint Condition Error"),
1465            self.tr(
1466                """<p>The condition of the breakpoint <b>{0}, {1}</b>"""
1467                """ contains a syntax error.</p>""")
1468            .format(filename, lineno))
1469
1470        model = self.debugServer.getBreakPointModel()
1471        index = model.getBreakPointIndex(filename, lineno)
1472        if not index.isValid():
1473            return
1474
1475        bp = model.getBreakPointByIndex(index)
1476        if not bp:
1477            return
1478
1479        fn, line, cond, temp, enabled, count = bp[:6]
1480
1481        from .EditBreakpointDialog import EditBreakpointDialog
1482        dlg = EditBreakpointDialog(
1483            (fn, line), (cond, temp, enabled, count),
1484            [], self.ui, modal=True)
1485        if dlg.exec() == QDialog.DialogCode.Accepted:
1486            cond, temp, enabled, count = dlg.getData()
1487            model.setBreakPointByIndex(index, fn, line,
1488                                       (cond, temp, enabled, count))
1489
1490    def __clientWatchConditionError(self, cond, debuggerId):
1491        """
1492        Private method to handle a expression error of a watch expression.
1493
1494        Note: This can only happen for normal watch expressions
1495
1496        @param cond expression of the watch expression
1497        @type str
1498        @param debuggerId ID of the debugger backend
1499        @type str
1500        """
1501        E5MessageBox.critical(
1502            self.ui,
1503            self.tr("Watch Expression Error"),
1504            self.tr("""<p>The watch expression <b>{0}</b>"""
1505                    """ contains a syntax error.</p>""")
1506            .format(cond))
1507
1508        model = self.debugServer.getWatchPointModel()
1509        index = model.getWatchPointIndex(cond)
1510        if not index.isValid():
1511            return
1512
1513        wp = model.getWatchPointByIndex(index)
1514        if not wp:
1515            return
1516
1517        cond, special, temp, enabled, count = wp[:5]
1518
1519        from .EditWatchpointDialog import EditWatchpointDialog
1520        dlg = EditWatchpointDialog(
1521            (cond, temp, enabled, count, special), self.ui)
1522        if dlg.exec() == QDialog.DialogCode.Accepted:
1523            cond, temp, enabled, count, special = dlg.getData()
1524
1525            # check for duplicates
1526            idx = model.getWatchPointIndex(cond, special)
1527            duplicate = (idx.isValid() and
1528                         idx.internalPointer() != index.internalPointer())
1529            if duplicate:
1530                if not special:
1531                    msg = self.tr(
1532                        """<p>A watch expression '<b>{0}</b>'"""
1533                        """ already exists.</p>"""
1534                    ).format(Utilities.html_encode(cond))
1535                else:
1536                    msg = self.tr(
1537                        """<p>A watch expression '<b>{0}</b>'"""
1538                        """ for the variable <b>{1}</b> already"""
1539                        """ exists.</p>"""
1540                    ).format(special, Utilities.html_encode(cond))
1541                E5MessageBox.warning(
1542                    self.ui,
1543                    self.tr("Watch expression already exists"),
1544                    msg)
1545                model.deleteWatchPointByIndex(index)
1546            else:
1547                model.setWatchPointByIndex(index, cond, special,
1548                                           (temp, enabled, count))
1549
1550    def __configureVariablesFilters(self):
1551        """
1552        Private slot for displaying the variables filter configuration dialog.
1553        """
1554        from .VariablesFilterDialog import VariablesFilterDialog
1555        dlg = VariablesFilterDialog(self.ui, 'Filter Dialog', True)
1556        dlg.setSelection(self.__localsVarFilterList,
1557                         self.__globalsVarFilterList)
1558        if dlg.exec() == QDialog.DialogCode.Accepted:
1559            self.__localsVarFilterList, self.__globalsVarFilterList = (
1560                dlg.getSelection()
1561            )
1562            self.debugViewer.setVariablesFilter(
1563                self.__globalsVarFilterList, self.__localsVarFilterList)
1564
1565    def __configureExceptionsFilter(self):
1566        """
1567        Private slot for displaying the exception filter dialog.
1568        """
1569        from .ExceptionsFilterDialog import ExceptionsFilterDialog
1570        dlg = ExceptionsFilterDialog(self.excList, ignore=False)
1571        if dlg.exec() == QDialog.DialogCode.Accepted:
1572            self.excList = dlg.getExceptionsList()[:]   # keep a copy
1573
1574    def __configureIgnoredExceptions(self):
1575        """
1576        Private slot for displaying the ignored exceptions dialog.
1577        """
1578        from .ExceptionsFilterDialog import ExceptionsFilterDialog
1579        dlg = ExceptionsFilterDialog(self.excIgnoreList, ignore=True)
1580        if dlg.exec() == QDialog.DialogCode.Accepted:
1581            self.excIgnoreList = dlg.getExceptionsList()[:]   # keep a copy
1582
1583    def __toggleBreakpoint(self):
1584        """
1585        Private slot to handle the 'Set/Reset breakpoint' action.
1586        """
1587        self.viewmanager.activeWindow().menuToggleBreakpoint()
1588
1589    def __editBreakpoint(self):
1590        """
1591        Private slot to handle the 'Edit breakpoint' action.
1592        """
1593        self.viewmanager.activeWindow().menuEditBreakpoint()
1594
1595    def __nextBreakpoint(self):
1596        """
1597        Private slot to handle the 'Next breakpoint' action.
1598        """
1599        self.viewmanager.activeWindow().menuNextBreakpoint()
1600
1601    def __previousBreakpoint(self):
1602        """
1603        Private slot to handle the 'Previous breakpoint' action.
1604        """
1605        self.viewmanager.activeWindow().menuPreviousBreakpoint()
1606
1607    def __clearBreakpoints(self):
1608        """
1609        Private slot to handle the 'Clear breakpoints' action.
1610        """
1611        self.debugServer.getBreakPointModel().deleteAll()
1612
1613    def __showDebugMenu(self):
1614        """
1615        Private method to set up the debug menu.
1616        """
1617        bpCount = self.debugServer.getBreakPointModel().rowCount()
1618        self.menuBreakpointsAct.setEnabled(bpCount > 0)
1619
1620    def __showBreakpointsMenu(self):
1621        """
1622        Private method to handle the show breakpoints menu signal.
1623        """
1624        self.breakpointsMenu.clear()
1625
1626        model = self.debugServer.getBreakPointModel()
1627        for row in range(model.rowCount()):
1628            index = model.index(row, 0)
1629            filename, line, cond = model.getBreakPointByIndex(index)[:3]
1630            formattedCond = " : {0}".format(cond[:20]) if cond else ""
1631            bpSuffix = " : {0:d}{1}".format(line, formattedCond)
1632            act = self.breakpointsMenu.addAction(
1633                "{0}{1}".format(
1634                    Utilities.compactPath(
1635                        filename,
1636                        self.ui.maxMenuFilePathLen - len(bpSuffix)),
1637                    bpSuffix))
1638            act.setData([filename, line])
1639
1640    def __breakpointSelected(self, act):
1641        """
1642        Private method to handle the breakpoint selected signal.
1643
1644        @param act reference to the action that triggered (QAction)
1645        """
1646        qvList = act.data()
1647        filename = qvList[0]
1648        line = qvList[1]
1649        self.viewmanager.openSourceFile(filename, line)
1650
1651    def __compileChangedProjectFiles(self):
1652        """
1653        Private method to signal compilation of changed forms and resources
1654        is wanted.
1655        """
1656        if Preferences.getProject("AutoCompileForms"):
1657            self.compileForms.emit()
1658        if Preferences.getProject("AutoCompileResources"):
1659            self.compileResources.emit()
1660        if Preferences.getProject("AutoExecuteMake"):
1661            self.executeMake.emit()
1662        QApplication.processEvents()
1663
1664    def __coverageScript(self):
1665        """
1666        Private slot to handle the coverage of script action.
1667        """
1668        self.__doCoverage(False)
1669
1670    def __coverageProject(self):
1671        """
1672        Private slot to handle the coverage of project action.
1673        """
1674        self.__compileChangedProjectFiles()
1675        self.__doCoverage(True)
1676
1677    def __doCoverage(self, runProject):
1678        """
1679        Private method to handle the coverage actions.
1680
1681        @param runProject flag indicating coverage of the current project
1682            (True) or script (false)
1683        """
1684        from .StartDialog import StartDialog
1685
1686        self.__resetUI()
1687        doNotStart = False
1688
1689        # Get the command line arguments, the working directory and the
1690        # exception reporting flag.
1691        cap = (
1692            self.tr("Coverage of Project")
1693            if runProject else
1694            self.tr("Coverage of Script")
1695        )
1696        dlg = StartDialog(
1697            cap, self.lastUsedVenvName, self.argvHistory, self.wdHistory,
1698            self.envHistory, self.exceptions, self.ui, 2,
1699            autoClearShell=self.autoClearShell,
1700            configOverride=self.overrideGlobalConfig)
1701        if dlg.exec() == QDialog.DialogCode.Accepted:
1702            (lastUsedVenvName, argv, wd, env, exceptions, clearShell,
1703             console) = dlg.getData()
1704            configOverride = dlg.getGlobalOverrideData()
1705            eraseCoverage = dlg.getCoverageData()
1706
1707            if runProject:
1708                fn = self.project.getMainScript(True)
1709                if fn is None:
1710                    E5MessageBox.critical(
1711                        self.ui,
1712                        self.tr("Coverage of Project"),
1713                        self.tr(
1714                            "There is no main script defined for the"
1715                            " current project. Aborting"))
1716                    return
1717
1718                if (
1719                    Preferences.getDebugger("Autosave") and
1720                    not self.project.saveAllScripts(reportSyntaxErrors=True)
1721                ):
1722                    doNotStart = True
1723
1724                # save the info for later use
1725                self.project.setDbgInfo(
1726                    lastUsedVenvName, argv, wd, env, exceptions, self.excList,
1727                    self.excIgnoreList, clearShell,
1728                    configOverride=configOverride
1729                )
1730
1731                self.lastStartAction = 6
1732                self.clientType = self.project.getProjectLanguage()
1733            else:
1734                editor = self.viewmanager.activeWindow()
1735                if editor is None:
1736                    return
1737
1738                if (
1739                    not self.viewmanager.checkDirty(
1740                        editor, Preferences.getDebugger("Autosave")) or
1741                    editor.getFileName() is None
1742                ):
1743                    return
1744
1745                fn = editor.getFileName()
1746                self.lastStartAction = 5
1747                self.clientType = editor.determineFileType()
1748
1749            # save the filename for use by the restart method
1750            self.lastDebuggedFile = fn
1751            self.restartAct.setEnabled(True)
1752
1753            # save the most recently used virtual environment
1754            self.lastUsedVenvName = lastUsedVenvName
1755
1756            # This moves any previous occurrence of these arguments to the head
1757            # of the list.
1758            self.setArgvHistory(argv)
1759            self.setWdHistory(wd)
1760            self.setEnvHistory(env)
1761
1762            # Save the exception flags
1763            self.exceptions = exceptions
1764
1765            # Save the erase coverage flag
1766            self.eraseCoverage = eraseCoverage
1767
1768            # Save the clear interpreter flag
1769            self.autoClearShell = clearShell
1770
1771            # Save the run in console flag
1772            self.runInConsole = console
1773
1774            # Save the global config override data
1775            self.overrideGlobalConfig = copy.deepcopy(configOverride)
1776
1777            # Hide all error highlights
1778            self.viewmanager.unhighlight()
1779
1780            if not doNotStart:
1781                if runProject and self.project.getProjectType() in [
1782                        "E6Plugin"]:
1783                    argv = '--plugin="{0}" {1}'.format(fn, argv)
1784                    fn = os.path.join(getConfig('ericDir'), "eric6.py")
1785
1786                self.debugViewer.initCallStackViewer(runProject)
1787
1788                # Ask the client to open the new program.
1789                self.debugServer.remoteCoverage(
1790                    lastUsedVenvName, fn, argv, wd, env,
1791                    autoClearShell=self.autoClearShell, erase=eraseCoverage,
1792                    forProject=runProject, runInConsole=console,
1793                    clientType=self.clientType,
1794                    configOverride=self.overrideGlobalConfig)
1795
1796                self.stopAct.setEnabled(True)
1797
1798        if dlg.clearHistories():
1799            self.setArgvHistory("", clearHistories=True)
1800            self.setWdHistory("", clearHistories=True)
1801            self.setEnvHistory("", clearHistories=True)
1802            self.setMultiprocessNoDebugHistory("", clearHistories=True)
1803        elif dlg.historiesModified():
1804            argvHistory, wdHistory, envHistory, _ = dlg.getHistories()
1805            self.setArgvHistory("", history=argvHistory)
1806            self.setWdHistory("", history=wdHistory)
1807            self.setEnvHistory("", history=envHistory)
1808
1809    def __profileScript(self):
1810        """
1811        Private slot to handle the profile script action.
1812        """
1813        self.__doProfile(False)
1814
1815    def __profileProject(self):
1816        """
1817        Private slot to handle the profile project action.
1818        """
1819        self.__compileChangedProjectFiles()
1820        self.__doProfile(True)
1821
1822    def __doProfile(self, runProject):
1823        """
1824        Private method to handle the profile actions.
1825
1826        @param runProject flag indicating profiling of the current project
1827            (True) or script (False)
1828        """
1829        from .StartDialog import StartDialog
1830
1831        self.__resetUI()
1832        doNotStart = False
1833
1834        # Get the command line arguments, the working directory and the
1835        # exception reporting flag.
1836        cap = (
1837            self.tr("Profile of Project")
1838            if runProject else
1839            self.tr("Profile of Script")
1840        )
1841        dlg = StartDialog(
1842            cap, self.lastUsedVenvName, self.argvHistory, self.wdHistory,
1843            self.envHistory, self.exceptions, self.ui, 3,
1844            autoClearShell=self.autoClearShell,
1845            configOverride=self.overrideGlobalConfig)
1846        if dlg.exec() == QDialog.DialogCode.Accepted:
1847            (lastUsedVenvName, argv, wd, env, exceptions, clearShell,
1848             console) = dlg.getData()
1849            configOverride = dlg.getGlobalOverrideData()
1850            eraseTimings = dlg.getProfilingData()
1851
1852            if runProject:
1853                fn = self.project.getMainScript(True)
1854                if fn is None:
1855                    E5MessageBox.critical(
1856                        self.ui,
1857                        self.tr("Profile of Project"),
1858                        self.tr(
1859                            "There is no main script defined for the"
1860                            " current project. Aborting"))
1861                    return
1862
1863                if (
1864                    Preferences.getDebugger("Autosave") and
1865                    not self.project.saveAllScripts(reportSyntaxErrors=True)
1866                ):
1867                    doNotStart = True
1868
1869                # save the info for later use
1870                self.project.setDbgInfo(
1871                    lastUsedVenvName, argv, wd, env, exceptions, self.excList,
1872                    self.excIgnoreList, clearShell,
1873                    configOverride=configOverride
1874                )
1875
1876                self.lastStartAction = 8
1877                self.clientType = self.project.getProjectLanguage()
1878            else:
1879                editor = self.viewmanager.activeWindow()
1880                if editor is None:
1881                    return
1882
1883                if (
1884                    not self.viewmanager.checkDirty(
1885                        editor, Preferences.getDebugger("Autosave")) or
1886                    editor.getFileName() is None
1887                ):
1888                    return
1889
1890                fn = editor.getFileName()
1891                self.lastStartAction = 7
1892                self.clientType = editor.determineFileType()
1893
1894            # save the filename for use by the restart method
1895            self.lastDebuggedFile = fn
1896            self.restartAct.setEnabled(True)
1897
1898            # save the most recently used virtual environment
1899            self.lastUsedVenvName = lastUsedVenvName
1900
1901            # This moves any previous occurrence of these arguments to the head
1902            # of the list.
1903            self.setArgvHistory(argv)
1904            self.setWdHistory(wd)
1905            self.setEnvHistory(env)
1906
1907            # Save the exception flags
1908            self.exceptions = exceptions
1909
1910            # Save the erase timing flag
1911            self.eraseTimings = eraseTimings
1912
1913            # Save the clear interpreter flag
1914            self.autoClearShell = clearShell
1915
1916            # Save the run in console flag
1917            self.runInConsole = console
1918
1919            # Save the global config override data
1920            self.overrideGlobalConfig = copy.deepcopy(configOverride)
1921
1922            # Hide all error highlights
1923            self.viewmanager.unhighlight()
1924
1925            if not doNotStart:
1926                if runProject and self.project.getProjectType() in [
1927                        "E6Plugin"]:
1928                    argv = '--plugin="{0}" {1}'.format(fn, argv)
1929                    fn = os.path.join(getConfig('ericDir'), "eric6.py")
1930
1931                self.debugViewer.initCallStackViewer(runProject)
1932
1933                # Ask the client to open the new program.
1934                self.debugServer.remoteProfile(
1935                    lastUsedVenvName, fn, argv, wd, env,
1936                    autoClearShell=self.autoClearShell, erase=eraseTimings,
1937                    forProject=runProject, runInConsole=console,
1938                    clientType=self.clientType,
1939                    configOverride=self.overrideGlobalConfig)
1940
1941                self.stopAct.setEnabled(True)
1942
1943        if dlg.clearHistories():
1944            self.setArgvHistory("", clearHistories=True)
1945            self.setWdHistory("", clearHistories=True)
1946            self.setEnvHistory("", clearHistories=True)
1947            self.setMultiprocessNoDebugHistory("", clearHistories=True)
1948        elif dlg.historiesModified():
1949            argvHistory, wdHistory, envHistory, _ = dlg.getHistories()
1950            self.setArgvHistory("", history=argvHistory)
1951            self.setWdHistory("", history=wdHistory)
1952            self.setEnvHistory("", history=envHistory)
1953
1954    def __runScript(self):
1955        """
1956        Private slot to handle the run script action.
1957        """
1958        self.__doRun(False)
1959
1960    def __runProject(self):
1961        """
1962        Private slot to handle the run project action.
1963        """
1964        self.__compileChangedProjectFiles()
1965        self.__doRun(True)
1966
1967    def __doRun(self, runProject):
1968        """
1969        Private method to handle the run actions.
1970
1971        @param runProject flag indicating running the current project (True)
1972                or script (False)
1973        """
1974        from .StartDialog import StartDialog
1975
1976        self.__resetUI()
1977        doNotStart = False
1978
1979        # Get the command line arguments, the working directory and the
1980        # exception reporting flag.
1981        cap = (
1982            self.tr("Run Project")
1983            if runProject else
1984            self.tr("Run Script")
1985        )
1986        dlg = StartDialog(
1987            cap, self.lastUsedVenvName, self.argvHistory, self.wdHistory,
1988            self.envHistory, self.exceptions, self.ui, 1,
1989            autoClearShell=self.autoClearShell,
1990            configOverride=self.overrideGlobalConfig)
1991        if dlg.exec() == QDialog.DialogCode.Accepted:
1992            (lastUsedVenvName, argv, wd, env, exceptions, clearShell,
1993             console) = dlg.getData()
1994            configOverride = dlg.getGlobalOverrideData()
1995
1996            if runProject:
1997                fn = self.project.getMainScript(True)
1998                if fn is None:
1999                    E5MessageBox.critical(
2000                        self.ui,
2001                        self.tr("Run Project"),
2002                        self.tr(
2003                            "There is no main script defined for the"
2004                            " current project. Aborting"))
2005                    return
2006
2007                if (
2008                    Preferences.getDebugger("Autosave") and
2009                    not self.project.saveAllScripts(reportSyntaxErrors=True)
2010                ):
2011                    doNotStart = True
2012
2013                # save the info for later use
2014                self.project.setDbgInfo(
2015                    lastUsedVenvName, argv, wd, env, exceptions, self.excList,
2016                    self.excIgnoreList, clearShell,
2017                    configOverride=configOverride
2018                )
2019
2020                self.lastStartAction = 4
2021                self.clientType = self.project.getProjectLanguage()
2022            else:
2023                editor = self.viewmanager.activeWindow()
2024                if editor is None:
2025                    return
2026
2027                if (
2028                    not self.viewmanager.checkDirty(
2029                        editor,
2030                        Preferences.getDebugger("Autosave")) or
2031                    editor.getFileName() is None
2032                ):
2033                    return
2034
2035                fn = editor.getFileName()
2036                self.lastStartAction = 3
2037                self.clientType = editor.determineFileType()
2038
2039            # save the filename for use by the restart method
2040            self.lastDebuggedFile = fn
2041            self.restartAct.setEnabled(True)
2042
2043            # save the most recently used virtual environment
2044            self.lastUsedVenvName = lastUsedVenvName
2045
2046            # This moves any previous occurrence of these arguments to the head
2047            # of the list.
2048            self.setArgvHistory(argv)
2049            self.setWdHistory(wd)
2050            self.setEnvHistory(env)
2051
2052            # Save the exception flags
2053            self.exceptions = exceptions
2054
2055            # Save the clear interpreter flag
2056            self.autoClearShell = clearShell
2057
2058            # Save the run in console flag
2059            self.runInConsole = console
2060
2061            # Save the global config override data
2062            self.overrideGlobalConfig = copy.deepcopy(configOverride)
2063
2064            # Hide all error highlights
2065            self.viewmanager.unhighlight()
2066
2067            if not doNotStart:
2068                if runProject and self.project.getProjectType() in [
2069                        "E6Plugin"]:
2070                    argv = '--plugin="{0}" {1}'.format(fn, argv)
2071                    fn = os.path.join(getConfig('ericDir'), "eric6.py")
2072
2073                self.debugViewer.initCallStackViewer(runProject)
2074
2075                # Ask the client to open the new program.
2076                self.debugServer.remoteRun(
2077                    lastUsedVenvName, fn, argv, wd, env,
2078                    autoClearShell=self.autoClearShell, forProject=runProject,
2079                    runInConsole=console, clientType=self.clientType,
2080                    configOverride=self.overrideGlobalConfig)
2081
2082                self.stopAct.setEnabled(True)
2083
2084        if dlg.clearHistories():
2085            self.setArgvHistory("", clearHistories=True)
2086            self.setWdHistory("", clearHistories=True)
2087            self.setEnvHistory("", clearHistories=True)
2088            self.setMultiprocessNoDebugHistory("", clearHistories=True)
2089        elif dlg.historiesModified():
2090            argvHistory, wdHistory, envHistory, _ = dlg.getHistories()
2091            self.setArgvHistory("", history=argvHistory)
2092            self.setWdHistory("", history=wdHistory)
2093            self.setEnvHistory("", history=envHistory)
2094
2095    def __debugScript(self):
2096        """
2097        Private slot to handle the debug script action.
2098        """
2099        self.__doDebug(False)
2100
2101    def __debugProject(self):
2102        """
2103        Private slot to handle the debug project action.
2104        """
2105        self.__compileChangedProjectFiles()
2106        self.__doDebug(True)
2107
2108    def __doDebug(self, debugProject):
2109        """
2110        Private method to handle the debug actions.
2111
2112        @param debugProject flag indicating debugging the current project
2113            (True) or script (False)
2114        """
2115        from .StartDialog import StartDialog
2116
2117        self.__resetUI()
2118        doNotStart = False
2119
2120        # Get the command line arguments, the working directory and the
2121        # exception reporting flag.
2122        cap = (
2123            self.tr("Debug Project")
2124            if debugProject else
2125            self.tr("Debug Script")
2126        )
2127        dlg = StartDialog(
2128            cap, self.lastUsedVenvName, self.argvHistory, self.wdHistory,
2129            self.envHistory, self.exceptions, self.ui, 0,
2130            tracePython=self.tracePython, autoClearShell=self.autoClearShell,
2131            autoContinue=self.autoContinue,
2132            enableMultiprocess=self.enableMultiprocess,
2133            multiprocessNoDebugHistory=self.multiprocessNoDebugHistory,
2134            configOverride=self.overrideGlobalConfig)
2135        if dlg.exec() == QDialog.DialogCode.Accepted:
2136            (lastUsedVenvName, argv, wd, env, exceptions, clearShell,
2137             console) = dlg.getData()
2138            configOverride = dlg.getGlobalOverrideData()
2139            (tracePython, autoContinue, enableMultiprocess,
2140             multiprocessNoDebug) = dlg.getDebugData()
2141
2142            if debugProject:
2143                fn = self.project.getMainScript(True)
2144                if fn is None:
2145                    E5MessageBox.critical(
2146                        self.ui,
2147                        self.tr("Debug Project"),
2148                        self.tr(
2149                            "There is no main script defined for the"
2150                            " current project. No debugging possible."))
2151                    return
2152
2153                if (
2154                    Preferences.getDebugger("Autosave") and
2155                    not self.project.saveAllScripts(reportSyntaxErrors=True)
2156                ):
2157                    doNotStart = True
2158
2159                # save the info for later use
2160                self.project.setDbgInfo(
2161                    lastUsedVenvName, argv, wd, env, exceptions, self.excList,
2162                    self.excIgnoreList, clearShell, tracePython=tracePython,
2163                    autoContinue=autoContinue,
2164                    enableMultiprocess=enableMultiprocess,
2165                    multiprocessNoDebug=multiprocessNoDebug,
2166                    configOverride=configOverride
2167                )
2168
2169                self.lastStartAction = 2
2170                self.clientType = self.project.getProjectLanguage()
2171            else:
2172                editor = self.viewmanager.activeWindow()
2173                if editor is None:
2174                    return
2175
2176                if (
2177                    not self.viewmanager.checkDirty(
2178                        editor, Preferences.getDebugger("Autosave")) or
2179                    editor.getFileName() is None
2180                ):
2181                    return
2182
2183                fn = editor.getFileName()
2184                self.lastStartAction = 1
2185                self.clientType = editor.determineFileType()
2186
2187            # save the filename for use by the restart method
2188            self.lastDebuggedFile = fn
2189            self.restartAct.setEnabled(True)
2190
2191            # save the most recently used virtual environment
2192            self.lastUsedVenvName = lastUsedVenvName
2193
2194            # This moves any previous occurrence of these arguments to the head
2195            # of the list.
2196            self.setArgvHistory(argv)
2197            self.setWdHistory(wd)
2198            self.setEnvHistory(env)
2199
2200            # Save the exception flags
2201            self.exceptions = exceptions
2202
2203            # Save the tracePython flag
2204            self.tracePython = tracePython
2205
2206            # Save the clear interpreter flag
2207            self.autoClearShell = clearShell
2208
2209            # Save the run in console flag
2210            self.runInConsole = console
2211
2212            # Save the auto continue flag
2213            self.autoContinue = autoContinue
2214
2215            # Save the multiprocess debugging data
2216            self.enableMultiprocess = enableMultiprocess
2217            self.setMultiprocessNoDebugHistory(multiprocessNoDebug)
2218
2219            # Save the global config override data
2220            self.overrideGlobalConfig = copy.deepcopy(configOverride)
2221
2222            # Hide all error highlights
2223            self.viewmanager.unhighlight()
2224
2225            if not doNotStart:
2226                if debugProject and self.project.getProjectType() in [
2227                        "E6Plugin"]:
2228                    argv = '--plugin="{0}" {1}'.format(fn, argv)
2229                    fn = os.path.join(getConfig('ericDir'), "eric6.py")
2230                    tracePython = True  # override flag because it must be true
2231
2232                self.debugViewer.initCallStackViewer(debugProject)
2233
2234                # Ask the client to send call trace info
2235                enableCallTrace = self.debugViewer.isCallTraceEnabled()
2236                self.debugViewer.clearCallTrace()
2237                self.debugViewer.setCallTraceToProjectMode(debugProject)
2238
2239                # Ask the client to open the new program.
2240                self.debugServer.remoteLoad(
2241                    lastUsedVenvName, fn, argv, wd, env,
2242                    autoClearShell=self.autoClearShell,
2243                    tracePython=tracePython,
2244                    autoContinue=autoContinue, forProject=debugProject,
2245                    runInConsole=console, clientType=self.clientType,
2246                    enableCallTrace=enableCallTrace,
2247                    enableMultiprocess=enableMultiprocess,
2248                    multiprocessNoDebug=multiprocessNoDebug,
2249                    configOverride=self.overrideGlobalConfig)
2250
2251                if (
2252                    self.debugServer.isClientProcessUp() and
2253                    self.debugServer.getClientType() == self.clientType
2254                ):
2255                    # Signal that we have started a debugging session
2256                    self.debuggingStarted.emit(fn)
2257
2258                    self.stopAct.setEnabled(True)
2259
2260        if dlg.clearHistories():
2261            self.setArgvHistory("", clearHistories=True)
2262            self.setWdHistory("", clearHistories=True)
2263            self.setEnvHistory("", clearHistories=True)
2264            self.setMultiprocessNoDebugHistory("", clearHistories=True)
2265        elif dlg.historiesModified():
2266            (argvHistory, wdHistory, envHistory,
2267             noDebugHistory) = dlg.getHistories()
2268            self.setArgvHistory("", history=argvHistory)
2269            self.setWdHistory("", history=wdHistory)
2270            self.setEnvHistory("", history=envHistory)
2271            self.setMultiprocessNoDebugHistory("", history=noDebugHistory)
2272
2273    def __doRestart(self):
2274        """
2275        Private slot to handle the restart action to restart the last
2276        debugged file.
2277        """
2278        self.__resetUI()
2279        doNotStart = False
2280
2281        # first save any changes
2282        if self.lastStartAction in [1, 3, 5, 7, 9]:
2283            editor = self.viewmanager.getOpenEditor(self.lastDebuggedFile)
2284            if (
2285                editor and
2286                not self.viewmanager.checkDirty(
2287                    editor, Preferences.getDebugger("Autosave"))
2288            ):
2289                return
2290            forProject = False
2291        elif self.lastStartAction in [2, 4, 6, 8, 10]:
2292            if (
2293                Preferences.getDebugger("Autosave") and
2294                not self.project.saveAllScripts(reportSyntaxErrors=True)
2295            ):
2296                doNotStart = True
2297            self.__compileChangedProjectFiles()
2298            forProject = True
2299        else:
2300            return      # should not happen
2301
2302        # get the saved stuff
2303        venvName = self.lastUsedVenvName
2304        wd = self.wdHistory[0]
2305        argv = self.argvHistory[0]
2306        fn = self.lastDebuggedFile
2307        env = self.envHistory[0]
2308
2309        # Hide all error highlights
2310        self.viewmanager.unhighlight()
2311
2312        if not doNotStart:
2313            if forProject and self.project.getProjectType() in [
2314                    "E6Plugin"]:
2315                argv = '--plugin="{0}" {1}'.format(fn, argv)
2316                fn = os.path.join(getConfig('ericDir'), "eric6.py")
2317
2318            self.debugViewer.initCallStackViewer(forProject)
2319
2320            if self.lastStartAction in [1, 2]:
2321                # Ask the client to send call trace info
2322                enableCallTrace = self.debugViewer.isCallTraceEnabled()
2323                self.debugViewer.clearCallTrace()
2324                self.debugViewer.setCallTraceToProjectMode(forProject)
2325                multiprocessNoDebug = self.multiprocessNoDebugHistory[0]
2326
2327                # Ask the client to debug the new program.
2328                self.debugServer.remoteLoad(
2329                    venvName, fn, argv, wd, env,
2330                    autoClearShell=self.autoClearShell,
2331                    tracePython=self.tracePython,
2332                    autoContinue=self.autoContinue,
2333                    forProject=forProject,
2334                    runInConsole=self.runInConsole,
2335                    clientType=self.clientType,
2336                    enableCallTrace=enableCallTrace,
2337                    enableMultiprocess=self.enableMultiprocess,
2338                    multiprocessNoDebug=multiprocessNoDebug,
2339                    configOverride=self.overrideGlobalConfig)
2340
2341                # Signal that we have started a debugging session
2342                self.debuggingStarted.emit(fn)
2343
2344            elif self.lastStartAction in [3, 4]:
2345                # Ask the client to run the new program.
2346                self.debugServer.remoteRun(
2347                    venvName, fn, argv, wd, env,
2348                    autoClearShell=self.autoClearShell,
2349                    forProject=forProject,
2350                    runInConsole=self.runInConsole,
2351                    clientType=self.clientType,
2352                    configOverride=self.overrideGlobalConfig)
2353
2354            elif self.lastStartAction in [5, 6]:
2355                # Ask the client to coverage run the new program.
2356                self.debugServer.remoteCoverage(
2357                    venvName, fn, argv, wd, env,
2358                    autoClearShell=self.autoClearShell,
2359                    erase=self.eraseCoverage,
2360                    forProject=forProject,
2361                    runInConsole=self.runInConsole,
2362                    clientType=self.clientType,
2363                    configOverride=self.overrideGlobalConfig)
2364
2365            elif self.lastStartAction in [7, 8]:
2366                # Ask the client to profile run the new program.
2367                self.debugServer.remoteProfile(
2368                    venvName, fn, argv, wd, env,
2369                    autoClearShell=self.autoClearShell,
2370                    erase=self.eraseTimings,
2371                    forProject=forProject,
2372                    runInConsole=self.runInConsole,
2373                    clientType=self.clientType,
2374                    configOverride=self.overrideGlobalConfig)
2375
2376            self.stopAct.setEnabled(True)
2377
2378    def __stopScript(self):
2379        """
2380        Private slot to stop the running script.
2381        """
2382        self.debugServer.startClient(False)
2383
2384    def __passiveDebugStarted(self, fn, exc):
2385        """
2386        Private slot to handle a passive debug session start.
2387
2388        @param fn filename of the debugged script
2389        @param exc flag to enable exception reporting of the IDE (boolean)
2390        """
2391        # Hide all error highlights
2392        self.viewmanager.unhighlight()
2393
2394        # Set filename of script being debugged
2395        self.ui.currentProg = fn
2396
2397        # Set exception reporting
2398        self.setExceptionReporting(exc)
2399
2400        # Signal that we have started a debugging session
2401        self.debuggingStarted.emit(fn)
2402
2403        # Initialize the call stack viewer
2404        self.debugViewer.initCallStackViewer(False)
2405
2406    def __continue(self, debuggerId=""):
2407        """
2408        Private method to handle the Continue action.
2409
2410        @param debuggerId ID of the debugger backend
2411        @type str
2412        """
2413        if not debuggerId:
2414            debuggerId = self.getSelectedDebuggerId()
2415
2416        self.lastAction = 0
2417        self.__enterRemote()
2418        self.debugServer.remoteContinue(debuggerId)
2419
2420    def __specialContinue(self, debuggerId=""):
2421        """
2422        Private method to handle the Special Continue action.
2423
2424        @param debuggerId ID of the debugger backend
2425        @type str
2426        """
2427        if not debuggerId:
2428            debuggerId = self.getSelectedDebuggerId()
2429
2430        self.lastAction = 2
2431        self.__enterRemote()
2432        self.debugServer.remoteContinue(debuggerId, 1)
2433
2434    def __step(self, debuggerId=""):
2435        """
2436        Private method to handle the Step action.
2437
2438        @param debuggerId ID of the debugger backend
2439        @type str
2440        """
2441        if not debuggerId:
2442            debuggerId = self.getSelectedDebuggerId()
2443
2444        self.lastAction = 1
2445        self.__enterRemote()
2446        self.debugServer.remoteStep(debuggerId)
2447
2448    def __stepOver(self, debuggerId=""):
2449        """
2450        Private method to handle the Step Over action.
2451
2452        @param debuggerId ID of the debugger backend
2453        @type str
2454        """
2455        if not debuggerId:
2456            debuggerId = self.getSelectedDebuggerId()
2457
2458        self.lastAction = 2
2459        self.__enterRemote()
2460        self.debugServer.remoteStepOver(debuggerId)
2461
2462    def __stepOut(self, debuggerId=""):
2463        """
2464        Private method to handle the Step Out action.
2465
2466        @param debuggerId ID of the debugger backend
2467        @type str
2468        """
2469        if not debuggerId:
2470            debuggerId = self.getSelectedDebuggerId()
2471
2472        self.lastAction = 3
2473        self.__enterRemote()
2474        self.debugServer.remoteStepOut(debuggerId)
2475
2476    def __stepQuit(self, debuggerId=""):
2477        """
2478        Private method to handle the Step Quit action.
2479
2480        @param debuggerId ID of the debugger backend
2481        @type str
2482        """
2483        if not debuggerId:
2484            debuggerId = self.getSelectedDebuggerId()
2485
2486        self.lastAction = 4
2487        self.__enterRemote()
2488        self.debugServer.remoteStepQuit(debuggerId)
2489        self.__resetUI()
2490
2491    def __runToCursor(self, debuggerId=""):
2492        """
2493        Private method to handle the Run to Cursor action.
2494
2495        @param debuggerId ID of the debugger backend
2496        @type str
2497        """
2498        if not debuggerId:
2499            debuggerId = self.getSelectedDebuggerId()
2500
2501        self.lastAction = 0
2502        aw = self.viewmanager.activeWindow()
2503        line = aw.getCursorPosition()[0] + 1
2504        self.__enterRemote()
2505        self.debugServer.remoteBreakpoint(
2506            self.getSelectedDebuggerId(),
2507            aw.getFileName(), line, 1, None, 1)
2508        self.debugServer.remoteContinue(debuggerId)
2509
2510    def __runUntil(self, debuggerId=""):
2511        """
2512        Private method to handle the Run Until action.
2513
2514        @param debuggerId ID of the debugger backend
2515        @type str
2516        """
2517        if not debuggerId:
2518            debuggerId = self.getSelectedDebuggerId()
2519
2520        self.lastAction = 0
2521        aw = self.viewmanager.activeWindow()
2522        line = aw.getCursorPosition()[0] + 1
2523        self.__enterRemote()
2524        self.debugServer.remoteContinueUntil(debuggerId, line)
2525
2526    def __moveInstructionPointer(self, debuggerId=""):
2527        """
2528        Private method to move the instruction pointer to a different line.
2529
2530        @param debuggerId ID of the debugger backend
2531        @type str
2532        """
2533        if not debuggerId:
2534            debuggerId = self.getSelectedDebuggerId()
2535
2536        self.lastAction = 0
2537        aw = self.viewmanager.activeWindow()
2538        line = aw.getCursorPosition()[0] + 1
2539        self.debugServer.remoteMoveIP(debuggerId, line)
2540
2541    def __enterRemote(self):
2542        """
2543        Private method to update the user interface.
2544
2545        This method is called just prior to executing some of
2546        the program being debugged.
2547        """
2548        # Disable further debug commands from the user.
2549        self.debugActGrp.setEnabled(False)
2550
2551        self.viewmanager.unhighlight(True)
2552
2553    def getActions(self):
2554        """
2555        Public method to get a list of all actions.
2556
2557        @return list of all actions (list of E5Action)
2558        """
2559        return self.actions[:]
2560
2561    def getSelectedDebuggerId(self):
2562        """
2563        Public method to get the currently selected debugger ID.
2564
2565        @return selected debugger ID
2566        @rtype str
2567        """
2568        return self.debugViewer.getSelectedDebuggerId()
2569
2570    def setDebugActionsEnabled(self, enable):
2571        """
2572        Public method to set the enabled state of the debug actions.
2573
2574        @param enable enable state to be set
2575        @type bool
2576        """
2577        self.debugActGrp.setEnabled(enable)
2578
2579    def setMultiprocessNoDebugHistory(self, noDebugList, clearHistories=False,
2580                                      history=None):
2581        """
2582        Public slot to initialize the no debug list history.
2583
2584        @param noDebugList whitespace separated list of programs not to be
2585            debugged
2586        @type str
2587        @param clearHistories flag indicating, that the list should be cleared
2588        @type bool
2589        @param history list of history entries to be set
2590        @type list of str
2591        """
2592        if clearHistories:
2593            del self.multiprocessNoDebugHistory[1:]
2594        elif history is not None:
2595            self.multiprocessNoDebugHistory = history[:]
2596        else:
2597            if noDebugList in self.multiprocessNoDebugHistory:
2598                self.multiprocessNoDebugHistory.remove(noDebugList)
2599            self.multiprocessNoDebugHistory.insert(0, noDebugList)
2600
2601    def setEnableMultiprocess(self, enableMultiprocess):
2602        """
2603        Public slot to initialize the enableMultiprocess flag.
2604
2605        @param enableMultiprocess flag indicating, that the debugger should be
2606            run in multi process mode
2607        @type bool
2608        """
2609        self.enableMultiprocess = enableMultiprocess
2610
2611    def setEnableGlobalConfigOverride(self, overrideData):
2612        """
2613        Public method to initialize the global config override data.
2614
2615        @param overrideData dictionary containing a flag indicating to enable
2616            global config override and a flag indicating to redirect
2617            stdin/stdout/stderr
2618        @type dict
2619        """
2620        self.overrideGlobalConfig = copy.deepcopy(overrideData)
2621