eric7/QScintilla/Shell.py

branch
eric7
changeset 8312
800c432b34c8
parent 8265
0090cfa83159
child 8318
962bce857696
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2002 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a graphical Python shell.
8 """
9
10 import sys
11 import re
12 import contextlib
13 import enum
14
15 from PyQt5.QtCore import pyqtSignal, pyqtSlot, QFileInfo, Qt, QEvent
16 from PyQt5.QtGui import QClipboard, QPalette, QFont
17 from PyQt5.QtWidgets import (
18 QDialog, QInputDialog, QApplication, QMenu, QWidget, QHBoxLayout,
19 QVBoxLayout, QShortcut, QSizePolicy
20 )
21 from PyQt5.Qsci import QsciScintilla
22
23 from E5Gui.E5Application import e5App
24 from E5Gui import E5MessageBox
25
26 from .QsciScintillaCompat import QsciScintillaCompat
27
28 import Preferences
29 import Utilities
30
31 import UI.PixmapCache
32
33 from Debugger.DebugClientCapabilities import HasCompleter
34
35
36 class ShellAssembly(QWidget):
37 """
38 Class implementing the containing widget for the shell.
39 """
40 def __init__(self, dbs, vm, project, horizontal=True, parent=None):
41 """
42 Constructor
43
44 @param dbs reference to the debug server object
45 @type DebugServer
46 @param vm reference to the viewmanager object
47 @type ViewManager
48 @param project reference to the project object
49 @type Project
50 @param horizontal flag indicating a horizontal layout
51 @type bool
52 @param parent parent widget
53 @type QWidget
54 """
55 super().__init__(parent)
56
57 self.__shell = Shell(dbs, vm, project, False, self)
58
59 from UI.SearchWidget import SearchWidget
60 self.__searchWidget = SearchWidget(self.__shell, self, horizontal)
61 self.__searchWidget.setSizePolicy(QSizePolicy.Policy.Fixed,
62 QSizePolicy.Policy.Preferred)
63 self.__searchWidget.hide()
64
65 if horizontal:
66 self.__layout = QHBoxLayout(self)
67 else:
68 self.__layout = QVBoxLayout(self)
69 self.__layout.setContentsMargins(1, 1, 1, 1)
70 self.__layout.addWidget(self.__shell)
71 self.__layout.addWidget(self.__searchWidget)
72
73 self.__searchWidget.searchNext.connect(self.__shell.searchNext)
74 self.__searchWidget.searchPrevious.connect(self.__shell.searchPrev)
75 self.__shell.searchStringFound.connect(
76 self.__searchWidget.searchStringFound)
77
78 def showFind(self, txt=""):
79 """
80 Public method to display the search widget.
81
82 @param txt text to be shown in the combo (string)
83 """
84 self.__searchWidget.showFind(txt)
85
86 def shell(self):
87 """
88 Public method to get a reference to the shell widget.
89
90 @return reference to the shell widget (Shell)
91 """
92 return self.__shell
93
94
95 class ShellHistoryStyle(enum.Enum):
96 """
97 Class defining the shell history styles.
98 """
99 DISABLED = 0
100 LINUXSTYLE = 1
101 WINDOWSSTYLE = 2
102
103
104 class Shell(QsciScintillaCompat):
105 """
106 Class implementing a graphical Python shell.
107
108 A user can enter commands that are executed in the remote
109 Python interpreter.
110
111 @signal searchStringFound(bool) emitted to indicate the search
112 result
113 @signal historyStyleChanged(ShellHistoryStyle) emitted to indicate a
114 change of the history style
115 @signal queueText(str) emitted to queue some text for processing
116 @signal virtualEnvironmentChanged(str) emitted to signal the new virtual
117 environment of the shell
118 """
119 searchStringFound = pyqtSignal(bool)
120 historyStyleChanged = pyqtSignal(ShellHistoryStyle)
121 queueText = pyqtSignal(str)
122 virtualEnvironmentChanged = pyqtSignal(str)
123
124 def __init__(self, dbs, vm, project, windowedVariant, parent=None):
125 """
126 Constructor
127
128 @param dbs reference to the debug server object
129 @type DebugServer
130 @param vm reference to the viewmanager object
131 @type ViewManager
132 @param project reference to the project object
133 @type Project
134 @param windowedVariant flag indicating the shell window variant
135 @type bool
136 @param parent parent widget
137 @type QWidget
138 """
139 super().__init__(parent)
140 self.setUtf8(True)
141
142 self.vm = vm
143 self.__mainWindow = parent
144 self.__lastSearch = ()
145 self.__windowed = windowedVariant
146 self.__currentVenv = ""
147 self.__currentWorkingDirectory = ""
148
149 self.linesepRegExp = r"\r\n|\n|\r"
150
151 self.passive = ((not self.__windowed) and
152 Preferences.getDebugger("PassiveDbgEnabled"))
153 if self.passive:
154 self.setWindowTitle(self.tr('Shell - Passive'))
155 else:
156 self.setWindowTitle(self.tr('Shell'))
157
158 if self.__windowed:
159 self.setWhatsThis(self.tr(
160 """<b>The Shell Window</b>"""
161 """<p>You can use the cursor keys while entering commands."""
162 """ There is also a history of commands that can be recalled"""
163 """ using the up and down cursor keys while holding down the"""
164 """ Ctrl-key. This can be switched to just the up and down"""
165 """ cursor keys on the Shell page of the configuration"""
166 """ dialog. Pressing these keys after some text has been"""
167 """ entered will start an incremental search.</p>"""
168 """<p>The shell has some special commands. '%restart' kills"""
169 """ the shell and starts a new one. '%clear' clears the"""
170 """ display of the shell window. '%start' is used to start a"""
171 """ shell for a virtual environment and should be followed"""
172 """ by a virtual environment name. '%start' without a"""
173 """ virtual environment name starts the default shell."""
174 """ Available virtual environments may be listed with the"""
175 """ '%envs' or '%environments' commands. The active virtual"""
176 """ environment can be questioned by the '%which' command."""
177 """ '%quit' or '%exit' is used to exit the application."""
178 """ These commands (except '%environments', '%envs' and"""
179 """ '%which') are available through the window menus as"""
180 """ well.</p>"""
181 """<p>Pressing the Tab key after some text has been entered"""
182 """ will show a list of possible completions. The relevant"""
183 """ entry may be selected from this list. If only one entry"""
184 """ is available, this will be inserted automatically.</p>"""
185 ))
186 else:
187 self.setWhatsThis(self.tr(
188 """<b>The Shell Window</b>"""
189 """<p>This is simply an interpreter running in a window. The"""
190 """ interpreter is the one that is used to run the program"""
191 """ being debugged. This means that you can execute any"""
192 """ command while the program being debugged is running.</p>"""
193 """<p>You can use the cursor keys while entering commands."""
194 """ There is also a history of commands that can be recalled"""
195 """ using the up and down cursor keys while holding down the"""
196 """ Ctrl-key. This can be switched to just the up and down"""
197 """ cursor keys on the Shell page of the configuration"""
198 """ dialog. Pressing these keys after some text has been"""
199 """ entered will start an incremental search.</p>"""
200 """<p>The shell has some special commands. '%restart' kills"""
201 """ the shell and starts a new one. '%clear' clears the"""
202 """ display of the shell window. '%start' is used to start a"""
203 """ shell for a virtual environment and should be followed"""
204 """ by a virtual environment name. '%start' without a"""
205 """ virtual environment name starts the default shell."""
206 """ Available virtual environments may be listed with the"""
207 """ '%envs' or '%environments' commands. The active virtual"""
208 """ environment can be questioned by the '%which' command."""
209 """ These commands (except '%environments' and '%envs') are"""
210 """ available through the context menu as well.</p>"""
211 """<p>Pressing the Tab key after some text has been entered"""
212 """ will show a list of possible completions. The relevant"""
213 """ entry may be selected from this list. If only one entry"""
214 """ is available, this will be inserted automatically.</p>"""
215 """<p>In passive debugging mode the shell is only available"""
216 """ after the program to be debugged has connected to the"""
217 """ IDE until it has finished. This is indicated by a"""
218 """ different prompt and by an indication in the window"""
219 """ caption.</p>"""
220 ))
221
222 self.userListActivated.connect(self.__completionListSelected)
223 self.linesChanged.connect(self.__resizeLinenoMargin)
224
225 if self.__windowed:
226 self.__showStdOutErr = True
227 else:
228 self.__showStdOutErr = Preferences.getShell("ShowStdOutErr")
229 if self.__showStdOutErr:
230 dbs.clientProcessStdout.connect(self.__writeStdOut)
231 dbs.clientProcessStderr.connect(self.__writeStdErr)
232 dbs.clientOutput.connect(self.__writeQueued)
233 dbs.clientStatement.connect(self.__clientStatement)
234 dbs.clientGone.connect(self.__initialise)
235 dbs.clientRawInput.connect(self.__raw_input)
236 dbs.clientBanner.connect(self.__writeBanner)
237 dbs.clientCompletionList.connect(self.__showCompletions)
238 dbs.clientCapabilities.connect(self.__clientCapabilities)
239 dbs.clientException.connect(self.__clientException)
240 dbs.clientSyntaxError.connect(self.__clientSyntaxError)
241 dbs.clientSignal.connect(self.__clientSignal)
242 dbs.mainClientExit.connect(self.__writePrompt)
243 self.dbs = dbs
244
245 # will register a method to get the debugger ID to work with
246 self.__getSelectedDebuggerId = None
247
248 # Make sure we have prompts.
249 if self.passive:
250 sys.ps1 = self.tr("Passive >>> ")
251 else:
252 try:
253 sys.ps1
254 except AttributeError:
255 sys.ps1 = ">>> "
256 try:
257 sys.ps2
258 except AttributeError:
259 sys.ps2 = "... "
260
261 # Initialize instance variables.
262 self.__initialise()
263 self.prline = 0
264 self.prcol = 0
265 self.inDragDrop = False
266 self.lexer_ = None
267 self.completionText = ""
268
269 self.clientType = ''
270
271 # Initialize history
272 self.__historyLists = {}
273 self.__maxHistoryEntries = Preferences.getShell("MaxHistoryEntries")
274 self.__historyStyle = Preferences.getShell("HistoryStyle")
275 self.__historyWrap = Preferences.getShell("HistoryWrap")
276 self.__history = []
277 self.__setHistoryIndex()
278 # remove obsolete shell histories (Python and Ruby)
279 for clientType in ["Python", "Ruby"]:
280 Preferences.Prefs.settings.remove("Shell/Histories/" + clientType)
281
282 # clear QScintilla defined keyboard commands
283 # we do our own handling through the view manager
284 self.clearAlternateKeys()
285 self.clearKeys()
286 self.__actionsAdded = False
287
288 if self.passive:
289 self.__getBanner()
290
291 if not self.__windowed:
292 # Create a little language context menu
293 self.lmenu = QMenu(self.tr('Start'))
294 self.lmenu.aboutToShow.connect(self.__showStartMenu)
295 self.lmenu.triggered.connect(self.__startDebugClient)
296
297 # Create the history context menu
298 self.hmenu = QMenu(self.tr('History'))
299 self.hmenu.addAction(self.tr('Select entry'), self.selectHistory)
300 self.hmenu.addAction(self.tr('Show'), self.showHistory)
301 self.hmenu.addAction(self.tr('Clear'), self.clearHistory)
302
303 # Create a little context menu
304 self.menu = QMenu(self)
305 self.menu.addAction(self.tr('Cut'), self.cut)
306 self.menu.addAction(self.tr('Copy'), self.copy)
307 self.menu.addAction(self.tr('Paste'), self.paste)
308 self.menu.addMenu(self.hmenu).setEnabled(self.isHistoryEnabled())
309
310 self.menu.addSeparator()
311 self.menu.addAction(self.tr('Find'), self.__find)
312 self.menu.addSeparator()
313 self.menu.addAction(self.tr('Clear'), self.clear)
314 self.menu.addAction(self.tr('Restart'), self.doRestart)
315 self.menu.addAction(
316 self.tr('Restart and Clear'), self.doClearRestart)
317 self.menu.addSeparator()
318 self.menu.addMenu(self.lmenu)
319 self.menu.addAction(self.tr('Active Name'), self.__showVenvName)
320 self.menu.addSeparator()
321 self.menu.addAction(self.tr("Configure..."), self.__configure)
322
323 self.__bindLexer()
324 self.__setTextDisplay()
325 self.__setMargin0()
326
327 # set the autocompletion and calltips function
328 self.__setAutoCompletion()
329 self.__setCallTips()
330
331 self.setWindowIcon(UI.PixmapCache.getIcon("eric"))
332
333 self.incrementalSearchString = ""
334 self.incrementalSearchActive = False
335
336 self.supportedEditorCommands = {
337 QsciScintilla.SCI_LINEDELETE: self.__clearCurrentLine,
338 QsciScintilla.SCI_TAB: self.__QScintillaTab,
339 QsciScintilla.SCI_NEWLINE: self.__QScintillaNewline,
340
341 QsciScintilla.SCI_DELETEBACK: self.__QScintillaDeleteBack,
342 QsciScintilla.SCI_CLEAR: self.__QScintillaDelete,
343 QsciScintilla.SCI_DELWORDLEFT: self.__QScintillaDeleteWordLeft,
344 QsciScintilla.SCI_DELWORDRIGHT: self.__QScintillaDeleteWordRight,
345 QsciScintilla.SCI_DELLINELEFT: self.__QScintillaDeleteLineLeft,
346 QsciScintilla.SCI_DELLINERIGHT: self.__QScintillaDeleteLineRight,
347
348 QsciScintilla.SCI_CHARLEFT: self.__QScintillaCharLeft,
349 QsciScintilla.SCI_CHARRIGHT: self.__QScintillaCharRight,
350 QsciScintilla.SCI_WORDLEFT: self.__QScintillaWordLeft,
351 QsciScintilla.SCI_WORDRIGHT: self.__QScintillaWordRight,
352 QsciScintilla.SCI_VCHOME: self.__QScintillaVCHome,
353 QsciScintilla.SCI_LINEEND: self.__QScintillaLineEnd,
354
355 QsciScintilla.SCI_LINEUP: self.__QScintillaCursorCommand,
356 QsciScintilla.SCI_LINEDOWN: self.__QScintillaCursorCommand,
357 QsciScintilla.SCI_LINESCROLLUP: self.__QScintillaCursorCommand,
358 QsciScintilla.SCI_LINESCROLLDOWN: self.__QScintillaCursorCommand,
359
360 QsciScintilla.SCI_PAGEUP: self.__QScintillaAutoCompletionCommand,
361 QsciScintilla.SCI_PAGEDOWN: self.__QScintillaAutoCompletionCommand,
362
363 QsciScintilla.SCI_CHARLEFTEXTEND: self.__QScintillaCharLeftExtend,
364 QsciScintilla.SCI_CHARRIGHTEXTEND: self.extendSelectionRight,
365 QsciScintilla.SCI_WORDLEFTEXTEND: self.__QScintillaWordLeftExtend,
366 QsciScintilla.SCI_WORDRIGHTEXTEND: self.extendSelectionWordRight,
367 QsciScintilla.SCI_VCHOMEEXTEND: self.__QScintillaVCHomeExtend,
368 QsciScintilla.SCI_LINEENDEXTEND: self.extendSelectionToEOL,
369
370 QsciScintilla.SCI_CANCEL: self.__QScintillaCancel,
371 }
372
373 self.__historyNavigateByCursor = (
374 Preferences.getShell("HistoryNavigateByCursor")
375 )
376
377 self.__queuedText = ''
378 self.__blockTextProcessing = False
379 self.queueText.connect(self.__concatenateText,
380 Qt.ConnectionType.QueuedConnection)
381
382 self.__project = project
383 if self.__project:
384 self.__project.projectOpened.connect(self.__projectOpened)
385 self.__project.projectClosed.connect(self.__projectClosed)
386
387 self.grabGesture(Qt.GestureType.PinchGesture)
388
389 def __showStartMenu(self):
390 """
391 Private slot to prepare the start submenu.
392 """
393 self.lmenu.clear()
394 venvManager = e5App().getObject("VirtualEnvManager")
395 for venvName in sorted(venvManager.getVirtualenvNames()):
396 self.lmenu.addAction(venvName)
397 if self.__project.isOpen():
398 self.lmenu.addSeparator()
399 self.lmenu.addAction(self.tr("Project"))
400
401 def __resizeLinenoMargin(self):
402 """
403 Private slot to resize the line numbers margin.
404 """
405 linenoMargin = Preferences.getShell("LinenoMargin")
406 if linenoMargin:
407 self.setMarginWidth(0, '8' * (len(str(self.lines())) + 1))
408
409 def closeShell(self):
410 """
411 Public method to shutdown the shell.
412 """
413 for clientType in self.__historyLists:
414 self.saveHistory(clientType)
415
416 def __bindLexer(self, language='Python3'):
417 """
418 Private slot to set the lexer.
419
420 @param language lexer language to set (string)
421 """
422 self.language = language
423 if Preferences.getShell("SyntaxHighlightingEnabled"):
424 from . import Lexers
425 self.lexer_ = Lexers.getLexer(self.language, self)
426 else:
427 self.lexer_ = None
428
429 if self.lexer_ is None:
430 self.setLexer(None)
431 font = Preferences.getShell("MonospacedFont")
432 self.monospacedStyles(font)
433 return
434
435 # get the font for style 0 and set it as the default font
436 key = 'Scintilla/{0}/style0/font'.format(self.lexer_.language())
437 fdesc = Preferences.Prefs.settings.value(key)
438 if fdesc is not None:
439 font = QFont(fdesc[0], int(fdesc[1]))
440 self.lexer_.setDefaultFont(font)
441 self.setLexer(self.lexer_)
442 self.lexer_.readSettings(Preferences.Prefs.settings, "Scintilla")
443 if self.lexer_.hasSubstyles():
444 self.lexer_.readSubstyles(self)
445
446 # initialize the lexer APIs settings
447 api = self.vm.getAPIsManager().getAPIs(self.language)
448 if api is not None:
449 api = api.getQsciAPIs()
450 if api is not None:
451 self.lexer_.setAPIs(api)
452
453 self.lexer_.setDefaultColor(self.lexer_.color(0))
454 self.lexer_.setDefaultPaper(self.lexer_.paper(0))
455
456 def __setMargin0(self):
457 """
458 Private method to configure margin 0.
459 """
460 # set the settings for all margins
461 self.setMarginsFont(Preferences.getShell("MarginsFont"))
462 self.setMarginsForegroundColor(
463 Preferences.getEditorColour("MarginsForeground"))
464 self.setMarginsBackgroundColor(
465 Preferences.getEditorColour("MarginsBackground"))
466
467 # set margin 0 settings
468 linenoMargin = Preferences.getShell("LinenoMargin")
469 self.setMarginLineNumbers(0, linenoMargin)
470 if linenoMargin:
471 self.__resizeLinenoMargin()
472 else:
473 self.setMarginWidth(0, 0)
474
475 # disable margins 1 and 2
476 self.setMarginWidth(1, 0)
477 self.setMarginWidth(2, 0)
478
479 def __setTextDisplay(self):
480 """
481 Private method to configure the text display.
482 """
483 self.setTabWidth(Preferences.getEditor("TabWidth"))
484 if Preferences.getEditor("ShowWhitespace"):
485 self.setWhitespaceVisibility(
486 QsciScintilla.WhitespaceVisibility.WsVisible)
487 with contextlib.suppress(AttributeError):
488 self.setWhitespaceForegroundColor(
489 Preferences.getEditorColour("WhitespaceForeground"))
490 self.setWhitespaceBackgroundColor(
491 Preferences.getEditorColour("WhitespaceBackground"))
492 self.setWhitespaceSize(
493 Preferences.getEditor("WhitespaceSize"))
494 else:
495 self.setWhitespaceVisibility(
496 QsciScintilla.WhitespaceVisibility.WsInvisible)
497 self.setEolVisibility(Preferences.getEditor("ShowEOL"))
498 if Preferences.getEditor("BraceHighlighting"):
499 self.setBraceMatching(QsciScintilla.BraceMatch.SloppyBraceMatch)
500 else:
501 self.setBraceMatching(QsciScintilla.BraceMatch.NoBraceMatch)
502 self.setMatchedBraceForegroundColor(
503 Preferences.getEditorColour("MatchingBrace"))
504 self.setMatchedBraceBackgroundColor(
505 Preferences.getEditorColour("MatchingBraceBack"))
506 self.setUnmatchedBraceForegroundColor(
507 Preferences.getEditorColour("NonmatchingBrace"))
508 self.setUnmatchedBraceBackgroundColor(
509 Preferences.getEditorColour("NonmatchingBraceBack"))
510 if Preferences.getEditor("CustomSelectionColours"):
511 self.setSelectionBackgroundColor(
512 Preferences.getEditorColour("SelectionBackground"))
513 else:
514 self.setSelectionBackgroundColor(
515 QApplication.palette().color(QPalette.ColorRole.Highlight))
516 if Preferences.getEditor("ColourizeSelText"):
517 self.resetSelectionForegroundColor()
518 elif Preferences.getEditor("CustomSelectionColours"):
519 self.setSelectionForegroundColor(
520 Preferences.getEditorColour("SelectionForeground"))
521 else:
522 self.setSelectionForegroundColor(
523 QApplication.palette().color(
524 QPalette.ColorRole.HighlightedText))
525 self.setSelectionToEol(Preferences.getEditor("ExtendSelectionToEol"))
526 self.setCaretForegroundColor(
527 Preferences.getEditorColour("CaretForeground"))
528 self.setCaretLineVisible(False)
529 self.caretWidth = Preferences.getEditor("CaretWidth")
530 self.setCaretWidth(self.caretWidth)
531 if Preferences.getShell("WrapEnabled"):
532 self.setWrapMode(QsciScintilla.WrapMode.WrapWord)
533 else:
534 self.setWrapMode(QsciScintilla.WrapMode.WrapNone)
535 self.useMonospaced = Preferences.getShell("UseMonospacedFont")
536 self.__setMonospaced(self.useMonospaced)
537
538 self.setCursorFlashTime(QApplication.cursorFlashTime())
539
540 if Preferences.getEditor("OverrideEditAreaColours"):
541 self.setColor(Preferences.getEditorColour("EditAreaForeground"))
542 self.setPaper(Preferences.getEditorColour("EditAreaBackground"))
543
544 def __setMonospaced(self, on):
545 """
546 Private method to set/reset a monospaced font.
547
548 @param on flag to indicate usage of a monospace font (boolean)
549 """
550 if on:
551 if not self.lexer_:
552 f = Preferences.getShell("MonospacedFont")
553 self.monospacedStyles(f)
554 else:
555 if not self.lexer_:
556 self.clearStyles()
557 self.__setMargin0()
558 self.setFont(Preferences.getShell("MonospacedFont"))
559
560 self.useMonospaced = on
561
562 def __setAutoCompletion(self, language='Python'):
563 """
564 Private method to configure the autocompletion function.
565
566 @param language of the autocompletion set to set (string)
567 """
568 self.setAutoCompletionCaseSensitivity(
569 Preferences.getEditor("AutoCompletionCaseSensitivity"))
570 self.setAutoCompletionThreshold(-1)
571
572 self.racEnabled = Preferences.getShell("AutoCompletionEnabled")
573
574 self.maxLines = Preferences.getEditor("AutoCompletionMaxLines")
575 self.maxChars = Preferences.getEditor("AutoCompletionMaxChars")
576
577 def __setCallTips(self, language='Python'):
578 """
579 Private method to configure the calltips function.
580
581 @param language of the calltips set to set (string)
582 """
583 if Preferences.getShell("CallTipsEnabled"):
584 self.setCallTipsBackgroundColor(
585 Preferences.getEditorColour("CallTipsBackground"))
586 self.setCallTipsForegroundColor(
587 Preferences.getEditorColour("CallTipsForeground"))
588 self.setCallTipsHighlightColor(
589 Preferences.getEditorColour("CallTipsHighlight"))
590 self.setCallTipsVisible(Preferences.getEditor("CallTipsVisible"))
591 calltipsStyle = Preferences.getEditor("CallTipsStyle")
592 if calltipsStyle == QsciScintilla.CallTipsStyle.CallTipsNoContext:
593 self.setCallTipsStyle(
594 QsciScintilla.CallTipsStyle.CallTipsNoContext)
595 elif (
596 calltipsStyle ==
597 QsciScintilla.CallTipsStyle.CallTipsNoAutoCompletionContext
598 ):
599 self.setCallTipsStyle(
600 QsciScintilla.CallTipsStyle
601 .CallTipsNoAutoCompletionContext)
602 else:
603 self.setCallTipsStyle(
604 QsciScintilla.CallTipsStyle.CallTipsContext)
605 else:
606 self.setCallTipsStyle(QsciScintilla.CallTipsStyle.CallTipsNone)
607
608 def setDebuggerUI(self, ui):
609 """
610 Public method to set the debugger UI.
611
612 @param ui reference to the debugger UI object (DebugUI)
613 """
614 ui.exceptionInterrupt.connect(self.__writePrompt)
615 self.registerDebuggerIdMethod(ui.getSelectedDebuggerId)
616
617 def registerDebuggerIdMethod(self, method):
618 """
619 Public method to register a method to get the debugger ID to send data
620 to.
621
622 @param method reference to the method
623 @type function
624 """
625 self.__getSelectedDebuggerId = method
626
627 def __initialise(self):
628 """
629 Private method to get ready for a new remote interpreter.
630 """
631 self.buff = ""
632 self.inContinue = False
633 self.__inRawMode = False
634 self.__echoInput = True
635 self.__rawModeDebuggerId = None
636 self.__rawModeQueue = []
637 self.clientCapabilities = 0
638 self.inCommandExecution = False
639 self.interruptCommandExecution = False
640
641 def __clientCapabilities(self, cap, clType, venvName):
642 """
643 Private slot to handle the reporting of the clients capabilities.
644
645 @param cap client capabilities
646 @type int
647 @param clType type of the debug client
648 @type str
649 @param venvName name of the virtual environment
650 @type str
651 """
652 self.clientCapabilities = cap
653 self.__currentVenv = venvName
654 if clType != self.clientType:
655 self.clientType = clType
656 self.__bindLexer(self.clientType)
657 self.__setTextDisplay()
658 self.__setMargin0()
659 self.__setAutoCompletion(self.clientType)
660 self.__setCallTips(self.clientType)
661 self.racEnabled = (
662 Preferences.getShell("AutoCompletionEnabled") and
663 (cap & HasCompleter) > 0
664 )
665
666 if self.clientType not in self.__historyLists:
667 # load history list
668 self.loadHistory(self.clientType)
669 self.__history = self.__historyLists[self.clientType]
670 self.__setHistoryIndex()
671
672 self.virtualEnvironmentChanged.emit(venvName)
673 Preferences.setShell("LastVirtualEnvironment", venvName)
674
675 def __setHistoryIndex(self, index=None):
676 """
677 Private method to set the initial history index.
678
679 @param index index value to be set
680 @type int or None
681 """
682 if index is None:
683 # determine based on history style
684 if (
685 self.clientType and
686 self.__historyStyle == ShellHistoryStyle.WINDOWSSTYLE
687 ):
688 idx = int(Preferences.Prefs.settings.value(
689 "Shell/HistoryIndexes/" + self.clientType, -1))
690 if idx >= len(self.__history):
691 idx = -1
692 self.__histidx = idx
693 else:
694 self.__histidx = -1
695 else:
696 self.__histidx = index
697 if self.__histidx >= len(self.__history):
698 self.__histidx = -1
699 if (
700 self.clientType and
701 self.__historyStyle == ShellHistoryStyle.WINDOWSSTYLE
702 ):
703 Preferences.Prefs.settings.setValue(
704 "Shell/HistoryIndexes/" + self.clientType, self.__histidx)
705
706 def __isHistoryIndexValid(self):
707 """
708 Private method to test, if the history index is valid.
709
710 @return flag indicating validity
711 @rtype bool
712 """
713 return (0 <= self.__histidx < len(self.__history))
714
715 def getHistoryIndex(self):
716 """
717 Public method to get the current value of the history index.
718
719 @return history index
720 @rtype int
721 """
722 return self.__histidx
723
724 def loadHistory(self, clientType):
725 """
726 Public method to load the history for the given client type.
727
728 @param clientType type of the debug client (string)
729 """
730 hl = Preferences.Prefs.settings.value("Shell/Histories/" + clientType)
731 if hl is not None:
732 self.__historyLists[clientType] = hl[-self.__maxHistoryEntries:]
733 else:
734 self.__historyLists[clientType] = []
735
736 def reloadHistory(self):
737 """
738 Public method to reload the history of the currently selected client
739 type.
740 """
741 self.loadHistory(self.clientType)
742 self.__history = self.__historyLists[self.clientType]
743 self.__setHistoryIndex()
744
745 def saveHistory(self, clientType):
746 """
747 Public method to save the history for the given client type.
748
749 @param clientType type of the debug client (string)
750 """
751 if clientType in self.__historyLists:
752 Preferences.Prefs.settings.setValue(
753 "Shell/Histories/" + clientType,
754 self.__historyLists[clientType])
755
756 def getHistory(self, clientType):
757 """
758 Public method to get the history for the given client type.
759
760 @param clientType type of the debug client (string).
761 If it is None, the current history is returned.
762 @return reference to the history list (list of strings)
763 """
764 if clientType is None:
765 return self.__history
766 elif clientType in self.__historyLists:
767 return self.__historyLists[clientType]
768 else:
769 return []
770
771 def clearHistory(self):
772 """
773 Public slot to clear the current history.
774 """
775 if self.clientType:
776 self.__historyLists[self.clientType] = []
777 self.__history = self.__historyLists[self.clientType]
778 else:
779 self.__history = []
780 self.__setHistoryIndex(index=-1)
781
782 def selectHistory(self):
783 """
784 Public slot to select a history entry to execute.
785 """
786 current = self.__histidx
787 if current == -1:
788 current = len(self.__history) - 1
789 cmd, ok = QInputDialog.getItem(
790 self,
791 self.tr("Select History"),
792 self.tr("Select the history entry to execute"
793 " (most recent shown last)."),
794 self.__history,
795 current, False)
796 if ok:
797 self.__insertHistory(cmd)
798
799 def showHistory(self):
800 """
801 Public slot to show the shell history dialog.
802 """
803 from .ShellHistoryDialog import ShellHistoryDialog
804 dlg = ShellHistoryDialog(self.__history, self.vm, self)
805 if dlg.exec() == QDialog.DialogCode.Accepted:
806 self.__historyLists[self.clientType], idx = dlg.getHistory()
807 self.__history = self.__historyLists[self.clientType]
808 self.__setHistoryIndex(index=idx)
809
810 def clearAllHistories(self):
811 """
812 Public method to clear all available histories and sync them.
813 """
814 Preferences.Prefs.settings.beginGroup("Shell/Histories")
815 for clientType in Preferences.Prefs.settings.childKeys():
816 self.__historyLists[clientType] = []
817 self.saveHistory(clientType)
818 Preferences.Prefs.settings.endGroup()
819
820 self.clearHistory()
821
822 def getClientType(self):
823 """
824 Public slot to get the clients type.
825
826 @return client type (string)
827 """
828 return self.clientType
829
830 def __getBanner(self):
831 """
832 Private method to get the banner for the remote interpreter.
833
834 It requests the interpreter version and platform running on the
835 debug client side.
836 """
837 if self.passive:
838 self.__writeBanner('', '', '', '')
839 else:
840 self.dbs.remoteBanner()
841
842 def __writeBanner(self, version, platform, venvName):
843 """
844 Private method to write a banner with info from the debug client.
845
846 @param version interpreter version string
847 @type str
848 @param platform platform of the remote interpreter
849 @type str
850 @param venvName name of the virtual environment
851 @type str
852 """
853 super().clear()
854 if self.passive and not self.dbs.isConnected():
855 self.__write(self.tr('Passive Debug Mode'))
856 self.__write(self.tr('\nNot connected'))
857 else:
858 self.__currentVenv = venvName
859 version = version.replace("#", self.tr("No."))
860 if platform != "":
861 self.__write(self.tr('{0} on {1}').format(version, platform))
862 else:
863 self.__write(version)
864 if venvName:
865 self.__write("\n[{0}]".format(venvName))
866
867 self.virtualEnvironmentChanged.emit(venvName)
868 Preferences.setShell("LastVirtualEnvironment", venvName)
869 self.__write('\n')
870
871 self.__write(sys.ps1)
872
873 def __writePrompt(self):
874 """
875 Private method to write the prompt using a write queue.
876 """
877 self.queueText.emit(self.inContinue and sys.ps2 or sys.ps1)
878
879 def __clientStatement(self, more):
880 """
881 Private method to handle the response from the debugger client.
882
883 @param more flag indicating that more user input is required
884 @type bool
885 """
886 if not self.__inRawMode:
887 self.inContinue = more
888 self.__writePrompt()
889 self.inCommandExecution = False
890
891 def __clientException(self, exceptionType, exceptionMessage, stackTrace):
892 """
893 Private method to handle an exception of the client.
894
895 @param exceptionType type of exception raised (string)
896 @param exceptionMessage message given by the exception (string)
897 @param stackTrace list of stack entries (list of string)
898 """
899 self .__clientError()
900
901 if (
902 not self.__windowed and
903 Preferences.getDebugger("ShowExceptionInShell") and
904 exceptionType
905 ):
906 if stackTrace:
907 self.__write(
908 self.tr('Exception "{0}"\n{1}\nFile: {2}, Line: {3}\n')
909 .format(
910 exceptionType,
911 exceptionMessage,
912 stackTrace[0][0],
913 stackTrace[0][1]
914 )
915 )
916 else:
917 self.__write(
918 self.tr('Exception "{0}"\n{1}\n')
919 .format(
920 exceptionType,
921 exceptionMessage)
922 )
923
924 def __clientSyntaxError(self, message, filename, lineNo, characterNo):
925 """
926 Private method to handle a syntax error in the debugged program.
927
928 @param message message of the syntax error (string)
929 @param filename translated filename of the syntax error position
930 (string)
931 @param lineNo line number of the syntax error position (integer)
932 @param characterNo character number of the syntax error position
933 (integer)
934 """
935 self .__clientError()
936
937 if (
938 not self.__windowed and
939 Preferences.getDebugger("ShowExceptionInShell")
940 ):
941 if message is None:
942 self.__write(self.tr("Unspecified syntax error.\n"))
943 else:
944 self.__write(
945 self.tr('Syntax error "{1}" in file {0} at line {2},'
946 ' character {3}.\n')
947 .format(filename, message, lineNo, characterNo)
948 )
949
950 def __clientSignal(self, message, filename, lineNo, funcName, funcArgs):
951 """
952 Private method to handle a signal generated on the client side.
953
954 @param message message of the syntax error
955 @type str
956 @param filename translated filename of the syntax error position
957 @type str
958 @param lineNo line number of the syntax error position
959 @type int
960 @param funcName name of the function causing the signal
961 @type str
962 @param funcArgs function arguments
963 @type str
964 """
965 self.__clientError()
966
967 self.__write(
968 self.tr("""Signal "{0}" generated in file {1} at line {2}.\n"""
969 """Function: {3}({4})""")
970 .format(message, filename, lineNo, funcName, funcArgs)
971 )
972
973 def __clientError(self):
974 """
975 Private method to handle an error in the client.
976 """
977 self.inCommandExecution = False
978 self.interruptCommandExecution = True
979 self.inContinue = False
980
981 def __getEndPos(self):
982 """
983 Private method to return the line and column of the last character.
984
985 @return tuple of two values (int, int) giving the line and column
986 """
987 line = self.lines() - 1
988 return (line, len(self.text(line)))
989
990 def __writeQueued(self, s):
991 """
992 Private method to display some text using a write queue.
993
994 @param s text to be displayed (string)
995 """
996 self.queueText.emit(s)
997
998 def __concatenateText(self, text):
999 """
1000 Private slot to queue text and process it in one step.
1001
1002 @param text text to be appended
1003 @type str
1004 """
1005 self.__queuedText += text
1006 if self.__blockTextProcessing:
1007 return
1008
1009 self.__blockTextProcessing = True
1010 # Get all text which is still waiting for output
1011 QApplication.processEvents()
1012
1013 # Finally process the accumulated text
1014 self.__flushQueuedText()
1015
1016 def __flushQueuedText(self):
1017 """
1018 Private slot to flush the accumulated text output.
1019 """
1020 self.__write(self.__queuedText)
1021
1022 self.__queuedText = ''
1023 self.__blockTextProcessing = False
1024
1025 # little trick to get the cursor position registered within QScintilla
1026 self.SendScintilla(QsciScintilla.SCI_CHARLEFT)
1027 self.SendScintilla(QsciScintilla.SCI_CHARRIGHT)
1028
1029 def __write(self, s):
1030 """
1031 Private method to display some text without queuing.
1032
1033 @param s text to be displayed
1034 @type str
1035 """
1036 line, col = self.__getEndPos()
1037 self.setCursorPosition(line, col)
1038 self.insert(Utilities.filterAnsiSequences(s))
1039 self.prline, self.prcol = self.getCursorPosition()
1040 self.ensureCursorVisible()
1041 self.ensureLineVisible(self.prline)
1042
1043 def __writeStdOut(self, s):
1044 """
1045 Private method to display some text with StdOut label.
1046
1047 @param s text to be displayed (string)
1048 """
1049 self.__write(self.tr("StdOut: {0}").format(s))
1050
1051 def __writeStdErr(self, s):
1052 """
1053 Private method to display some text with StdErr label.
1054
1055 @param s text to be displayed (string)
1056 """
1057 self.__write(self.tr("StdErr: {0}").format(s))
1058
1059 def __raw_input(self, prompt, echo, debuggerId):
1060 """
1061 Private method to handle raw input.
1062
1063 @param prompt the input prompt
1064 @type str
1065 @param echo flag indicating an echoing of the input
1066 @type bool
1067 @param debuggerId ID of the debugger backend
1068 @type str
1069 """
1070 if self.__inRawMode:
1071 # we are processing another raw input event already
1072 self.__rawModeQueue.append((debuggerId, prompt, echo))
1073 else:
1074 self.setFocus()
1075 self.__inRawMode = True
1076 self.__echoInput = echo
1077 self.__rawModeDebuggerId = debuggerId
1078
1079 # Get all text which is still waiting for output
1080 QApplication.processEvents()
1081 self.__flushQueuedText()
1082
1083 self.__write(self.tr("<{0}> {1}").format(debuggerId, prompt))
1084 line, col = self.__getEndPos()
1085 self.setCursorPosition(line, col)
1086 buf = self.text(line)
1087 if buf.startswith(sys.ps1):
1088 buf = buf.replace(sys.ps1, "")
1089 if buf.startswith(sys.ps2):
1090 buf = buf.replace(sys.ps2, "")
1091 self.prompt = buf
1092 # move cursor to end of line
1093 self.moveCursorToEOL()
1094
1095 def paste(self, lines=None):
1096 """
1097 Public slot to handle the paste action.
1098
1099 @param lines list of lines to be inserted
1100 @type list of str
1101 """
1102 if self.__isCursorOnLastLine():
1103 line, col = self.getCursorPosition()
1104 lastLine = self.text(line)
1105 if lastLine.startswith(sys.ps1):
1106 lastLine = lastLine[len(sys.ps1):]
1107 col -= len(sys.ps1)
1108 prompt = sys.ps1
1109 elif lastLine.startswith(sys.ps2):
1110 lastLine = lastLine[len(sys.ps2):]
1111 col -= len(sys.ps2)
1112 prompt = sys.ps2
1113 else:
1114 prompt = ""
1115 if col < 0:
1116 col = 0
1117 prompt = ""
1118
1119 # Remove if text is selected
1120 if self.hasSelectedText():
1121 lineFrom, indexFrom, lineTo, indexTo = self.getSelection()
1122 if self.text(lineFrom).startswith(sys.ps1):
1123 indexFrom -= len(sys.ps1)
1124 indexTo -= len(sys.ps1)
1125 elif self.text(lineFrom).startswith(sys.ps2):
1126 indexFrom -= len(sys.ps2)
1127 indexTo -= len(sys.ps2)
1128 if indexFrom < 0:
1129 indexFrom = 0
1130 lastLine = lastLine[:indexFrom] + lastLine[indexTo:]
1131 col = indexFrom
1132
1133 self.setCursorPosition(line, len(prompt))
1134 self.deleteLineRight()
1135
1136 if lines is None:
1137 lines = QApplication.clipboard().text()
1138
1139 lines = lastLine[:col] + lines + lastLine[col:]
1140 self.executeLines(lines)
1141 line, _ = self.getCursorPosition()
1142 pos = len(self.text(line)) - (len(lastLine) - col)
1143 self.setCursorPosition(line, pos)
1144
1145 def executeLines(self, lines, historyIndex=None):
1146 """
1147 Public method to execute a set of lines as multiple commands.
1148
1149 @param lines multiple lines of text to be executed as
1150 single commands
1151 @type str
1152 @param historyIndex history index to be set
1153 @type int
1154 """
1155 lines = lines.splitlines(True)
1156 if not lines:
1157 return
1158
1159 indentLen = self.__indentLength(lines[0])
1160 for line in lines:
1161 if line.startswith(sys.ps1):
1162 line = line[len(sys.ps1) + indentLen:]
1163 elif line.startswith(sys.ps2):
1164 line = line[len(sys.ps2) + indentLen:]
1165 else:
1166 line = line[indentLen:]
1167
1168 if line.endswith(("\r\n", "\r", "\n")):
1169 fullline = True
1170 cmd = line.rstrip()
1171 else:
1172 fullline = False
1173
1174 self.incrementalSearchActive = True
1175 self.__insertTextAtEnd(line)
1176 if fullline:
1177 self.incrementalSearchActive = False
1178
1179 self.__executeCommand(cmd, historyIndex=historyIndex)
1180 if self.interruptCommandExecution:
1181 self.__executeCommand("")
1182 break
1183
1184 def __indentLength(self, line):
1185 """
1186 Private method to determine the indentation length of the given line.
1187
1188 @param line line to determine the indentation length for
1189 @type str
1190 @return indentation length
1191 @rtype int
1192 """
1193 if line.startswith(sys.ps1):
1194 line = line[len(sys.ps1):]
1195 # If line starts with sys.ps2 or neither don't manipulate the line.
1196 indentLen = len(line) - len(line.lstrip())
1197 return indentLen
1198
1199 def __clearCurrentLine(self):
1200 """
1201 Private method to clear the line containing the cursor.
1202 """
1203 line, col = self.getCursorPosition()
1204 if self.text(line).startswith(sys.ps1):
1205 col = len(sys.ps1)
1206 elif self.text(line).startswith(sys.ps2):
1207 col = len(sys.ps2)
1208 else:
1209 col = 0
1210 self.setCursorPosition(line, col)
1211 self.deleteLineRight()
1212
1213 def __insertText(self, s):
1214 """
1215 Private method to insert some text at the current cursor position.
1216
1217 @param s text to be inserted (string)
1218 """
1219 line, col = self.getCursorPosition()
1220 self.insertAt(Utilities.filterAnsiSequences(s), line, col)
1221 self.setCursorPosition(line, col + len(s))
1222
1223 def __insertTextAtEnd(self, s):
1224 """
1225 Private method to insert some text at the end of the command line.
1226
1227 @param s text to be inserted (string)
1228 """
1229 line, col = self.__getEndPos()
1230 self.setCursorPosition(line, col)
1231 self.insert(Utilities.filterAnsiSequences(s))
1232 self.prline, _ = self.getCursorPosition()
1233
1234 def __insertTextNoEcho(self, s):
1235 """
1236 Private method to insert some text at the end of the buffer without
1237 echoing it.
1238
1239 @param s text to be inserted (string)
1240 """
1241 self.buff += s
1242 self.prline, self.prcol = self.getCursorPosition()
1243
1244 def mousePressEvent(self, event):
1245 """
1246 Protected method to handle the mouse press event.
1247
1248 @param event the mouse press event (QMouseEvent)
1249 """
1250 self.setFocus()
1251 if event.button() == Qt.MouseButton.MidButton:
1252 lines = QApplication.clipboard().text(QClipboard.Mode.Selection)
1253 self.paste(lines)
1254 else:
1255 super().mousePressEvent(event)
1256
1257 def wheelEvent(self, evt):
1258 """
1259 Protected method to handle wheel events.
1260
1261 @param evt reference to the wheel event (QWheelEvent)
1262 """
1263 if evt.modifiers() & Qt.KeyboardModifier.ControlModifier:
1264 delta = evt.angleDelta().y()
1265 if delta < 0:
1266 self.zoomOut()
1267 elif delta > 0:
1268 self.zoomIn()
1269 evt.accept()
1270 return
1271
1272 super().wheelEvent(evt)
1273
1274 def event(self, evt):
1275 """
1276 Public method handling events.
1277
1278 @param evt reference to the event (QEvent)
1279 @return flag indicating, if the event was handled (boolean)
1280 """
1281 if evt.type() == QEvent.Type.Gesture:
1282 self.gestureEvent(evt)
1283 return True
1284
1285 return super().event(evt)
1286
1287 def gestureEvent(self, evt):
1288 """
1289 Protected method handling gesture events.
1290
1291 @param evt reference to the gesture event (QGestureEvent
1292 """
1293 pinch = evt.gesture(Qt.GestureType.PinchGesture)
1294 if pinch:
1295 if pinch.state() == Qt.GestureState.GestureStarted:
1296 zoom = (self.getZoom() + 10) / 10.0
1297 pinch.setTotalScaleFactor(zoom)
1298 elif pinch.state() == Qt.GestureState.GestureUpdated:
1299 zoom = int(pinch.totalScaleFactor() * 10) - 10
1300 if zoom <= -9:
1301 zoom = -9
1302 pinch.setTotalScaleFactor(0.1)
1303 elif zoom >= 20:
1304 zoom = 20
1305 pinch.setTotalScaleFactor(3.0)
1306 self.zoomTo(zoom)
1307 evt.accept()
1308
1309 def editorCommand(self, cmd):
1310 """
1311 Public method to perform an editor command.
1312
1313 @param cmd the scintilla command to be performed
1314 """
1315 try:
1316 self.supportedEditorCommands[cmd]()
1317 except TypeError:
1318 self.supportedEditorCommands[cmd](cmd)
1319 except KeyError:
1320 pass
1321
1322 def __isCursorOnLastLine(self):
1323 """
1324 Private method to check, if the cursor is on the last line.
1325
1326 @return flag indicating that the cursor is on the last line (boolean)
1327 """
1328 cline, ccol = self.getCursorPosition()
1329 return cline == self.lines() - 1
1330
1331 def keyPressEvent(self, ev):
1332 """
1333 Protected method to handle the user input a key at a time.
1334
1335 @param ev key event (QKeyEvent)
1336 """
1337 txt = ev.text()
1338
1339 # See it is text to insert.
1340 if len(txt) and txt >= " ":
1341 if not self.__isCursorOnLastLine():
1342 line, col = self.__getEndPos()
1343 self.setCursorPosition(line, col)
1344 self.prline, self.prcol = self.getCursorPosition()
1345 if self.__echoInput:
1346 ac = self.isListActive()
1347 super().keyPressEvent(ev)
1348 self.incrementalSearchActive = True
1349 if ac and self.racEnabled:
1350 self.dbs.remoteCompletion(
1351 self.__getSelectedDebuggerId(),
1352 self.completionText + txt
1353 )
1354 else:
1355 self.__insertTextNoEcho(txt)
1356 else:
1357 ev.ignore()
1358
1359 def __QScintillaCommand(self, cmd):
1360 """
1361 Private method to send the command to QScintilla.
1362
1363 @param cmd QScintilla command
1364 """
1365 self.SendScintilla(cmd)
1366
1367 def __QScintillaTab(self, cmd):
1368 """
1369 Private method to handle the Tab key.
1370
1371 @param cmd QScintilla command
1372 """
1373 if self.isListActive():
1374 self.SendScintilla(cmd)
1375 elif self.__isCursorOnLastLine():
1376 line, index = self.getCursorPosition()
1377 buf = self.text(line)
1378 if buf.startswith(sys.ps1):
1379 buf = buf.replace(sys.ps1, "")
1380 if buf.startswith(sys.ps2):
1381 buf = buf.replace(sys.ps2, "")
1382 if self.inContinue and not buf[:index - len(sys.ps2)].strip():
1383 self.SendScintilla(cmd)
1384 elif self.racEnabled:
1385 self.dbs.remoteCompletion(
1386 self.__getSelectedDebuggerId(),
1387 buf
1388 )
1389
1390 def __QScintillaLeftDeleteCommand(self, method):
1391 """
1392 Private method to handle a QScintilla delete command working to
1393 the left.
1394
1395 @param method shell method to execute
1396 """
1397 if self.__isCursorOnLastLine():
1398 line, col = self.getCursorPosition()
1399 db = 0
1400 ac = self.isListActive()
1401 oldLength = len(self.text(line))
1402
1403 if self.text(line).startswith(sys.ps1):
1404 if col > len(sys.ps1):
1405 method()
1406 db = 1
1407 elif self.text(line).startswith(sys.ps2):
1408 if col > len(sys.ps2):
1409 method()
1410 db = 1
1411 elif col > 0:
1412 method()
1413 db = 1
1414 if db and ac and self.racEnabled and self.completionText:
1415 delta = len(self.text(line)) - oldLength
1416 self.dbs.remoteCompletion(
1417 self.__getSelectedDebuggerId(),
1418 self.completionText[:delta]
1419 )
1420
1421 def __QScintillaDeleteBack(self):
1422 """
1423 Private method to handle the Backspace key.
1424 """
1425 self.__QScintillaLeftDeleteCommand(self.deleteBack)
1426
1427 def __QScintillaDeleteWordLeft(self):
1428 """
1429 Private method to handle the Delete Word Left command.
1430 """
1431 self.__QScintillaLeftDeleteCommand(self.deleteWordLeft)
1432
1433 def __QScintillaDelete(self):
1434 """
1435 Private method to handle the delete command.
1436 """
1437 if self.__isCursorOnLastLine():
1438 if self.hasSelectedText():
1439 lineFrom, indexFrom, lineTo, indexTo = self.getSelection()
1440 if self.text(lineFrom).startswith(sys.ps1):
1441 if indexFrom >= len(sys.ps1):
1442 self.delete()
1443 elif self.text(lineFrom).startswith(sys.ps2):
1444 if indexFrom >= len(sys.ps2):
1445 self.delete()
1446 elif indexFrom >= 0:
1447 self.delete()
1448 else:
1449 self.delete()
1450
1451 def __QScintillaDeleteLineLeft(self):
1452 """
1453 Private method to handle the Delete Line Left command.
1454 """
1455 if self.__isCursorOnLastLine():
1456 if self.isListActive():
1457 self.cancelList()
1458
1459 line, col = self.getCursorPosition()
1460 if self.text(line).startswith(sys.ps1):
1461 prompt = sys.ps1
1462 elif self.text(line).startswith(sys.ps2):
1463 prompt = sys.ps2
1464 else:
1465 prompt = ""
1466
1467 self.deleteLineLeft()
1468 self.insertAt(prompt, line, 0)
1469 self.setCursorPosition(line, len(prompt))
1470
1471 def __QScintillaNewline(self, cmd):
1472 """
1473 Private method to handle the Return key.
1474
1475 @param cmd QScintilla command
1476 """
1477 if self.__isCursorOnLastLine():
1478 if self.isListActive():
1479 self.SendScintilla(cmd)
1480 else:
1481 self.incrementalSearchString = ""
1482 self.incrementalSearchActive = False
1483 line, col = self.__getEndPos()
1484 self.setCursorPosition(line, col)
1485 buf = self.text(line)
1486 if buf.startswith(sys.ps1):
1487 buf = buf.replace(sys.ps1, "")
1488 if buf.startswith(sys.ps2):
1489 buf = buf.replace(sys.ps2, "")
1490 self.insert('\n')
1491 self.__executeCommand(buf)
1492 else:
1493 txt = ""
1494 line, col = self.getCursorPosition()
1495 if self.hasSelectedText():
1496 lineFrom, indexFrom, lineTo, indexTo = self.getSelection()
1497 if line == lineFrom:
1498 txt = self.text(line)[indexFrom:].rstrip()
1499 elif line == lineTo:
1500 txt = self.text(line)[:indexTo]
1501 else:
1502 txt = self.text(line)[col:].rstrip()
1503
1504 if txt:
1505 line, col = self.__getEndPos()
1506 self.setCursorPosition(line, col)
1507 self.insert(txt)
1508
1509 def __QScintillaLeftCommand(self, method, allLinesAllowed=False):
1510 """
1511 Private method to handle a QScintilla command working to the left.
1512
1513 @param method shell method to execute
1514 @param allLinesAllowed flag indicating that the command may be executed
1515 on any line (boolean)
1516 """
1517 if self.__isCursorOnLastLine() or allLinesAllowed:
1518 line, col = self.getCursorPosition()
1519 if self.text(line).startswith(sys.ps1):
1520 if col > len(sys.ps1):
1521 method()
1522 elif self.text(line).startswith(sys.ps2):
1523 if col > len(sys.ps2):
1524 method()
1525 elif col > 0:
1526 method()
1527 else:
1528 method()
1529
1530 def __QScintillaCharLeft(self):
1531 """
1532 Private method to handle the Cursor Left command.
1533 """
1534 self.__QScintillaLeftCommand(self.moveCursorLeft)
1535
1536 def __QScintillaWordLeft(self):
1537 """
1538 Private method to handle the Cursor Word Left command.
1539 """
1540 self.__QScintillaLeftCommand(self.moveCursorWordLeft)
1541
1542 def __QScintillaRightCommand(self, method):
1543 """
1544 Private method to handle a QScintilla command working to the right.
1545
1546 @param method shell method to execute
1547 """
1548 if self.__isCursorOnLastLine():
1549 method()
1550 else:
1551 method()
1552
1553 def __QScintillaCharRight(self):
1554 """
1555 Private method to handle the Cursor Right command.
1556 """
1557 self.__QScintillaRightCommand(self.moveCursorRight)
1558
1559 def __QScintillaWordRight(self):
1560 """
1561 Private method to handle the Cursor Word Right command.
1562 """
1563 self.__QScintillaRightCommand(self.moveCursorWordRight)
1564
1565 def __QScintillaDeleteWordRight(self):
1566 """
1567 Private method to handle the Delete Word Right command.
1568 """
1569 self.__QScintillaRightCommand(self.deleteWordRight)
1570
1571 def __QScintillaDeleteLineRight(self):
1572 """
1573 Private method to handle the Delete Line Right command.
1574 """
1575 self.__QScintillaRightCommand(self.deleteLineRight)
1576
1577 def __QScintillaVCHome(self, cmd):
1578 """
1579 Private method to handle the Home key.
1580
1581 @param cmd QScintilla command
1582 """
1583 if self.isListActive():
1584 self.SendScintilla(cmd)
1585 elif self.__isCursorOnLastLine():
1586 line, col = self.getCursorPosition()
1587 if self.text(line).startswith(sys.ps1):
1588 col = len(sys.ps1)
1589 elif self.text(line).startswith(sys.ps2):
1590 col = len(sys.ps2)
1591 else:
1592 col = 0
1593 self.setCursorPosition(line, col)
1594
1595 def __QScintillaLineEnd(self, cmd):
1596 """
1597 Private method to handle the End key.
1598
1599 @param cmd QScintilla command
1600 """
1601 if self.isListActive():
1602 self.SendScintilla(cmd)
1603 elif self.__isCursorOnLastLine():
1604 self.moveCursorToEOL()
1605
1606 def __QScintillaCursorCommand(self, cmd):
1607 """
1608 Private method to handle the cursor commands.
1609
1610 @param cmd QScintilla command
1611 """
1612 if self.isListActive() or self.isCallTipActive():
1613 if cmd in (QsciScintilla.SCI_LINEUP, QsciScintilla.SCI_LINEDOWN):
1614 self.SendScintilla(cmd)
1615 else:
1616 if self.__historyNavigateByCursor:
1617 if cmd == QsciScintilla.SCI_LINEUP:
1618 self.__QScintillaHistoryUp(cmd)
1619 elif cmd == QsciScintilla.SCI_LINEDOWN:
1620 self.__QScintillaHistoryDown(cmd)
1621 elif cmd == QsciScintilla.SCI_LINESCROLLUP:
1622 self.__QScintillaLineUp(cmd)
1623 elif cmd == QsciScintilla.SCI_LINESCROLLDOWN:
1624 self.__QScintillaLineDown(cmd)
1625 else:
1626 if cmd == QsciScintilla.SCI_LINEUP:
1627 self.__QScintillaLineUp(cmd)
1628 elif cmd == QsciScintilla.SCI_LINEDOWN:
1629 self.__QScintillaLineDown(cmd)
1630 elif cmd == QsciScintilla.SCI_LINESCROLLUP:
1631 self.__QScintillaHistoryUp(cmd)
1632 elif cmd == QsciScintilla.SCI_LINESCROLLDOWN:
1633 self.__QScintillaHistoryDown(cmd)
1634
1635 def __QScintillaLineUp(self, cmd):
1636 """
1637 Private method to handle the cursor up command.
1638
1639 @param cmd QScintilla command
1640 """
1641 self.SendScintilla(QsciScintilla.SCI_LINEUP)
1642
1643 def __QScintillaLineDown(self, cmd):
1644 """
1645 Private method to handle the cursor down command.
1646
1647 @param cmd QScintilla command
1648 """
1649 self.SendScintilla(QsciScintilla.SCI_LINEDOWN)
1650
1651 def __QScintillaHistoryUp(self, cmd):
1652 """
1653 Private method to handle the history up command.
1654
1655 @param cmd QScintilla command
1656 """
1657 if self.isHistoryEnabled():
1658 line, col = self.__getEndPos()
1659 buf = self.text(line)
1660 if buf.startswith(sys.ps1):
1661 buf = buf.replace(sys.ps1, "")
1662 if buf.startswith(sys.ps2):
1663 buf = buf.replace(sys.ps2, "")
1664 if buf and self.incrementalSearchActive:
1665 if (
1666 self.incrementalSearchString and
1667 buf.startswith(self.incrementalSearchString)
1668 ):
1669 idx, found = self.__rsearchHistory(
1670 self.incrementalSearchString, self.__histidx)
1671 if found and idx >= 0:
1672 self.__setHistoryIndex(index=idx)
1673 self.__useHistory()
1674 else:
1675 idx, found = self.__rsearchHistory(buf)
1676 if found and idx >= 0:
1677 self.__setHistoryIndex(index=idx)
1678 self.incrementalSearchString = buf
1679 self.__useHistory()
1680 else:
1681 if self.__historyWrap:
1682 if self.__histidx < 0:
1683 # wrap around
1684 self.__setHistoryIndex(index=len(self.__history) - 1)
1685 else:
1686 self.__setHistoryIndex(index=self.__histidx - 1)
1687 self.__useHistory()
1688 else:
1689 if self.__histidx < 0:
1690 self.__setHistoryIndex(index=len(self.__history) - 1)
1691 self.__useHistory()
1692 elif self.__histidx > 0:
1693 self.__setHistoryIndex(index=self.__histidx - 1)
1694 self.__useHistory()
1695
1696 def __QScintillaHistoryDown(self, cmd):
1697 """
1698 Private method to handle the history down command.
1699
1700 @param cmd QScintilla command
1701 """
1702 if self.isHistoryEnabled():
1703 line, col = self.__getEndPos()
1704 buf = self.text(line)
1705 if buf.startswith(sys.ps1):
1706 buf = buf.replace(sys.ps1, "")
1707 if buf.startswith(sys.ps2):
1708 buf = buf.replace(sys.ps2, "")
1709 if buf and self.incrementalSearchActive:
1710 if (
1711 self.incrementalSearchString and
1712 buf.startswith(self.incrementalSearchString)
1713 ):
1714 idx, found = self.__searchHistory(
1715 self.incrementalSearchString, self.__histidx)
1716 if found and idx >= 0:
1717 self.__setHistoryIndex(index=idx)
1718 self.__useHistory()
1719 else:
1720 idx, found = self.__searchHistory(buf)
1721 if found and idx >= 0:
1722 self.__setHistoryIndex(index=idx)
1723 self.incrementalSearchString = buf
1724 self.__useHistory()
1725 else:
1726 if self.__historyWrap:
1727 if self.__histidx >= len(self.__history) - 1:
1728 # wrap around
1729 self.__setHistoryIndex(index=0)
1730 else:
1731 self.__setHistoryIndex(index=self.__histidx + 1)
1732 self.__useHistory()
1733 else:
1734 if self.__isHistoryIndexValid():
1735 self.__setHistoryIndex(index=self.__histidx + 1)
1736 self.__useHistory()
1737
1738 def __QScintillaCancel(self):
1739 """
1740 Private method to handle the ESC command.
1741 """
1742 if self.isListActive() or self.isCallTipActive():
1743 self.SendScintilla(QsciScintilla.SCI_CANCEL)
1744 else:
1745 if self.incrementalSearchActive:
1746 self.__resetIncrementalHistorySearch()
1747 self.__insertHistory("")
1748
1749 def __QScintillaCharLeftExtend(self):
1750 """
1751 Private method to handle the Extend Selection Left command.
1752 """
1753 self.__QScintillaLeftCommand(self.extendSelectionLeft, True)
1754
1755 def __QScintillaWordLeftExtend(self):
1756 """
1757 Private method to handle the Extend Selection Left one word command.
1758 """
1759 self.__QScintillaLeftCommand(self.extendSelectionWordLeft, True)
1760
1761 def __QScintillaVCHomeExtend(self):
1762 """
1763 Private method to handle the Extend Selection to start of line command.
1764 """
1765 line, col = self.getCursorPosition()
1766 if self.text(line).startswith(sys.ps1):
1767 col = len(sys.ps1)
1768 elif self.text(line).startswith(sys.ps2):
1769 col = len(sys.ps2)
1770 else:
1771 col = 0
1772
1773 self.extendSelectionToBOL()
1774 while col > 0:
1775 self.extendSelectionRight()
1776 col -= 1
1777
1778 def __QScintillaAutoCompletionCommand(self, cmd):
1779 """
1780 Private method to handle a command for autocompletion only.
1781
1782 @param cmd QScintilla command
1783 """
1784 if self.isListActive() or self.isCallTipActive():
1785 self.SendScintilla(cmd)
1786
1787 def __executeCommand(self, cmd, historyIndex=None):
1788 """
1789 Private slot to execute a command.
1790
1791 @param cmd command to be executed by debug client
1792 @type str
1793 @param historyIndex history index to be set
1794 @type int
1795 """
1796 if not self.__inRawMode:
1797 self.inCommandExecution = True
1798 self.interruptCommandExecution = False
1799 if not cmd:
1800 # make sure cmd is a string
1801 cmd = ''
1802
1803 # History Handling
1804 if self.isHistoryEnabled():
1805 if cmd != "" and (
1806 len(self.__history) == 0 or self.__history[-1] != cmd):
1807 if len(self.__history) == self.__maxHistoryEntries:
1808 del self.__history[0]
1809 self.__history.append(cmd)
1810 if self.__historyStyle == ShellHistoryStyle.LINUXSTYLE:
1811 self.__setHistoryIndex(index=-1)
1812 elif self.__historyStyle == ShellHistoryStyle.WINDOWSSTYLE:
1813 if historyIndex is None:
1814 if (
1815 self.__histidx - 1 > 0 and
1816 cmd != self.__history[self.__histidx - 1]
1817 ):
1818 self.__setHistoryIndex(index=-1)
1819 else:
1820 self.__setHistoryIndex(historyIndex)
1821
1822 if cmd.startswith("%"):
1823 if cmd == '%start' or cmd.startswith('%start '):
1824 if not self.passive:
1825 cmdList = cmd.split(None, 1)
1826 if len(cmdList) < 2:
1827 self.dbs.startClient(False)
1828 # start default backend
1829 else:
1830 venvName = cmdList[1]
1831 if venvName == self.tr("Project"):
1832 if self.__project.isOpen():
1833 self.dbs.startClient(
1834 False,
1835 forProject=True,
1836 workingDir=self.__project
1837 .getProjectPath()
1838 )
1839 self.__currentWorkingDirectory = (
1840 self.__project.getProjectPath()
1841 )
1842 else:
1843 self.dbs.startClient(
1844 False,
1845 venvName=self.__currentVenv,
1846 workingDir=self
1847 .__currentWorkingDirectory
1848 )
1849 # same as reset
1850 else:
1851 self.dbs.startClient(False, venvName=venvName)
1852 self.__currentWorkingDirectory = ""
1853 self.__getBanner()
1854 return
1855 elif cmd == '%clear':
1856 # Display the banner.
1857 self.__getBanner()
1858 if not self.passive:
1859 return
1860 else:
1861 cmd = ''
1862 elif cmd in ['%reset', '%restart']:
1863 self.dbs.startClient(
1864 False, venvName=self.__currentVenv,
1865 workingDir=self.__currentWorkingDirectory)
1866 if self.passive:
1867 return
1868 else:
1869 cmd = ''
1870 elif cmd in ['%envs', '%environments']:
1871 venvs = (
1872 e5App().getObject("VirtualEnvManager")
1873 .getVirtualenvNames()
1874 )
1875 s = (
1876 self.tr('Available Virtual Environments:\n{0}\n')
1877 .format('\n'.join(
1878 "- {0}".format(venv)
1879 for venv in sorted(venvs)
1880 ))
1881 )
1882 self.__write(s)
1883 self.__clientStatement(False)
1884 return
1885 elif cmd == '%which':
1886 s = self.tr("Current Virtual Environment: '{0}'\n").format(
1887 self.__currentVenv)
1888 self.__write(s)
1889 self.__clientStatement(False)
1890 return
1891 elif (
1892 cmd in ["%quit", "%quit()", "%exit", "%exit()"] and
1893 self.__windowed
1894 ):
1895 # call main window quit()
1896 self.vm.quit()
1897 return
1898 else:
1899 self.dbs.remoteStatement(
1900 self.__getSelectedDebuggerId(), cmd)
1901 while self.inCommandExecution:
1902 with contextlib.suppress(KeyboardInterrupt):
1903 QApplication.processEvents()
1904 else:
1905 if not self.__echoInput:
1906 cmd = self.buff
1907 self.buff = ""
1908 elif cmd:
1909 cmd = cmd[len(self.prompt):]
1910 self.__inRawMode = False
1911 self.__echoInput = True
1912
1913 self.dbs.remoteRawInput(self.__rawModeDebuggerId, cmd)
1914
1915 if self.__rawModeQueue:
1916 debuggerId, prompt, echo = self.__rawModeQueue.pop(0)
1917 self.__raw_input(prompt, echo, debuggerId)
1918
1919 def __showVenvName(self):
1920 """
1921 Private method to show the name of the active virtual environment.
1922 """
1923 s = "\n" + self.tr("Current Virtual Environment: '{0}'\n").format(
1924 self.__currentVenv)
1925 self.__write(s)
1926 self.__clientStatement(False)
1927
1928 def __useHistory(self):
1929 """
1930 Private method to display a command from the history.
1931 """
1932 if self.__isHistoryIndexValid():
1933 cmd = self.__history[self.__histidx]
1934 else:
1935 cmd = ""
1936 self.__resetIncrementalHistorySearch()
1937
1938 self.__insertHistory(cmd)
1939
1940 def __insertHistory(self, cmd):
1941 """
1942 Private method to insert a command selected from the history.
1943
1944 @param cmd history entry to be inserted (string)
1945 """
1946 self.setCursorPosition(self.prline, self.prcol)
1947 self.setSelection(self.prline, self.prcol,
1948 self.prline, self.lineLength(self.prline))
1949 self.removeSelectedText()
1950 self.__insertText(cmd)
1951
1952 def __resetIncrementalHistorySearch(self):
1953 """
1954 Private method to reset the incremental history search.
1955 """
1956 self.incrementalSearchString = ""
1957 self.incrementalSearchActive = False
1958
1959 def __searchHistory(self, txt, startIdx=-1):
1960 """
1961 Private method used to search the history.
1962
1963 @param txt text to match at the beginning
1964 @type str
1965 @param startIdx index to start search from
1966 @type int
1967 @return tuple containing the index of found entry and a flag indicating
1968 that something was found
1969 @rtype tuple of (int, bool)
1970 """
1971 idx = 0 if startIdx == -1 else startIdx + 1
1972 while (
1973 idx < len(self.__history) and
1974 not self.__history[idx].startswith(txt)
1975 ):
1976 idx += 1
1977 found = (idx < len(self.__history) and
1978 self.__history[idx].startswith(txt))
1979 return idx, found
1980
1981 def __rsearchHistory(self, txt, startIdx=-1):
1982 """
1983 Private method used to reverse search the history.
1984
1985 @param txt text to match at the beginning
1986 @type str
1987 @param startIdx index to start search from
1988 @type int
1989 @return tuple containing the index of found entry and a flag indicating
1990 that something was found
1991 @rtype tuple of (int, bool)
1992 """
1993 idx = len(self.__history) - 1 if startIdx == -1 else startIdx - 1
1994 while (
1995 idx >= 0 and
1996 not self.__history[idx].startswith(txt)
1997 ):
1998 idx -= 1
1999 found = idx >= 0 and self.__history[idx].startswith(txt)
2000 return idx, found
2001
2002 def focusNextPrevChild(self, nextChild):
2003 """
2004 Public method to stop Tab moving to the next window.
2005
2006 While the user is entering a multi-line command, the movement to
2007 the next window by the Tab key being pressed is suppressed.
2008
2009 @param nextChild next window
2010 @return flag indicating the movement
2011 """
2012 if nextChild and self.inContinue:
2013 return False
2014
2015 return QsciScintillaCompat.focusNextPrevChild(self, nextChild)
2016
2017 def contextMenuEvent(self, ev):
2018 """
2019 Protected method to show our own context menu.
2020
2021 @param ev context menu event (QContextMenuEvent)
2022 """
2023 if not self.__windowed:
2024 self.menu.popup(ev.globalPos())
2025 ev.accept()
2026
2027 def clear(self):
2028 """
2029 Public slot to clear the display.
2030 """
2031 # Display the banner.
2032 self.__getBanner()
2033
2034 def doClearRestart(self):
2035 """
2036 Public slot to handle the 'restart and clear' context menu entry.
2037 """
2038 self.doRestart()
2039 self.clear()
2040
2041 def doRestart(self):
2042 """
2043 Public slot to handle the 'restart' context menu entry.
2044 """
2045 self.dbs.startClient(False, venvName=self.__currentVenv,
2046 workingDir=self.__currentWorkingDirectory)
2047
2048 def __startDebugClient(self, action):
2049 """
2050 Private slot to start a debug client according to the action
2051 triggered.
2052
2053 @param action context menu action that was triggered (QAction)
2054 """
2055 venvName = action.text()
2056 if venvName == self.tr("Project"):
2057 if self.__project.isOpen():
2058 self.__currentWorkingDirectory = (
2059 self.__project.getProjectPath()
2060 )
2061 self.dbs.startClient(False, forProject=True,
2062 workingDir=self.__currentWorkingDirectory)
2063 else:
2064 self.dbs.startClient(False, venvName=venvName)
2065 self.__getBanner()
2066
2067 def handlePreferencesChanged(self):
2068 """
2069 Public slot to handle the preferencesChanged signal.
2070 """
2071 # rebind the lexer
2072 self.__bindLexer(self.language)
2073 self.recolor()
2074
2075 # set margin 0 configuration
2076 self.__setTextDisplay()
2077 self.__setMargin0()
2078
2079 # set the autocompletion and calltips function
2080 self.__setAutoCompletion()
2081 self.__setCallTips()
2082
2083 # do the history related stuff
2084 self.__maxHistoryEntries = Preferences.getShell("MaxHistoryEntries")
2085 for key in list(self.__historyLists.keys()):
2086 self.__historyLists[key] = (
2087 self.__historyLists[key][-self.__maxHistoryEntries:]
2088 )
2089 self.__historyStyle = Preferences.getShell("HistoryStyle")
2090 self.__historyWrap = Preferences.getShell("HistoryWrap")
2091 self.__setHistoryIndex()
2092 if not self.__windowed:
2093 self.hmenu.menuAction().setEnabled(self.isHistoryEnabled())
2094 self.__historyNavigateByCursor = Preferences.getShell(
2095 "HistoryNavigateByCursor")
2096 self.historyStyleChanged.emit(self.__historyStyle)
2097
2098 # do stdout /stderr stuff
2099 showStdOutErr = Preferences.getShell("ShowStdOutErr")
2100 if self.__showStdOutErr != showStdOutErr:
2101 if showStdOutErr:
2102 self.dbs.clientProcessStdout.connect(self.__writeStdOut)
2103 self.dbs.clientProcessStderr.connect(self.__writeStdErr)
2104 else:
2105 self.dbs.clientProcessStdout.disconnect(self.__writeStdOut)
2106 self.dbs.clientProcessStderr.disconnect(self.__writeStdErr)
2107 self.__showStdOutErr = showStdOutErr
2108
2109 @pyqtSlot(list, str)
2110 def __showCompletions(self, completions, text):
2111 """
2112 Private method to display the possible completions.
2113
2114 @param completions list of possible completions (list of strings)
2115 @param text text that is about to be completed (string)
2116 """
2117 if len(completions) == 0:
2118 return
2119
2120 if len(completions) > 1:
2121 completions.sort()
2122 self.showUserList(1, completions)
2123 self.completionText = text
2124 else:
2125 txt = completions[0]
2126 if text != "":
2127 txt = txt.replace(text, "")
2128 self.__insertText(txt)
2129 self.completionText = ""
2130
2131 def __completionListSelected(self, listId, txt):
2132 """
2133 Private slot to handle the selection from the completion list.
2134
2135 @param listId the ID of the user list (should be 1) (integer)
2136 @param txt the selected text (string)
2137 """
2138 if listId == 1:
2139 if self.completionText != "":
2140 txt = txt.replace(self.completionText, "")
2141 self.__insertText(txt)
2142 self.completionText = ""
2143
2144 #################################################################
2145 ## Drag and Drop Support
2146 #################################################################
2147
2148 def dragEnterEvent(self, event):
2149 """
2150 Protected method to handle the drag enter event.
2151
2152 @param event the drag enter event (QDragEnterEvent)
2153 """
2154 self.inDragDrop = (
2155 event.mimeData().hasUrls() or
2156 event.mimeData().hasText()
2157 )
2158 if self.inDragDrop:
2159 event.acceptProposedAction()
2160 else:
2161 super().dragEnterEvent(event)
2162
2163 def dragMoveEvent(self, event):
2164 """
2165 Protected method to handle the drag move event.
2166
2167 @param event the drag move event (QDragMoveEvent)
2168 """
2169 if self.inDragDrop:
2170 event.accept()
2171 else:
2172 super().dragMoveEvent(event)
2173
2174 def dragLeaveEvent(self, event):
2175 """
2176 Protected method to handle the drag leave event.
2177
2178 @param event the drag leave event (QDragLeaveEvent)
2179 """
2180 if self.inDragDrop:
2181 self.inDragDrop = False
2182 event.accept()
2183 else:
2184 super().dragLeaveEvent(event)
2185
2186 def dropEvent(self, event):
2187 """
2188 Protected method to handle the drop event.
2189
2190 @param event the drop event (QDropEvent)
2191 """
2192 if event.mimeData().hasUrls() and not self.__windowed:
2193 for url in event.mimeData().urls():
2194 fname = url.toLocalFile()
2195 if fname:
2196 if not QFileInfo(fname).isDir():
2197 self.vm.openSourceFile(fname)
2198 else:
2199 E5MessageBox.information(
2200 self,
2201 self.tr("Drop Error"),
2202 self.tr("""<p><b>{0}</b> is not a file.</p>""")
2203 .format(fname))
2204 event.acceptProposedAction()
2205 elif event.mimeData().hasText():
2206 s = event.mimeData().text()
2207 if s:
2208 event.acceptProposedAction()
2209 self.executeLines(s)
2210 del s
2211 else:
2212 super().dropEvent(event)
2213
2214 self.inDragDrop = False
2215
2216 def focusInEvent(self, event):
2217 """
2218 Protected method called when the shell receives focus.
2219
2220 @param event the event object (QFocusEvent)
2221 """
2222 if not self.__actionsAdded:
2223 self.addActions(self.vm.editorActGrp.actions())
2224 self.addActions(self.vm.copyActGrp.actions())
2225 self.addActions(self.vm.viewActGrp.actions())
2226 if not self.__windowed:
2227 self.__searchShortcut = QShortcut(
2228 self.vm.searchAct.shortcut(), self,
2229 self.__find, self.__find)
2230 self.__searchNextShortcut = QShortcut(
2231 self.vm.searchNextAct.shortcut(), self,
2232 self.__searchNext, self.__searchNext)
2233 self.__searchPrevShortcut = QShortcut(
2234 self.vm.searchPrevAct.shortcut(), self,
2235 self.__searchPrev, self.__searchPrev)
2236
2237 with contextlib.suppress(AttributeError):
2238 self.vm.editActGrp.setEnabled(False)
2239 self.vm.editorActGrp.setEnabled(True)
2240 self.vm.copyActGrp.setEnabled(True)
2241 self.vm.viewActGrp.setEnabled(True)
2242 self.vm.searchActGrp.setEnabled(False)
2243 if not self.__windowed:
2244 self.__searchShortcut.setEnabled(True)
2245 self.__searchNextShortcut.setEnabled(True)
2246 self.__searchPrevShortcut.setEnabled(True)
2247 self.setCaretWidth(self.caretWidth)
2248 self.setCursorFlashTime(QApplication.cursorFlashTime())
2249
2250 super().focusInEvent(event)
2251
2252 def focusOutEvent(self, event):
2253 """
2254 Protected method called when the shell loses focus.
2255
2256 @param event the event object (QFocusEvent)
2257 """
2258 with contextlib.suppress(AttributeError):
2259 self.vm.editorActGrp.setEnabled(False)
2260 if not self.__windowed:
2261 self.__searchShortcut.setEnabled(False)
2262 self.__searchNextShortcut.setEnabled(False)
2263 self.__searchPrevShortcut.setEnabled(False)
2264 self.setCaretWidth(0)
2265 super().focusOutEvent(event)
2266
2267 def insert(self, txt):
2268 """
2269 Public slot to insert text at the current cursor position.
2270
2271 The cursor is advanced to the end of the inserted text.
2272
2273 @param txt text to be inserted (string)
2274 """
2275 txt = Utilities.filterAnsiSequences(txt)
2276 length = len(txt)
2277 line, col = self.getCursorPosition()
2278 self.insertAt(txt, line, col)
2279 if re.search(self.linesepRegExp, txt) is not None:
2280 line += 1
2281 self.setCursorPosition(line, col + length)
2282
2283 def __configure(self):
2284 """
2285 Private method to open the configuration dialog.
2286 """
2287 e5App().getObject("UserInterface").showPreferences("shellPage")
2288
2289 def __find(self):
2290 """
2291 Private slot to show the find widget.
2292 """
2293 txt = self.selectedText()
2294 self.__mainWindow.showFind(txt)
2295
2296 def __searchNext(self):
2297 """
2298 Private method to search for the next occurrence.
2299 """
2300 if self.__lastSearch:
2301 self.searchNext(*self.__lastSearch)
2302
2303 def searchNext(self, txt, caseSensitive, wholeWord, regexp):
2304 """
2305 Public method to search the next occurrence of the given text.
2306
2307 @param txt text to search for
2308 @type str
2309 @param caseSensitive flag indicating to perform a case sensitive
2310 search
2311 @type bool
2312 @param wholeWord flag indicating to search for whole words
2313 only
2314 @type bool
2315 @param regexp flag indicating a regular expression search
2316 @type bool
2317 """
2318 self.__lastSearch = (txt, caseSensitive, wholeWord, regexp)
2319 posixMode = Preferences.getEditor("SearchRegexpMode") == 0 and regexp
2320 cxx11Mode = Preferences.getEditor("SearchRegexpMode") == 1 and regexp
2321 ok = self.findFirst(
2322 txt, regexp, caseSensitive, wholeWord, True, forward=True,
2323 posix=posixMode, cxx11=cxx11Mode)
2324 self.searchStringFound.emit(ok)
2325
2326 def __searchPrev(self):
2327 """
2328 Private method to search for the next occurrence.
2329 """
2330 if self.__lastSearch:
2331 self.searchPrev(*self.__lastSearch)
2332
2333 def searchPrev(self, txt, caseSensitive, wholeWord, regexp):
2334 """
2335 Public method to search the previous occurrence of the given text.
2336
2337 @param txt text to search for
2338 @type str
2339 @param caseSensitive flag indicating to perform a case sensitive
2340 search
2341 @type bool
2342 @param wholeWord flag indicating to search for whole words
2343 only
2344 @type bool
2345 @param regexp flag indicating a regular expression search
2346 @type bool
2347 """
2348 self.__lastSearch = (txt, caseSensitive, wholeWord, regexp)
2349 if self.hasSelectedText():
2350 line, index = self.getSelection()[:2]
2351 else:
2352 line, index = -1, -1
2353 posixMode = Preferences.getEditor("SearchRegexpMode") == 0 and regexp
2354 cxx11Mode = Preferences.getEditor("SearchRegexpMode") == 1 and regexp
2355 ok = self.findFirst(
2356 txt, regexp, caseSensitive, wholeWord, True,
2357 forward=False, line=line, index=index, posix=posixMode,
2358 cxx11=cxx11Mode)
2359 self.searchStringFound.emit(ok)
2360
2361 def historyStyle(self):
2362 """
2363 Public method to get the shell history style.
2364
2365 @return shell history style
2366 @rtype ShellHistoryStyle
2367 """
2368 return self.__historyStyle
2369
2370 def isHistoryEnabled(self):
2371 """
2372 Public method to check, if the history is enabled.
2373
2374 @return flag indicating if history is enabled
2375 @rtype bool
2376 """
2377 return self.__historyStyle != ShellHistoryStyle.DISABLED
2378
2379 #################################################################
2380 ## Project Support
2381 #################################################################
2382
2383 def __projectOpened(self):
2384 """
2385 Private slot to start the shell for the opened project.
2386 """
2387 if Preferences.getProject("RestartShellForProject"):
2388 self.dbs.startClient(False, forProject=True,
2389 workingDir=self.__project.getProjectPath())
2390 self.__currentWorkingDirectory = self.__project.getProjectPath()
2391 self.__getBanner()
2392
2393 def __projectClosed(self):
2394 """
2395 Private slot to restart the default shell when the project is closed.
2396 """
2397 if Preferences.getProject("RestartShellForProject"):
2398 self.dbs.startClient(False)
2399 self.__getBanner()
2400
2401 #
2402 # eflag: noqa = M601

eric ide

mercurial