|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2002 - 2009 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 |
|
13 from PyQt4.QtCore import * |
|
14 from PyQt4.QtGui import * |
|
15 from PyQt4.Qsci import QsciScintilla |
|
16 |
|
17 from E4Gui.E4Application import e4App |
|
18 |
|
19 import Lexers |
|
20 from QsciScintillaCompat import QsciScintillaCompat, QSCINTILLA_VERSION |
|
21 |
|
22 import Preferences |
|
23 import UI.PixmapCache |
|
24 from Utilities import toUnicode |
|
25 |
|
26 from Debugger.DebugClientCapabilities import HasShell, HasCompleter |
|
27 |
|
28 from ShellHistoryDialog import ShellHistoryDialog |
|
29 |
|
30 class Shell(QsciScintillaCompat): |
|
31 """ |
|
32 Class implementing a graphical Python shell. |
|
33 |
|
34 A user can enter commands that are executed in the remote |
|
35 Python interpreter. |
|
36 """ |
|
37 def __init__(self, dbs, vm, parent = None): |
|
38 """ |
|
39 Constructor |
|
40 |
|
41 @param dbs reference to the debug server object |
|
42 @param vm reference to the viewmanager object |
|
43 @param parent parent widget (QWidget) |
|
44 """ |
|
45 QsciScintillaCompat.__init__(self, parent) |
|
46 self.setUtf8(True) |
|
47 |
|
48 self.vm = vm |
|
49 |
|
50 self.linesepRegExp = r"\r\n|\n|\r" |
|
51 |
|
52 self.passive = Preferences.getDebugger("PassiveDbgEnabled") |
|
53 if self.passive: |
|
54 self.setWindowTitle(self.trUtf8('Shell - Passive')) |
|
55 else: |
|
56 self.setWindowTitle(self.trUtf8('Shell')) |
|
57 |
|
58 self.setWhatsThis(self.trUtf8( |
|
59 """<b>The Shell Window</b>""" |
|
60 """<p>This is simply an interpreter running in a window. The""" |
|
61 """ interpreter is the one that is used to run the program being debugged.""" |
|
62 """ This means that you can execute any command while the program""" |
|
63 """ being debugged is running.</p>""" |
|
64 """<p>You can use the cursor keys while entering commands. There is also a""" |
|
65 """ history of commands that can be recalled using the up and down cursor""" |
|
66 """ keys. Pressing the up or down key after some text has been entered will""" |
|
67 """ start an incremental search.</p>""" |
|
68 """<p>The shell has some special commands. 'reset' kills the shell""" |
|
69 """ and starts a new one. 'clear' clears the display of the shell window.""" |
|
70 """ 'start' is used to switch the shell language and must be followed by""" |
|
71 """ a supported language. Supported languages are listed by the 'languages'""" |
|
72 """ command. These commands (except 'languages') are available through the""" |
|
73 """ context menu as well.</p>""" |
|
74 """<p>Pressing the Tab key after some text has been entered will show""" |
|
75 """ a list of possible commandline completions. The relevant entry may""" |
|
76 """ be selected from this list. If only one entry is available, this will""" |
|
77 """ inserted automatically.</p>""" |
|
78 """<p>In passive debugging mode the shell is only available after the""" |
|
79 """ program to be debugged has connected to the IDE until it has finished.""" |
|
80 """ This is indicated by a different prompt and by an indication in the""" |
|
81 """ window caption.</p>""" |
|
82 )) |
|
83 |
|
84 self.connect(self, SIGNAL('userListActivated(int, const QString)'), |
|
85 self.__completionListSelected) |
|
86 |
|
87 self.__showStdOutErr = Preferences.getShell("ShowStdOutErr") |
|
88 if self.__showStdOutErr: |
|
89 self.connect(dbs, SIGNAL('clientProcessStdout'), self.__writeStdOut) |
|
90 self.connect(dbs, SIGNAL('clientProcessStderr'), self.__writeStdErr) |
|
91 self.connect(dbs, SIGNAL('clientOutput'), self.__write) |
|
92 self.connect(dbs, SIGNAL('clientStatement'), self.__clientStatement) |
|
93 self.connect(dbs, SIGNAL('clientGone'), self.__initialise) |
|
94 self.connect(dbs, SIGNAL('clientRawInput'), self.__raw_input) |
|
95 self.connect(dbs, SIGNAL('clientBanner'), self.__writeBanner) |
|
96 self.connect(dbs, SIGNAL('clientCompletionList'), self.__showCompletions) |
|
97 self.connect(dbs, SIGNAL('clientCapabilities'), self.__clientCapabilities) |
|
98 self.connect(dbs, SIGNAL('clientException'), self.__clientError) |
|
99 self.connect(dbs, SIGNAL('clientSyntaxError'), self.__clientError) |
|
100 self.dbs = dbs |
|
101 |
|
102 # Initialise instance variables. |
|
103 self.__initialise() |
|
104 self.prline = 0 |
|
105 self.prcol = 0 |
|
106 self.inDragDrop = False |
|
107 self.lexer_ = None |
|
108 self.completionText = "" |
|
109 |
|
110 # Initialize history |
|
111 self.historyLists = {} |
|
112 self.maxHistoryEntries = Preferences.getShell("MaxHistoryEntries") |
|
113 self.history = [] |
|
114 self.histidx = -1 |
|
115 |
|
116 self.clientType = '' |
|
117 |
|
118 # clear QScintilla defined keyboard commands |
|
119 # we do our own handling through the view manager |
|
120 self.clearAlternateKeys() |
|
121 self.clearKeys() |
|
122 self.__actionsAdded = False |
|
123 |
|
124 # Make sure we have prompts. |
|
125 if self.passive: |
|
126 sys.ps1 = self.trUtf8("Passive >>> ") |
|
127 else: |
|
128 try: |
|
129 sys.ps1 |
|
130 except AttributeError: |
|
131 sys.ps1 = ">>> " |
|
132 try: |
|
133 sys.ps2 |
|
134 except AttributeError: |
|
135 sys.ps2 = "... " |
|
136 |
|
137 if self.passive: |
|
138 self.__getBanner() |
|
139 |
|
140 # Create a little language context menu |
|
141 self.lmenu = QMenu(self.trUtf8('Start')) |
|
142 self.clientLanguages = self.dbs.getSupportedLanguages(shellOnly = True) |
|
143 self.clientLanguages.sort() |
|
144 for language in self.clientLanguages: |
|
145 act = self.lmenu.addAction(language) |
|
146 act.setData(QVariant(language)) |
|
147 self.connect(self.lmenu, SIGNAL("triggered(QAction *)"), self.__startDebugClient) |
|
148 |
|
149 # Create the history context menu |
|
150 self.hmenu = QMenu(self.trUtf8('History')) |
|
151 self.hmenu.addAction(self.trUtf8('Select entry'), self.__selectHistory) |
|
152 self.hmenu.addAction(self.trUtf8('Show'), self.__showHistory) |
|
153 self.hmenu.addAction(self.trUtf8('Clear'), self.__clearHistory) |
|
154 |
|
155 # Create a little context menu |
|
156 self.menu = QMenu(self) |
|
157 self.menu.addAction(self.trUtf8('Cut'), self.cut) |
|
158 self.menu.addAction(self.trUtf8('Copy'), self.copy) |
|
159 self.menu.addAction(self.trUtf8('Paste'), self.paste) |
|
160 self.menu.addMenu(self.hmenu) |
|
161 self.menu.addSeparator() |
|
162 self.menu.addAction(self.trUtf8('Clear'), self.clear) |
|
163 self.menu.addAction(self.trUtf8('Reset'), self.__reset) |
|
164 self.menu.addAction(self.trUtf8('Reset and Clear'), |
|
165 self.__resetAndClear) |
|
166 self.menu.addSeparator() |
|
167 self.menu.addMenu(self.lmenu) |
|
168 self.menu.addSeparator() |
|
169 self.menu.addAction(self.trUtf8("Configure..."), self.__configure) |
|
170 |
|
171 self.__bindLexer() |
|
172 self.__setTextDisplay() |
|
173 self.__setMargin0() |
|
174 |
|
175 # set the autocompletion and calltips function |
|
176 self.__setAutoCompletion() |
|
177 self.__setCallTips() |
|
178 |
|
179 self.setWindowIcon(UI.PixmapCache.getIcon("eric.png")) |
|
180 |
|
181 self.incrementalSearchString = "" |
|
182 self.incrementalSearchActive = False |
|
183 |
|
184 self.supportedEditorCommands = { |
|
185 QsciScintilla.SCI_LINEDELETE : self.__clearCurrentLine, |
|
186 QsciScintilla.SCI_TAB : self.__QScintillaTab, |
|
187 QsciScintilla.SCI_NEWLINE : self.__QScintillaNewline, |
|
188 |
|
189 QsciScintilla.SCI_DELETEBACK : self.__QScintillaDeleteBack, |
|
190 QsciScintilla.SCI_CLEAR : self.__QScintillaDelete, |
|
191 QsciScintilla.SCI_DELWORDLEFT : self.__QScintillaDeleteWordLeft, |
|
192 QsciScintilla.SCI_DELWORDRIGHT : self.__QScintillaDeleteWordRight, |
|
193 QsciScintilla.SCI_DELLINELEFT : self.__QScintillaDeleteLineLeft, |
|
194 QsciScintilla.SCI_DELLINERIGHT : self.__QScintillaDeleteLineRight, |
|
195 |
|
196 QsciScintilla.SCI_CHARLEFT : self.__QScintillaCharLeft, |
|
197 QsciScintilla.SCI_CHARRIGHT : self.__QScintillaCharRight, |
|
198 QsciScintilla.SCI_WORDLEFT : self.__QScintillaWordLeft, |
|
199 QsciScintilla.SCI_WORDRIGHT : self.__QScintillaWordRight, |
|
200 QsciScintilla.SCI_VCHOME : self.__QScintillaVCHome, |
|
201 QsciScintilla.SCI_LINEEND : self.__QScintillaLineEnd, |
|
202 QsciScintilla.SCI_LINEUP : self.__QScintillaLineUp, |
|
203 QsciScintilla.SCI_LINEDOWN : self.__QScintillaLineDown, |
|
204 |
|
205 QsciScintilla.SCI_PAGEUP : self.__QScintillaAutoCompletionCommand, |
|
206 QsciScintilla.SCI_PAGEDOWN : self.__QScintillaAutoCompletionCommand, |
|
207 QsciScintilla.SCI_CANCEL : self.__QScintillaAutoCompletionCommand, |
|
208 |
|
209 QsciScintilla.SCI_CHARLEFTEXTEND : self.__QScintillaCharLeftExtend, |
|
210 QsciScintilla.SCI_CHARRIGHTEXTEND : self.extendSelectionRight, |
|
211 QsciScintilla.SCI_WORDLEFTEXTEND : self.__QScintillaWordLeftExtend, |
|
212 QsciScintilla.SCI_WORDRIGHTEXTEND : self.extendSelectionWordRight, |
|
213 QsciScintilla.SCI_VCHOMEEXTEND : self.__QScintillaVCHomeExtend, |
|
214 QsciScintilla.SCI_LINEENDEXTEND : self.extendSelectionToEOL, |
|
215 } |
|
216 |
|
217 def closeShell(self): |
|
218 """ |
|
219 Public method to shutdown the shell. |
|
220 """ |
|
221 for key in self.historyLists.keys(): |
|
222 self.saveHistory(key) |
|
223 |
|
224 def __bindLexer(self, language = 'Python'): |
|
225 """ |
|
226 Private slot to set the lexer. |
|
227 |
|
228 @param language lexer language to set (string) |
|
229 """ |
|
230 self.language = language |
|
231 if Preferences.getShell("SyntaxHighlightingEnabled"): |
|
232 self.lexer_ = Lexers.getLexer(self.language, self) |
|
233 else: |
|
234 self.lexer_ = None |
|
235 |
|
236 if self.lexer_ is None: |
|
237 self.setLexer(None) |
|
238 font = Preferences.getShell("MonospacedFont") |
|
239 self.monospacedStyles(font) |
|
240 return |
|
241 |
|
242 # get the font for style 0 and set it as the default font |
|
243 key = 'Scintilla/%s/style0/font' % self.lexer_.language() |
|
244 fontVariant = Preferences.Prefs.settings.value(key) |
|
245 if fontVariant.isValid(): |
|
246 fdesc = fontVariant.toStringList() |
|
247 font = QFont(fdesc[0], int(fdesc[1])) |
|
248 self.lexer_.setDefaultFont(font) |
|
249 self.setLexer(self.lexer_) |
|
250 self.lexer_.readSettings(Preferences.Prefs.settings, "Scintilla") |
|
251 |
|
252 # initialize the lexer APIs settings |
|
253 api = self.vm.getAPIsManager().getAPIs(self.language) |
|
254 if api is not None: |
|
255 api = api.getQsciAPIs() |
|
256 if api is not None: |
|
257 self.lexer_.setAPIs(api) |
|
258 |
|
259 def __setMargin0(self): |
|
260 """ |
|
261 Private method to configure margin 0. |
|
262 """ |
|
263 # set the settings for all margins |
|
264 self.setMarginsFont(Preferences.getShell("MarginsFont")) |
|
265 self.setMarginsForegroundColor(Preferences.getEditorColour("MarginsForeground")) |
|
266 self.setMarginsBackgroundColor(Preferences.getEditorColour("MarginsBackground")) |
|
267 |
|
268 # set margin 0 settings |
|
269 linenoMargin = Preferences.getShell("LinenoMargin") |
|
270 self.setMarginLineNumbers(0, linenoMargin) |
|
271 if linenoMargin: |
|
272 self.setMarginWidth(0, ' ' + '8' * Preferences.getShell("LinenoWidth")) |
|
273 else: |
|
274 self.setMarginWidth(0, 0) |
|
275 |
|
276 # disable margins 1 and 2 |
|
277 self.setMarginWidth(1, 0) |
|
278 self.setMarginWidth(2, 0) |
|
279 |
|
280 def __setTextDisplay(self): |
|
281 """ |
|
282 Private method to configure the text display. |
|
283 """ |
|
284 self.setTabWidth(Preferences.getEditor("TabWidth")) |
|
285 if Preferences.getEditor("ShowWhitespace"): |
|
286 self.setWhitespaceVisibility(QsciScintilla.WsVisible) |
|
287 else: |
|
288 self.setWhitespaceVisibility(QsciScintilla.WsInvisible) |
|
289 self.setEolVisibility(Preferences.getEditor("ShowEOL")) |
|
290 if Preferences.getEditor("BraceHighlighting"): |
|
291 self.setBraceMatching(QsciScintilla.SloppyBraceMatch) |
|
292 else: |
|
293 self.setBraceMatching(QsciScintilla.NoBraceMatch) |
|
294 self.setMatchedBraceForegroundColor( |
|
295 Preferences.getEditorColour("MatchingBrace")) |
|
296 self.setMatchedBraceBackgroundColor( |
|
297 Preferences.getEditorColour("MatchingBraceBack")) |
|
298 self.setUnmatchedBraceForegroundColor( |
|
299 Preferences.getEditorColour("NonmatchingBrace")) |
|
300 self.setUnmatchedBraceBackgroundColor( |
|
301 Preferences.getEditorColour("NonmatchingBraceBack")) |
|
302 if Preferences.getEditor("CustomSelectionColours"): |
|
303 self.setSelectionBackgroundColor(\ |
|
304 Preferences.getEditorColour("SelectionBackground")) |
|
305 else: |
|
306 self.setSelectionBackgroundColor(\ |
|
307 QApplication.palette().color(QPalette.Highlight)) |
|
308 if Preferences.getEditor("ColourizeSelText"): |
|
309 self.resetSelectionForegroundColor() |
|
310 elif Preferences.getEditor("CustomSelectionColours"): |
|
311 self.setSelectionForegroundColor(\ |
|
312 Preferences.getEditorColour("SelectionForeground")) |
|
313 else: |
|
314 self.setSelectionForegroundColor(\ |
|
315 QApplication.palette().color(QPalette.HighlightedText)) |
|
316 self.setSelectionToEol(Preferences.getEditor("ExtendSelectionToEol")) |
|
317 self.setCaretForegroundColor( |
|
318 Preferences.getEditorColour("CaretForeground")) |
|
319 self.setCaretLineBackgroundColor( |
|
320 Preferences.getEditorColour("CaretLineBackground")) |
|
321 self.setCaretLineVisible(Preferences.getEditor("CaretLineVisible")) |
|
322 self.caretWidth = Preferences.getEditor("CaretWidth") |
|
323 self.setCaretWidth(self.caretWidth) |
|
324 if Preferences.getShell("WrapEnabled"): |
|
325 self.setWrapMode(QsciScintilla.WrapWord) |
|
326 else: |
|
327 self.setWrapMode(QsciScintilla.WrapNone) |
|
328 self.useMonospaced = Preferences.getShell("UseMonospacedFont") |
|
329 self.__setMonospaced(self.useMonospaced) |
|
330 |
|
331 def __setMonospaced(self, on): |
|
332 """ |
|
333 Private method to set/reset a monospaced font. |
|
334 |
|
335 @param on flag to indicate usage of a monospace font (boolean) |
|
336 """ |
|
337 if on: |
|
338 f = Preferences.getShell("MonospacedFont") |
|
339 self.monospacedStyles(f) |
|
340 else: |
|
341 if not self.lexer_: |
|
342 self.clearStyles() |
|
343 self.__setMargin0() |
|
344 self.setFont(Preferences.getShell("MonospacedFont")) |
|
345 |
|
346 self.useMonospaced = on |
|
347 |
|
348 def __setAutoCompletion(self, language = 'Python'): |
|
349 """ |
|
350 Private method to configure the autocompletion function. |
|
351 |
|
352 @param language of the autocompletion set to set (string) |
|
353 """ |
|
354 self.setAutoCompletionCaseSensitivity( |
|
355 Preferences.getEditor("AutoCompletionCaseSensitivity")) |
|
356 self.setAutoCompletionThreshold(-1) |
|
357 |
|
358 self.racEnabled = Preferences.getShell("AutoCompletionEnabled") |
|
359 |
|
360 def __setCallTips(self, language = 'Python'): |
|
361 """ |
|
362 Private method to configure the calltips function. |
|
363 |
|
364 @param language of the calltips set to set (string) |
|
365 """ |
|
366 if Preferences.getShell("CallTipsEnabled"): |
|
367 self.setCallTipsBackgroundColor( |
|
368 Preferences.getEditorColour("CallTipsBackground")) |
|
369 self.setCallTipsVisible(Preferences.getEditor("CallTipsVisible")) |
|
370 calltipsStyle = Preferences.getEditor("CallTipsStyle") |
|
371 if calltipsStyle == QsciScintilla.CallTipsNoContext: |
|
372 self.setCallTipsStyle(QsciScintilla.CallTipsNoContext) |
|
373 elif calltipsStyle == QsciScintilla.CallTipsNoAutoCompletionContext: |
|
374 self.setCallTipsStyle(QsciScintilla.CallTipsNoAutoCompletionContext) |
|
375 else: |
|
376 self.setCallTipsStyle(QsciScintilla.CallTipsContext) |
|
377 else: |
|
378 self.setCallTipsStyle(QsciScintilla.CallTipsNone) |
|
379 |
|
380 def setDebuggerUI(self, ui): |
|
381 """ |
|
382 Public method to set the debugger UI. |
|
383 |
|
384 @param ui reference to the debugger UI object (DebugUI) |
|
385 """ |
|
386 self.connect(ui, SIGNAL('exceptionInterrupt'), self.__writePrompt) |
|
387 |
|
388 def __initialise(self): |
|
389 """ |
|
390 Private method to get ready for a new remote interpreter. |
|
391 """ |
|
392 self.buff = "" |
|
393 self.inContinue = False |
|
394 self.inRawMode = False |
|
395 self.echoInput = True |
|
396 self.clientCapabilities = 0 |
|
397 |
|
398 def __clientCapabilities(self, cap, clType): |
|
399 """ |
|
400 Private slot to handle the reporting of the clients capabilities. |
|
401 |
|
402 @param cap client capabilities (integer) |
|
403 @param clType type of the debug client (string) |
|
404 """ |
|
405 self.clientCapabilities = cap |
|
406 if clType != self.clientType: |
|
407 self.clientType = clType |
|
408 self.__bindLexer(clType) |
|
409 self.__setMargin0() |
|
410 self.__setAutoCompletion(clType) |
|
411 self.__setCallTips(clType) |
|
412 self.racEnabled = Preferences.getShell("AutoCompletionEnabled") and \ |
|
413 (cap & HasCompleter) > 0 |
|
414 |
|
415 if not self.historyLists.has_key(clType): |
|
416 # load history list |
|
417 self.loadHistory(clType) |
|
418 self.history = self.historyLists[clType] |
|
419 self.histidx = -1 |
|
420 |
|
421 def loadHistory(self, clientType): |
|
422 """ |
|
423 Public method to load the history for the given client type. |
|
424 |
|
425 @param clientType type of the debug client (string) |
|
426 """ |
|
427 hVariant = Preferences.Prefs.settings.value("Shell/Histories/" + clientType) |
|
428 if hVariant.isValid(): |
|
429 hl = hVariant.toStringList() |
|
430 self.historyLists[clientType] = hl[-self.maxHistoryEntries:] |
|
431 else: |
|
432 self.historyLists[clientType] = [] |
|
433 |
|
434 def reloadHistory(self): |
|
435 """ |
|
436 Public method to reload the history of the currently selected client type. |
|
437 """ |
|
438 self.loadHistory(self.clientType) |
|
439 self.history = self.historyLists[self.clientType] |
|
440 self.histidx = -1 |
|
441 |
|
442 def saveHistory(self, clientType): |
|
443 """ |
|
444 Public method to save the history for the given client type. |
|
445 |
|
446 @param clientType type of the debug client (string) |
|
447 """ |
|
448 if self.historyLists.has_key(clientType): |
|
449 Preferences.Prefs.settings.setValue(\ |
|
450 "Shell/Histories/" + clientType, QVariant(self.historyLists[clientType])) |
|
451 |
|
452 def getHistory(self, clientType): |
|
453 """ |
|
454 Public method to get the history for the given client type. |
|
455 |
|
456 @param clientType type of the debug client (string). |
|
457 If it is None, the current history is returned. |
|
458 @return reference to the history list (list of strings) |
|
459 """ |
|
460 if clientType is None: |
|
461 return self.history |
|
462 elif clientType in self.historyLists: |
|
463 return self.historyLists[clientType] |
|
464 else: |
|
465 return [] |
|
466 |
|
467 def __clearHistory(self): |
|
468 """ |
|
469 Private slot to clear the current history. |
|
470 """ |
|
471 self.history = [] |
|
472 |
|
473 def __selectHistory(self): |
|
474 """ |
|
475 Private slot to select a history entry to execute. |
|
476 """ |
|
477 cmd, ok = QInputDialog.getItem(\ |
|
478 self, |
|
479 self.trUtf8("Select History"), |
|
480 self.trUtf8("Select the history entry to execute (most recent shown last)."), |
|
481 self.history, |
|
482 0, False) |
|
483 if ok: |
|
484 self.__insertHistory(cmd) |
|
485 |
|
486 def __showHistory(self): |
|
487 """ |
|
488 Private slot to show the shell history dialog. |
|
489 """ |
|
490 dlg = ShellHistoryDialog(self.history, self.vm, self) |
|
491 if dlg.exec_() == QDialog.Accepted: |
|
492 self.historyLists[self.clientType] = dlg.getHistory() |
|
493 self.history = self.historyLists[self.clientType] |
|
494 self.histidx = -1 |
|
495 |
|
496 def getClientType(self): |
|
497 """ |
|
498 Public slot to get the clients type. |
|
499 |
|
500 @return client type (string) |
|
501 """ |
|
502 return self.clientType |
|
503 |
|
504 def __getBanner(self): |
|
505 """ |
|
506 Private method to get the banner for the remote interpreter. |
|
507 |
|
508 It requests the interpreter version and platform running on the |
|
509 debug client side. |
|
510 """ |
|
511 if self.passive: |
|
512 self.__writeBanner('','','') |
|
513 else: |
|
514 self.dbs.remoteBanner() |
|
515 |
|
516 def __writeBanner(self, version, platform, dbgclient): |
|
517 """ |
|
518 Private method to write a banner with info from the debug client. |
|
519 |
|
520 @param version interpreter version string (string) |
|
521 @param platform platform of the remote interpreter (string) |
|
522 @param dbgclient debug client variant used (string) |
|
523 """ |
|
524 QsciScintillaCompat.clear(self) |
|
525 if self.passive and not self.dbs.isConnected(): |
|
526 self.__write(self.trUtf8('Passive Debug Mode')) |
|
527 self.__write(self.trUtf8('\nNot connected')) |
|
528 else: |
|
529 version = version.replace("#", self.trUtf8("No.")) |
|
530 if platform != "" and dbgclient != "": |
|
531 self.__write(self.trUtf8('{0} on {1}, {2}') |
|
532 .format(version, platform, dbgclient)) |
|
533 else: |
|
534 self.__write(version) |
|
535 self.__write('\n') |
|
536 |
|
537 self.__write(sys.ps1) |
|
538 |
|
539 def __writePrompt(self): |
|
540 """ |
|
541 Private method to write the prompt. |
|
542 """ |
|
543 self.__write(self.inContinue and sys.ps2 or sys.ps1) |
|
544 |
|
545 def __clientStatement(self, more): |
|
546 """ |
|
547 Private method to handle the response from the debugger client. |
|
548 |
|
549 @param more flag indicating that more user input is required (boolean) |
|
550 """ |
|
551 if not self.inRawMode: |
|
552 self.inContinue = more |
|
553 self.__writePrompt() |
|
554 self.inCommandExecution = False |
|
555 |
|
556 def __clientError(self): |
|
557 """ |
|
558 Private method to handle an error in the client. |
|
559 """ |
|
560 self.inCommandExecution = False |
|
561 self.interruptCommandExecution = True |
|
562 self.inContinue = False |
|
563 |
|
564 def __getEndPos(self): |
|
565 """ |
|
566 Private method to return the line and column of the last character. |
|
567 |
|
568 @return tuple of two values (int, int) giving the line and column |
|
569 """ |
|
570 line = self.lines() - 1 |
|
571 return (line, self.lineLength(line)) |
|
572 |
|
573 def __write(self, s): |
|
574 """ |
|
575 Private method to display some text. |
|
576 |
|
577 @param s text to be displayed (string) |
|
578 """ |
|
579 line, col = self.__getEndPos() |
|
580 self.setCursorPosition(line, col) |
|
581 self.insert(toUnicode(s)) |
|
582 self.prline, self.prcol = self.getCursorPosition() |
|
583 self.ensureCursorVisible() |
|
584 self.ensureLineVisible(self.prline) |
|
585 |
|
586 def __writeStdOut(self, s): |
|
587 """ |
|
588 Private method to display some text with StdOut label. |
|
589 |
|
590 @param s text to be displayed (string) |
|
591 """ |
|
592 self.__write(self.trUtf8("StdOut: {0}").format(s)) |
|
593 |
|
594 def __writeStdErr(self, s): |
|
595 """ |
|
596 Private method to display some text with StdErr label. |
|
597 |
|
598 @param s text to be displayed (string) |
|
599 """ |
|
600 self.__write(self.trUtf8("StdErr: {0}").format(s)) |
|
601 |
|
602 def __raw_input(self, s, echo): |
|
603 """ |
|
604 Private method to handle raw input. |
|
605 |
|
606 @param s prompt to be displayed (string) |
|
607 @param echo Flag indicating echoing of the input (boolean) |
|
608 """ |
|
609 self.setFocus() |
|
610 self.inRawMode = True |
|
611 self.echoInput = echo |
|
612 self.__write(s) |
|
613 line, col = self.__getEndPos() |
|
614 self.setCursorPosition(line,col) |
|
615 self.prompt = self.text(line)\ |
|
616 .replace(sys.ps1, "").replace(sys.ps2, "") |
|
617 # move cursor to end of line |
|
618 self.moveCursorToEOL() |
|
619 |
|
620 def paste(self): |
|
621 """ |
|
622 Reimplemented slot to handle the paste action. |
|
623 """ |
|
624 lines = QApplication.clipboard().text() |
|
625 self.executeLines(lines) |
|
626 |
|
627 def __middleMouseButton(self): |
|
628 """ |
|
629 Private method to handle the middle mouse button press. |
|
630 """ |
|
631 lines = QApplication.clipboard().text(QClipboard.Selection) |
|
632 self.executeLines(lines) |
|
633 |
|
634 def executeLines(self, lines): |
|
635 """ |
|
636 Public method to execute a set of lines as multiple commands. |
|
637 |
|
638 @param lines multiple lines of text to be executed as single |
|
639 commands (string) |
|
640 """ |
|
641 for line in lines.splitlines(True): |
|
642 if line.endswith("\r\n"): |
|
643 fullline = True |
|
644 cmd = line[:-2] |
|
645 elif line.endswith("\r") or line.endswith("\n"): |
|
646 fullline = True |
|
647 cmd = line[:-1] |
|
648 else: |
|
649 fullline = False |
|
650 |
|
651 self.__insertTextAtEnd(line) |
|
652 if fullline: |
|
653 self.__executeCommand(cmd) |
|
654 if self.interruptCommandExecution: |
|
655 self.__executeCommand("") |
|
656 break |
|
657 |
|
658 def __clearCurrentLine(self): |
|
659 """ |
|
660 Private method to clear the line containing the cursor. |
|
661 """ |
|
662 line, col = self.getCursorPosition() |
|
663 if self.text(line).startswith(sys.ps1): |
|
664 col = len(sys.ps1) |
|
665 elif self.text(line).startswith(sys.ps2): |
|
666 col = len(sys.ps2) |
|
667 else: |
|
668 col = 0 |
|
669 self.setCursorPosition(line, col) |
|
670 self.deleteLineRight() |
|
671 |
|
672 def __insertText(self, s): |
|
673 """ |
|
674 Private method to insert some text at the current cursor position. |
|
675 |
|
676 @param s text to be inserted (string) |
|
677 """ |
|
678 line, col = self.getCursorPosition() |
|
679 self.insertAt(s, line, col) |
|
680 self.setCursorPosition(line, col + len(s)) |
|
681 |
|
682 def __insertTextAtEnd(self, s): |
|
683 """ |
|
684 Private method to insert some text at the end of the command line. |
|
685 |
|
686 @param s text to be inserted (string) |
|
687 """ |
|
688 line, col = self.__getEndPos() |
|
689 self.setCursorPosition(line, col) |
|
690 self.insert(s) |
|
691 self.prline, self.prcol = self.getCursorPosition() |
|
692 |
|
693 def __insertTextNoEcho(self, s): |
|
694 """ |
|
695 Private method to insert some text at the end of the buffer without echoing it. |
|
696 |
|
697 @param s text to be inserted (string) |
|
698 """ |
|
699 self.buff += s |
|
700 self.prline, self.prcol = self.getCursorPosition() |
|
701 |
|
702 def mousePressEvent(self, event): |
|
703 """ |
|
704 Protected method to handle the mouse press event. |
|
705 |
|
706 @param event the mouse press event (QMouseEvent) |
|
707 """ |
|
708 self.setFocus() |
|
709 if event.button() == Qt.MidButton: |
|
710 self.__middleMouseButton() |
|
711 else: |
|
712 QsciScintillaCompat.mousePressEvent(self, event) |
|
713 |
|
714 def editorCommand(self, cmd): |
|
715 """ |
|
716 Public method to perform an editor command. |
|
717 |
|
718 @param cmd the scintilla command to be performed |
|
719 """ |
|
720 try: |
|
721 self.supportedEditorCommands[cmd]() |
|
722 except TypeError: |
|
723 self.supportedEditorCommands[cmd](cmd) |
|
724 except KeyError: |
|
725 pass |
|
726 |
|
727 def __isCursorOnLastLine(self): |
|
728 """ |
|
729 Private method to check, if the cursor is on the last line. |
|
730 """ |
|
731 cline, ccol = self.getCursorPosition() |
|
732 return cline == self.lines() - 1 |
|
733 |
|
734 def keyPressEvent(self, ev): |
|
735 """ |
|
736 Re-implemented to handle the user input a key at a time. |
|
737 |
|
738 @param ev key event (QKeyEvent) |
|
739 """ |
|
740 txt = ev.text() |
|
741 key = ev.key() |
|
742 |
|
743 # See it is text to insert. |
|
744 if len(txt) and txt >= " ": |
|
745 if not self.__isCursorOnLastLine(): |
|
746 line, col = self.__getEndPos() |
|
747 self.setCursorPosition(line, col) |
|
748 self.prline, self.prcol = self.getCursorPosition() |
|
749 if self.echoInput: |
|
750 ac = self.isListActive() |
|
751 QsciScintillaCompat.keyPressEvent(self, ev) |
|
752 self.incrementalSearchActive = True |
|
753 if ac and \ |
|
754 self.racEnabled: |
|
755 self.dbs.remoteCompletion(self.completionText + txt) |
|
756 else: |
|
757 self.__insertTextNoEcho(txt) |
|
758 else: |
|
759 ev.ignore() |
|
760 |
|
761 def __QScintillaTab(self, cmd): |
|
762 """ |
|
763 Private method to handle the Tab key. |
|
764 |
|
765 @param cmd QScintilla command |
|
766 """ |
|
767 if self.isListActive(): |
|
768 self.SendScintilla(cmd) |
|
769 elif self.__isCursorOnLastLine(): |
|
770 line, index = self.getCursorPosition() |
|
771 buf = self.text(line).replace(sys.ps1, "").replace(sys.ps2, "") |
|
772 if self.inContinue and not buf[:index-len(sys.ps2)].strip(): |
|
773 self.SendScintilla(cmd) |
|
774 elif self.racEnabled: |
|
775 self.dbs.remoteCompletion(buf) |
|
776 |
|
777 def __QScintillaLeftDeleteCommand(self, method): |
|
778 """ |
|
779 Private method to handle a QScintilla delete command working to the left. |
|
780 |
|
781 @param method shell method to execute |
|
782 """ |
|
783 if self.__isCursorOnLastLine(): |
|
784 line, col = self.getCursorPosition() |
|
785 db = 0 |
|
786 ac = self.isListActive() |
|
787 oldLength = len(self.text(line)) |
|
788 |
|
789 if self.text(line).startswith(sys.ps1): |
|
790 if col > len(sys.ps1): |
|
791 method() |
|
792 db = 1 |
|
793 elif self.text(line).startswith(sys.ps2): |
|
794 if col > len(sys.ps2): |
|
795 method() |
|
796 db = 1 |
|
797 elif col > 0: |
|
798 method() |
|
799 db = 1 |
|
800 if db and ac and self.racEnabled and self.completionText: |
|
801 delta = len(self.text(line)) - oldLength |
|
802 self.dbs.remoteCompletion(self.completionText[:delta]) |
|
803 |
|
804 def __QScintillaDeleteBack(self): |
|
805 """ |
|
806 Private method to handle the Backspace key. |
|
807 """ |
|
808 self.__QScintillaLeftDeleteCommand(self.deleteBack) |
|
809 |
|
810 def __QScintillaDeleteWordLeft(self): |
|
811 """ |
|
812 Private method to handle the Delete Word Left command. |
|
813 """ |
|
814 self.__QScintillaLeftDeleteCommand(self.deleteWordLeft) |
|
815 |
|
816 def __QScintillaDelete(self): |
|
817 """ |
|
818 Private method to handle the delete command. |
|
819 """ |
|
820 if self.__isCursorOnLastLine(): |
|
821 if self.hasSelectedText(): |
|
822 lineFrom, indexFrom, lineTo, indexTo = self.getSelection() |
|
823 if self.text(lineFrom).startswith(sys.ps1): |
|
824 if indexFrom >= len(sys.ps1): |
|
825 self.delete() |
|
826 elif self.text(lineFrom).startswith(sys.ps2): |
|
827 if indexFrom >= len(sys.ps2): |
|
828 self.delete() |
|
829 elif indexFrom >= 0: |
|
830 self.delete() |
|
831 else: |
|
832 self.delete() |
|
833 |
|
834 def __QScintillaDeleteLineLeft(self): |
|
835 """ |
|
836 Private method to handle the Delete Line Left command. |
|
837 """ |
|
838 if self.__isCursorOnLastLine(): |
|
839 if self.isListActive(): |
|
840 self.cancelList() |
|
841 |
|
842 line, col = self.getCursorPosition() |
|
843 if self.text(line).startswith(sys.ps1): |
|
844 prompt = sys.ps1 |
|
845 elif self.text(line).startswith(sys.ps2): |
|
846 prompt = sys.ps2 |
|
847 else: |
|
848 prompt = "" |
|
849 |
|
850 self.deleteLineLeft() |
|
851 self.insertAt(prompt, line, 0) |
|
852 self.setCursorPosition(line, len(prompt)) |
|
853 |
|
854 def __QScintillaNewline(self, cmd): |
|
855 """ |
|
856 Private method to handle the Return key. |
|
857 |
|
858 @param cmd QScintilla command |
|
859 """ |
|
860 if self.__isCursorOnLastLine(): |
|
861 if self.isListActive(): |
|
862 self.SendScintilla(cmd) |
|
863 else: |
|
864 self.incrementalSearchString = "" |
|
865 self.incrementalSearchActive = False |
|
866 line, col = self.__getEndPos() |
|
867 self.setCursorPosition(line,col) |
|
868 buf = self.text(line).replace(sys.ps1, "").replace(sys.ps2, "") |
|
869 self.insert('\n') |
|
870 self.__executeCommand(buf) |
|
871 |
|
872 def __QScintillaLeftCommand(self, method, allLinesAllowed = False): |
|
873 """ |
|
874 Private method to handle a QScintilla command working to the left. |
|
875 |
|
876 @param method shell method to execute |
|
877 @param allLinesAllowed flag indicating that the command may be executed |
|
878 on any line (boolean) |
|
879 """ |
|
880 if self.__isCursorOnLastLine() or allLinesAllowed: |
|
881 line, col = self.getCursorPosition() |
|
882 if self.text(line).startswith(sys.ps1): |
|
883 if col > len(sys.ps1): |
|
884 method() |
|
885 elif self.text(line).startswith(sys.ps2): |
|
886 if col > len(sys.ps2): |
|
887 method() |
|
888 elif col > 0: |
|
889 method() |
|
890 |
|
891 def __QScintillaCharLeft(self): |
|
892 """ |
|
893 Private method to handle the Cursor Left command. |
|
894 """ |
|
895 self.__QScintillaLeftCommand(self.moveCursorLeft) |
|
896 |
|
897 def __QScintillaWordLeft(self): |
|
898 """ |
|
899 Private method to handle the Cursor Word Left command. |
|
900 """ |
|
901 self.__QScintillaLeftCommand(self.moveCursorWordLeft) |
|
902 |
|
903 def __QScintillaRightCommand(self, method): |
|
904 """ |
|
905 Private method to handle a QScintilla command working to the right. |
|
906 |
|
907 @param method shell method to execute |
|
908 """ |
|
909 if self.__isCursorOnLastLine(): |
|
910 method() |
|
911 |
|
912 def __QScintillaCharRight(self): |
|
913 """ |
|
914 Private method to handle the Cursor Right command. |
|
915 """ |
|
916 self.__QScintillaRightCommand(self.moveCursorRight) |
|
917 |
|
918 def __QScintillaWordRight(self): |
|
919 """ |
|
920 Private method to handle the Cursor Word Right command. |
|
921 """ |
|
922 self.__QScintillaRightCommand(self.moveCursorWordRight) |
|
923 |
|
924 def __QScintillaDeleteWordRight(self): |
|
925 """ |
|
926 Private method to handle the Delete Word Right command. |
|
927 """ |
|
928 self.__QScintillaRightCommand(self.deleteWordRight) |
|
929 |
|
930 def __QScintillaDeleteLineRight(self): |
|
931 """ |
|
932 Private method to handle the Delete Line Right command. |
|
933 """ |
|
934 self.__QScintillaRightCommand(self.deleteLineRight) |
|
935 |
|
936 def __QScintillaVCHome(self, cmd): |
|
937 """ |
|
938 Private method to handle the Home key. |
|
939 |
|
940 @param cmd QScintilla command |
|
941 """ |
|
942 if self.isListActive(): |
|
943 self.SendScintilla(cmd) |
|
944 elif self.__isCursorOnLastLine(): |
|
945 line, col = self.getCursorPosition() |
|
946 if self.text(line).startswith(sys.ps1): |
|
947 col = len(sys.ps1) |
|
948 elif self.text(line).startswith(sys.ps2): |
|
949 col = len(sys.ps2) |
|
950 else: |
|
951 col = 0 |
|
952 self.setCursorPosition(line, col) |
|
953 |
|
954 def __QScintillaLineEnd(self, cmd): |
|
955 """ |
|
956 Private method to handle the End key. |
|
957 |
|
958 @param cmd QScintilla command |
|
959 """ |
|
960 if self.isListActive(): |
|
961 self.SendScintilla(cmd) |
|
962 elif self.__isCursorOnLastLine(): |
|
963 self.moveCursorToEOL() |
|
964 |
|
965 def __QScintillaLineUp(self, cmd): |
|
966 """ |
|
967 Private method to handle the Up key. |
|
968 |
|
969 @param cmd QScintilla command |
|
970 """ |
|
971 if self.isListActive(): |
|
972 self.SendScintilla(cmd) |
|
973 else: |
|
974 line, col = self.__getEndPos() |
|
975 buf = self.text(line).replace(sys.ps1, "").replace(sys.ps2, "") |
|
976 if buf and self.incrementalSearchActive: |
|
977 if self.incrementalSearchString: |
|
978 idx = self.__rsearchHistory(self.incrementalSearchString, |
|
979 self.histidx) |
|
980 if idx >= 0: |
|
981 self.histidx = idx |
|
982 self.__useHistory() |
|
983 else: |
|
984 idx = self.__rsearchHistory(buf) |
|
985 if idx >= 0: |
|
986 self.histidx = idx |
|
987 self.incrementalSearchString = buf |
|
988 self.__useHistory() |
|
989 else: |
|
990 if self.histidx < 0: |
|
991 self.histidx = len(self.history) |
|
992 if self.histidx > 0: |
|
993 self.histidx = self.histidx - 1 |
|
994 self.__useHistory() |
|
995 |
|
996 def __QScintillaLineDown(self, cmd): |
|
997 """ |
|
998 Private method to handle the Down key. |
|
999 |
|
1000 @param cmd QScintilla command |
|
1001 """ |
|
1002 if self.isListActive(): |
|
1003 self.SendScintilla(cmd) |
|
1004 else: |
|
1005 line, col = self.__getEndPos() |
|
1006 buf = self.text(line).replace(sys.ps1, "").replace(sys.ps2, "") |
|
1007 if buf and self.incrementalSearchActive: |
|
1008 if self.incrementalSearchString: |
|
1009 idx = self.__searchHistory(self.incrementalSearchString, self.histidx) |
|
1010 if idx >= 0: |
|
1011 self.histidx = idx |
|
1012 self.__useHistory() |
|
1013 else: |
|
1014 idx = self.__searchHistory(buf) |
|
1015 if idx >= 0: |
|
1016 self.histidx = idx |
|
1017 self.incrementalSearchString = buf |
|
1018 self.__useHistory() |
|
1019 else: |
|
1020 if self.histidx >= 0 and self.histidx < len(self.history): |
|
1021 self.histidx += 1 |
|
1022 self.__useHistory() |
|
1023 |
|
1024 def __QScintillaCharLeftExtend(self): |
|
1025 """ |
|
1026 Private method to handle the Extend Selection Left command. |
|
1027 """ |
|
1028 self.__QScintillaLeftCommand(self.extendSelectionLeft, True) |
|
1029 |
|
1030 def __QScintillaWordLeftExtend(self): |
|
1031 """ |
|
1032 Private method to handle the Extend Selection Left one word command. |
|
1033 """ |
|
1034 self.__QScintillaLeftCommand(self.extendSelectionWordLeft, True) |
|
1035 |
|
1036 def __QScintillaVCHomeExtend(self): |
|
1037 """ |
|
1038 Private method to handle the Extend Selection to start of line command. |
|
1039 """ |
|
1040 line, col = self.getCursorPosition() |
|
1041 if self.text(line).startswith(sys.ps1): |
|
1042 col = len(sys.ps1) |
|
1043 elif self.text(line).startswith(sys.ps2): |
|
1044 col = len(sys.ps2) |
|
1045 else: |
|
1046 col = 0 |
|
1047 |
|
1048 self.extendSelectionToBOL() |
|
1049 while col > 0: |
|
1050 self.extendSelectionRight() |
|
1051 col -= 1 |
|
1052 |
|
1053 def __QScintillaAutoCompletionCommand(self, cmd): |
|
1054 """ |
|
1055 Private method to handle a command for autocompletion only. |
|
1056 |
|
1057 @param cmd QScintilla command |
|
1058 """ |
|
1059 if self.isListActive() or self.isCallTipActive(): |
|
1060 self.SendScintilla(cmd) |
|
1061 |
|
1062 def __executeCommand(self, cmd): |
|
1063 """ |
|
1064 Private slot to execute a command. |
|
1065 |
|
1066 @param cmd command to be executed by debug client (string) |
|
1067 """ |
|
1068 if not self.inRawMode: |
|
1069 self.inCommandExecution = True |
|
1070 self.interruptCommandExecution = False |
|
1071 if not cmd: |
|
1072 cmd = '' |
|
1073 if len(self.history) == 0 or self.history[-1] != cmd: |
|
1074 if len(self.history) == self.maxHistoryEntries: |
|
1075 del self.history[0] |
|
1076 self.history.append(cmd) |
|
1077 self.histidx = -1 |
|
1078 if cmd.startswith('start '): |
|
1079 if not self.passive: |
|
1080 cmdList = cmd.split(None, 1) |
|
1081 if len(cmdList) < 2: |
|
1082 self.dbs.startClient(False) # same as reset |
|
1083 else: |
|
1084 language = cmdList[1] |
|
1085 if not language in self.clientLanguages: |
|
1086 language = cmdList[1].capitalize() |
|
1087 if not language in self.clientLanguages: |
|
1088 language = "" |
|
1089 if language: |
|
1090 self.dbs.startClient(False, language) |
|
1091 else: |
|
1092 # language not supported or typo |
|
1093 self.__write(\ |
|
1094 self.trUtf8('Shell language "{0}" not supported.\n')\ |
|
1095 .format(cmdList[1])) |
|
1096 self.__clientStatement(False) |
|
1097 return |
|
1098 cmd = '' |
|
1099 elif cmd == 'languages': |
|
1100 s = '%s\n' % ', '.join(self.clientLanguages) |
|
1101 self.__write(s) |
|
1102 self.__clientStatement(False) |
|
1103 return |
|
1104 elif cmd == 'clear': |
|
1105 # Display the banner. |
|
1106 self.__getBanner() |
|
1107 if not self.passive: |
|
1108 return |
|
1109 else: |
|
1110 cmd = '' |
|
1111 elif cmd == 'reset': |
|
1112 self.dbs.startClient(False) |
|
1113 if self.passive: |
|
1114 return |
|
1115 else: |
|
1116 cmd = '' |
|
1117 |
|
1118 self.dbs.remoteStatement(cmd) |
|
1119 while self.inCommandExecution: QApplication.processEvents() |
|
1120 else: |
|
1121 if not self.echoInput: |
|
1122 cmd = self.buff |
|
1123 self.inRawMode = False |
|
1124 self.echoInput = True |
|
1125 if not cmd: |
|
1126 cmd = '' |
|
1127 else: |
|
1128 cmd = cmd.replace(self.prompt, "") |
|
1129 self.dbs.remoteRawInput(cmd) |
|
1130 |
|
1131 def __useHistory(self): |
|
1132 """ |
|
1133 Private method to display a command from the history. |
|
1134 """ |
|
1135 if self.histidx < len(self.history): |
|
1136 cmd = self.history[self.histidx] |
|
1137 else: |
|
1138 cmd = "" |
|
1139 self.incrementalSearchString = "" |
|
1140 self.incrementalSearchActive = False |
|
1141 |
|
1142 self.__insertHistory(cmd) |
|
1143 |
|
1144 def __insertHistory(self, cmd): |
|
1145 """ |
|
1146 Private method to insert a command selected from the history. |
|
1147 |
|
1148 @param cmd history entry to be inserted (string) |
|
1149 """ |
|
1150 self.setCursorPosition(self.prline,self.prcol) |
|
1151 self.setSelection(self.prline,self.prcol,\ |
|
1152 self.prline,self.lineLength(self.prline)) |
|
1153 self.removeSelectedText() |
|
1154 self.__insertText(cmd) |
|
1155 |
|
1156 def __searchHistory(self, txt, startIdx = -1): |
|
1157 """ |
|
1158 Private method used to search the history. |
|
1159 |
|
1160 @param txt text to match at the beginning (string) |
|
1161 @param startIdx index to start search from (integer) |
|
1162 @return index of found entry (integer) |
|
1163 """ |
|
1164 if startIdx == -1: |
|
1165 idx = 0 |
|
1166 else: |
|
1167 idx = startIdx + 1 |
|
1168 while idx < len(self.history) and \ |
|
1169 not self.history[idx].startswith(txt): |
|
1170 idx += 1 |
|
1171 return idx |
|
1172 |
|
1173 def __rsearchHistory(self, txt, startIdx = -1): |
|
1174 """ |
|
1175 Private method used to reverse search the history. |
|
1176 |
|
1177 @param txt text to match at the beginning (string) |
|
1178 @param startIdx index to start search from (integer) |
|
1179 @return index of found entry (integer) |
|
1180 """ |
|
1181 if startIdx == -1: |
|
1182 idx = len(self.history) - 1 |
|
1183 else: |
|
1184 idx = startIdx - 1 |
|
1185 while idx >= 0 and \ |
|
1186 not self.history[idx].startswith(txt): |
|
1187 idx -= 1 |
|
1188 return idx |
|
1189 |
|
1190 def focusNextPrevChild(self, next): |
|
1191 """ |
|
1192 Reimplemented to stop Tab moving to the next window. |
|
1193 |
|
1194 While the user is entering a multi-line command, the movement to |
|
1195 the next window by the Tab key being pressed is suppressed. |
|
1196 |
|
1197 @param next next window |
|
1198 @return flag indicating the movement |
|
1199 """ |
|
1200 if next and self.inContinue: |
|
1201 return False |
|
1202 |
|
1203 return QsciScintillaCompat.focusNextPrevChild(self,next) |
|
1204 |
|
1205 def contextMenuEvent(self,ev): |
|
1206 """ |
|
1207 Reimplemented to show our own context menu. |
|
1208 |
|
1209 @param ev context menu event (QContextMenuEvent) |
|
1210 """ |
|
1211 self.menu.popup(ev.globalPos()) |
|
1212 ev.accept() |
|
1213 |
|
1214 def clear(self): |
|
1215 """ |
|
1216 Public slot to clear the display. |
|
1217 """ |
|
1218 # Display the banner. |
|
1219 self.__getBanner() |
|
1220 |
|
1221 def __resetAndClear(self): |
|
1222 """ |
|
1223 Private slot to handle the 'reset and clear' context menu entry. |
|
1224 """ |
|
1225 self.__reset() |
|
1226 self.clear() |
|
1227 |
|
1228 def __reset(self): |
|
1229 """ |
|
1230 Private slot to handle the 'reset' context menu entry. |
|
1231 """ |
|
1232 self.dbs.startClient(False) |
|
1233 |
|
1234 def __startDebugClient(self, action): |
|
1235 """ |
|
1236 Private slot to start a debug client accoding to the action triggered. |
|
1237 |
|
1238 @param action context menu action that was triggered (QAction) |
|
1239 """ |
|
1240 language = action.data().toString() |
|
1241 self.dbs.startClient(False, language) |
|
1242 |
|
1243 def handlePreferencesChanged(self): |
|
1244 """ |
|
1245 Public slot to handle the preferencesChanged signal. |
|
1246 """ |
|
1247 # rebind the lexer |
|
1248 self.__bindLexer(self.language) |
|
1249 self.recolor() |
|
1250 |
|
1251 # set margin 0 configuration |
|
1252 self.__setTextDisplay() |
|
1253 self.__setMargin0() |
|
1254 |
|
1255 # set the autocompletion and calltips function |
|
1256 self.__setAutoCompletion() |
|
1257 self.__setCallTips() |
|
1258 |
|
1259 # do the history related stuff |
|
1260 self.maxHistoryEntries = Preferences.getShell("MaxHistoryEntries") |
|
1261 for key in self.historyLists.keys(): |
|
1262 self.historyLists[key] = \ |
|
1263 self.historyLists[key][-self.maxHistoryEntries:] |
|
1264 |
|
1265 # do stdout /stderr stuff |
|
1266 showStdOutErr = Preferences.getShell("ShowStdOutErr") |
|
1267 if self.__showStdOutErr != showStdOutErr: |
|
1268 if showStdOutErr: |
|
1269 self.connect(self.dbs, SIGNAL('clientProcessStdout'), |
|
1270 self.__writeStdOut) |
|
1271 self.connect(self.dbs, SIGNAL('clientProcessStderr'), |
|
1272 self.__writeStdErr) |
|
1273 else: |
|
1274 self.disconnect(self.dbs, SIGNAL('clientProcessStdout'), |
|
1275 self.__writeStdOut) |
|
1276 self.disconnect(self.dbs, SIGNAL('clientProcessStderr'), |
|
1277 self.__writeStdErr) |
|
1278 self.__showStdOutErr = showStdOutErr |
|
1279 |
|
1280 def __showCompletions(self, completions, text): |
|
1281 """ |
|
1282 Private method to display the possible completions. |
|
1283 |
|
1284 @param completions list of possible completions (list of strings) |
|
1285 @param text text that is about to be completed (string) |
|
1286 """ |
|
1287 if len(completions) == 0: |
|
1288 return |
|
1289 |
|
1290 if len(completions) > 1: |
|
1291 completions.sort() |
|
1292 self.showUserList(1, completions) |
|
1293 self.completionText = text |
|
1294 else: |
|
1295 txt = completions[0] |
|
1296 if text != "": |
|
1297 txt = txt.replace(text, "") |
|
1298 self.__insertText(txt) |
|
1299 self.completionText = "" |
|
1300 |
|
1301 def __completionListSelected(self, id, txt): |
|
1302 """ |
|
1303 Private slot to handle the selection from the completion list. |
|
1304 |
|
1305 @param id the ID of the user list (should be 1) (integer) |
|
1306 @param txt the selected text (string) |
|
1307 """ |
|
1308 if id == 1: |
|
1309 if self.completionText != "": |
|
1310 txt = txt.replace(self.completionText, "") |
|
1311 self.__insertText(txt) |
|
1312 self.completionText = "" |
|
1313 |
|
1314 ################################################################# |
|
1315 ## Drag and Drop Support |
|
1316 ################################################################# |
|
1317 |
|
1318 def dragEnterEvent(self, event): |
|
1319 """ |
|
1320 Protected method to handle the drag enter event. |
|
1321 |
|
1322 @param event the drag enter event (QDragEnterEvent) |
|
1323 """ |
|
1324 self.inDragDrop = event.mimeData().hasUrls() or event.mimeData().hasText() |
|
1325 if self.inDragDrop: |
|
1326 event.acceptProposedAction() |
|
1327 else: |
|
1328 QsciScintillaCompat.dragEnterEvent(self, event) |
|
1329 |
|
1330 def dragMoveEvent(self, event): |
|
1331 """ |
|
1332 Protected method to handle the drag move event. |
|
1333 |
|
1334 @param event the drag move event (QDragMoveEvent) |
|
1335 """ |
|
1336 if self.inDragDrop: |
|
1337 event.accept() |
|
1338 else: |
|
1339 QsciScintillaCompat.dragMoveEvent(self, event) |
|
1340 |
|
1341 def dragLeaveEvent(self, event): |
|
1342 """ |
|
1343 Protected method to handle the drag leave event. |
|
1344 |
|
1345 @param event the drag leave event (QDragLeaveEvent) |
|
1346 """ |
|
1347 if self.inDragDrop: |
|
1348 self.inDragDrop = False |
|
1349 event.accept() |
|
1350 else: |
|
1351 QsciScintillaCompat.dragLeaveEvent(self, event) |
|
1352 |
|
1353 def dropEvent(self, event): |
|
1354 """ |
|
1355 Protected method to handle the drop event. |
|
1356 |
|
1357 @param event the drop event (QDropEvent) |
|
1358 """ |
|
1359 if event.mimeData().hasUrls(): |
|
1360 for url in event.mimeData().urls(): |
|
1361 fname = url.toLocalFile() |
|
1362 if fname: |
|
1363 if not QFileInfo(fname).isDir(): |
|
1364 self.vm.openSourceFile(fname) |
|
1365 else: |
|
1366 QMessageBox.information(None, |
|
1367 self.trUtf8("Drop Error"), |
|
1368 self.trUtf8("""<p><b>{0}</b> is not a file.</p>""") |
|
1369 .format(fname)) |
|
1370 event.acceptProposedAction() |
|
1371 elif event.mimeData().hasText(): |
|
1372 s = event.mimeData().text() |
|
1373 if s: |
|
1374 event.acceptProposedAction() |
|
1375 self.executeLines(s) |
|
1376 del s |
|
1377 else: |
|
1378 QsciScintillaCompat.dropEvent(self, event) |
|
1379 |
|
1380 self.inDragDrop = False |
|
1381 |
|
1382 def focusInEvent(self, event): |
|
1383 """ |
|
1384 Public method called when the shell receives focus. |
|
1385 |
|
1386 @param event the event object (QFocusEvent) |
|
1387 """ |
|
1388 if not self.__actionsAdded: |
|
1389 self.addActions(self.vm.editorActGrp.actions()) |
|
1390 self.addActions(self.vm.copyActGrp.actions()) |
|
1391 self.addActions(self.vm.viewActGrp.actions()) |
|
1392 |
|
1393 try: |
|
1394 self.vm.editActGrp.setEnabled(False) |
|
1395 self.vm.editorActGrp.setEnabled(True) |
|
1396 self.vm.copyActGrp.setEnabled(True) |
|
1397 self.vm.viewActGrp.setEnabled(True) |
|
1398 except AttributeError: |
|
1399 pass |
|
1400 self.setCaretWidth(self.caretWidth) |
|
1401 QsciScintillaCompat.focusInEvent(self, event) |
|
1402 |
|
1403 def focusOutEvent(self, event): |
|
1404 """ |
|
1405 Public method called when the shell loses focus. |
|
1406 |
|
1407 @param event the event object (QFocusEvent) |
|
1408 """ |
|
1409 try: |
|
1410 self.vm.editorActGrp.setEnabled(False) |
|
1411 except AttributeError: |
|
1412 pass |
|
1413 self.setCaretWidth(0) |
|
1414 QsciScintillaCompat.focusOutEvent(self, event) |
|
1415 |
|
1416 def insert(self, txt): |
|
1417 """ |
|
1418 Public slot to insert text at the current cursor position. |
|
1419 |
|
1420 The cursor is advanced to the end of the inserted text. |
|
1421 |
|
1422 @param txt text to be inserted (string) |
|
1423 """ |
|
1424 l = len(txt) |
|
1425 line, col = self.getCursorPosition() |
|
1426 self.insertAt(txt, line, col) |
|
1427 if re.search(self.linesepRegExp, txt) is not None: |
|
1428 line += 1 |
|
1429 self.setCursorPosition(line, col + l) |
|
1430 |
|
1431 def __configure(self): |
|
1432 """ |
|
1433 Private method to open the configuration dialog. |
|
1434 """ |
|
1435 e4App().getObject("UserInterface").showPreferences("shellPage") |