|
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 |