QScintilla/Shell.py

changeset 0
de9c2efb9d02
child 7
c679fb30c8f3
equal deleted inserted replaced
-1:000000000000 0:de9c2efb9d02
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")

eric ide

mercurial