eric6/QScintilla/Shell.py

branch
maintenance
changeset 8273
698ae46f40a4
parent 8176
31965986ecd1
parent 8265
0090cfa83159
child 8576
fe1957c69854
equal deleted inserted replaced
8190:fb0ef164f536 8273:698ae46f40a4
7 Module implementing a graphical Python shell. 7 Module implementing a graphical Python shell.
8 """ 8 """
9 9
10 import sys 10 import sys
11 import re 11 import re
12 12 import contextlib
13 from enum import Enum 13 import enum
14 14
15 from PyQt5.QtCore import pyqtSignal, pyqtSlot, QFileInfo, Qt, QEvent 15 from PyQt5.QtCore import pyqtSignal, pyqtSlot, QFileInfo, Qt, QEvent
16 from PyQt5.QtGui import QClipboard, QPalette, QFont 16 from PyQt5.QtGui import QClipboard, QPalette, QFont
17 from PyQt5.QtWidgets import ( 17 from PyQt5.QtWidgets import (
18 QDialog, QInputDialog, QApplication, QMenu, QWidget, QHBoxLayout, 18 QDialog, QInputDialog, QApplication, QMenu, QWidget, QHBoxLayout,
50 @param horizontal flag indicating a horizontal layout 50 @param horizontal flag indicating a horizontal layout
51 @type bool 51 @type bool
52 @param parent parent widget 52 @param parent parent widget
53 @type QWidget 53 @type QWidget
54 """ 54 """
55 super(ShellAssembly, self).__init__(parent) 55 super().__init__(parent)
56 56
57 self.__shell = Shell(dbs, vm, project, False, self) 57 self.__shell = Shell(dbs, vm, project, False, self)
58 58
59 from UI.SearchWidget import SearchWidget 59 from UI.SearchWidget import SearchWidget
60 self.__searchWidget = SearchWidget(self.__shell, self, horizontal) 60 self.__searchWidget = SearchWidget(self.__shell, self, horizontal)
90 @return reference to the shell widget (Shell) 90 @return reference to the shell widget (Shell)
91 """ 91 """
92 return self.__shell 92 return self.__shell
93 93
94 94
95 class ShellHistoryStyle(Enum): 95 class ShellHistoryStyle(enum.Enum):
96 """ 96 """
97 Class defining the shell history styles. 97 Class defining the shell history styles.
98 """ 98 """
99 Disabled = 0 99 DISABLED = 0
100 LinuxStyle = 1 100 LINUXSTYLE = 1
101 WindowsStyle = 2 101 WINDOWSSTYLE = 2
102 102
103 103
104 class Shell(QsciScintillaCompat): 104 class Shell(QsciScintillaCompat):
105 """ 105 """
106 Class implementing a graphical Python shell. 106 Class implementing a graphical Python shell.
134 @param windowedVariant flag indicating the shell window variant 134 @param windowedVariant flag indicating the shell window variant
135 @type bool 135 @type bool
136 @param parent parent widget 136 @param parent parent widget
137 @type QWidget 137 @type QWidget
138 """ 138 """
139 super(Shell, self).__init__(parent) 139 super().__init__(parent)
140 self.setUtf8(True) 140 self.setUtf8(True)
141 141
142 self.vm = vm 142 self.vm = vm
143 self.__mainWindow = parent 143 self.__mainWindow = parent
144 self.__lastSearch = () 144 self.__lastSearch = ()
240 dbs.clientSyntaxError.connect(self.__clientSyntaxError) 240 dbs.clientSyntaxError.connect(self.__clientSyntaxError)
241 dbs.clientSignal.connect(self.__clientSignal) 241 dbs.clientSignal.connect(self.__clientSignal)
242 dbs.mainClientExit.connect(self.__writePrompt) 242 dbs.mainClientExit.connect(self.__writePrompt)
243 self.dbs = dbs 243 self.dbs = dbs
244 244
245 self.__debugUI = None 245 # will register a method to get the debugger ID to work with
246 self.__getSelectedDebuggerId = None
246 247
247 # Make sure we have prompts. 248 # Make sure we have prompts.
248 if self.passive: 249 if self.passive:
249 sys.ps1 = self.tr("Passive >>> ") 250 sys.ps1 = self.tr("Passive >>> ")
250 else: 251 else:
481 """ 482 """
482 self.setTabWidth(Preferences.getEditor("TabWidth")) 483 self.setTabWidth(Preferences.getEditor("TabWidth"))
483 if Preferences.getEditor("ShowWhitespace"): 484 if Preferences.getEditor("ShowWhitespace"):
484 self.setWhitespaceVisibility( 485 self.setWhitespaceVisibility(
485 QsciScintilla.WhitespaceVisibility.WsVisible) 486 QsciScintilla.WhitespaceVisibility.WsVisible)
486 try: 487 with contextlib.suppress(AttributeError):
487 self.setWhitespaceForegroundColor( 488 self.setWhitespaceForegroundColor(
488 Preferences.getEditorColour("WhitespaceForeground")) 489 Preferences.getEditorColour("WhitespaceForeground"))
489 self.setWhitespaceBackgroundColor( 490 self.setWhitespaceBackgroundColor(
490 Preferences.getEditorColour("WhitespaceBackground")) 491 Preferences.getEditorColour("WhitespaceBackground"))
491 self.setWhitespaceSize( 492 self.setWhitespaceSize(
492 Preferences.getEditor("WhitespaceSize")) 493 Preferences.getEditor("WhitespaceSize"))
493 except AttributeError:
494 # QScintilla before 2.5 doesn't support this
495 pass
496 else: 494 else:
497 self.setWhitespaceVisibility( 495 self.setWhitespaceVisibility(
498 QsciScintilla.WhitespaceVisibility.WsInvisible) 496 QsciScintilla.WhitespaceVisibility.WsInvisible)
499 self.setEolVisibility(Preferences.getEditor("ShowEOL")) 497 self.setEolVisibility(Preferences.getEditor("ShowEOL"))
500 if Preferences.getEditor("BraceHighlighting"): 498 if Preferences.getEditor("BraceHighlighting"):
612 Public method to set the debugger UI. 610 Public method to set the debugger UI.
613 611
614 @param ui reference to the debugger UI object (DebugUI) 612 @param ui reference to the debugger UI object (DebugUI)
615 """ 613 """
616 ui.exceptionInterrupt.connect(self.__writePrompt) 614 ui.exceptionInterrupt.connect(self.__writePrompt)
617 self.__debugUI = ui 615 self.registerDebuggerIdMethod(ui.getSelectedDebuggerId)
616
617 def registerDebuggerIdMethod(self, method):
618 """
619 Public method to register a method to get the debugger ID to send data
620 to.
621
622 @param method reference to the method
623 @type function
624 """
625 self.__getSelectedDebuggerId = method
618 626
619 def __initialise(self): 627 def __initialise(self):
620 """ 628 """
621 Private method to get ready for a new remote interpreter. 629 Private method to get ready for a new remote interpreter.
622 """ 630 """
673 """ 681 """
674 if index is None: 682 if index is None:
675 # determine based on history style 683 # determine based on history style
676 if ( 684 if (
677 self.clientType and 685 self.clientType and
678 self.__historyStyle == ShellHistoryStyle.WindowsStyle 686 self.__historyStyle == ShellHistoryStyle.WINDOWSSTYLE
679 ): 687 ):
680 idx = int(Preferences.Prefs.settings.value( 688 idx = int(Preferences.Prefs.settings.value(
681 "Shell/HistoryIndexes/" + self.clientType, -1)) 689 "Shell/HistoryIndexes/" + self.clientType, -1))
682 if idx >= len(self.__history): 690 if idx >= len(self.__history):
683 idx = -1 691 idx = -1
688 self.__histidx = index 696 self.__histidx = index
689 if self.__histidx >= len(self.__history): 697 if self.__histidx >= len(self.__history):
690 self.__histidx = -1 698 self.__histidx = -1
691 if ( 699 if (
692 self.clientType and 700 self.clientType and
693 self.__historyStyle == ShellHistoryStyle.WindowsStyle 701 self.__historyStyle == ShellHistoryStyle.WINDOWSSTYLE
694 ): 702 ):
695 Preferences.Prefs.settings.setValue( 703 Preferences.Prefs.settings.setValue(
696 "Shell/HistoryIndexes/" + self.clientType, self.__histidx) 704 "Shell/HistoryIndexes/" + self.clientType, self.__histidx)
697 705
698 def __isHistoryIndexValid(self): 706 def __isHistoryIndexValid(self):
840 @param platform platform of the remote interpreter 848 @param platform platform of the remote interpreter
841 @type str 849 @type str
842 @param venvName name of the virtual environment 850 @param venvName name of the virtual environment
843 @type str 851 @type str
844 """ 852 """
845 super(Shell, self).clear() 853 super().clear()
846 if self.passive and not self.dbs.isConnected(): 854 if self.passive and not self.dbs.isConnected():
847 self.__write(self.tr('Passive Debug Mode')) 855 self.__write(self.tr('Passive Debug Mode'))
848 self.__write(self.tr('\nNot connected')) 856 self.__write(self.tr('\nNot connected'))
849 else: 857 else:
850 self.__currentVenv = venvName 858 self.__currentVenv = venvName
890 """ 898 """
891 self .__clientError() 899 self .__clientError()
892 900
893 if ( 901 if (
894 not self.__windowed and 902 not self.__windowed and
895 Preferences.getDebugger("ShowExceptionInShell") 903 Preferences.getDebugger("ShowExceptionInShell") and
904 exceptionType
896 ): 905 ):
897 if exceptionType: 906 if stackTrace:
898 if stackTrace: 907 self.__write(
899 self.__write( 908 self.tr('Exception "{0}"\n{1}\nFile: {2}, Line: {3}\n')
900 self.tr('Exception "{0}"\n{1}\nFile: {2}, Line: {3}\n') 909 .format(
901 .format( 910 exceptionType,
902 exceptionType, 911 exceptionMessage,
903 exceptionMessage, 912 stackTrace[0][0],
904 stackTrace[0][0], 913 stackTrace[0][1]
905 stackTrace[0][1]
906 )
907 ) 914 )
908 else: 915 )
909 self.__write( 916 else:
910 self.tr('Exception "{0}"\n{1}\n') 917 self.__write(
911 .format( 918 self.tr('Exception "{0}"\n{1}\n')
912 exceptionType, 919 .format(
913 exceptionMessage) 920 exceptionType,
914 ) 921 exceptionMessage)
922 )
915 923
916 def __clientSyntaxError(self, message, filename, lineNo, characterNo): 924 def __clientSyntaxError(self, message, filename, lineNo, characterNo):
917 """ 925 """
918 Private method to handle a syntax error in the debugged program. 926 Private method to handle a syntax error in the debugged program.
919 927
1242 self.setFocus() 1250 self.setFocus()
1243 if event.button() == Qt.MouseButton.MidButton: 1251 if event.button() == Qt.MouseButton.MidButton:
1244 lines = QApplication.clipboard().text(QClipboard.Mode.Selection) 1252 lines = QApplication.clipboard().text(QClipboard.Mode.Selection)
1245 self.paste(lines) 1253 self.paste(lines)
1246 else: 1254 else:
1247 super(Shell, self).mousePressEvent(event) 1255 super().mousePressEvent(event)
1248 1256
1249 def wheelEvent(self, evt): 1257 def wheelEvent(self, evt):
1250 """ 1258 """
1251 Protected method to handle wheel events. 1259 Protected method to handle wheel events.
1252 1260
1259 elif delta > 0: 1267 elif delta > 0:
1260 self.zoomIn() 1268 self.zoomIn()
1261 evt.accept() 1269 evt.accept()
1262 return 1270 return
1263 1271
1264 super(Shell, self).wheelEvent(evt) 1272 super().wheelEvent(evt)
1265 1273
1266 def event(self, evt): 1274 def event(self, evt):
1267 """ 1275 """
1268 Public method handling events. 1276 Public method handling events.
1269 1277
1272 """ 1280 """
1273 if evt.type() == QEvent.Type.Gesture: 1281 if evt.type() == QEvent.Type.Gesture:
1274 self.gestureEvent(evt) 1282 self.gestureEvent(evt)
1275 return True 1283 return True
1276 1284
1277 return super(Shell, self).event(evt) 1285 return super().event(evt)
1278 1286
1279 def gestureEvent(self, evt): 1287 def gestureEvent(self, evt):
1280 """ 1288 """
1281 Protected method handling gesture events. 1289 Protected method handling gesture events.
1282 1290
1334 line, col = self.__getEndPos() 1342 line, col = self.__getEndPos()
1335 self.setCursorPosition(line, col) 1343 self.setCursorPosition(line, col)
1336 self.prline, self.prcol = self.getCursorPosition() 1344 self.prline, self.prcol = self.getCursorPosition()
1337 if self.__echoInput: 1345 if self.__echoInput:
1338 ac = self.isListActive() 1346 ac = self.isListActive()
1339 super(Shell, self).keyPressEvent(ev) 1347 super().keyPressEvent(ev)
1340 self.incrementalSearchActive = True 1348 self.incrementalSearchActive = True
1341 if ac and self.racEnabled: 1349 if ac and self.racEnabled:
1342 self.dbs.remoteCompletion( 1350 self.dbs.remoteCompletion(
1343 self.__debugUI.getSelectedDebuggerId(), 1351 self.__getSelectedDebuggerId(),
1344 self.completionText + txt 1352 self.completionText + txt
1345 ) 1353 )
1346 else: 1354 else:
1347 self.__insertTextNoEcho(txt) 1355 self.__insertTextNoEcho(txt)
1348 else: 1356 else:
1373 buf = buf.replace(sys.ps2, "") 1381 buf = buf.replace(sys.ps2, "")
1374 if self.inContinue and not buf[:index - len(sys.ps2)].strip(): 1382 if self.inContinue and not buf[:index - len(sys.ps2)].strip():
1375 self.SendScintilla(cmd) 1383 self.SendScintilla(cmd)
1376 elif self.racEnabled: 1384 elif self.racEnabled:
1377 self.dbs.remoteCompletion( 1385 self.dbs.remoteCompletion(
1378 self.__debugUI.getSelectedDebuggerId(), 1386 self.__getSelectedDebuggerId(),
1379 buf 1387 buf
1380 ) 1388 )
1381 1389
1382 def __QScintillaLeftDeleteCommand(self, method): 1390 def __QScintillaLeftDeleteCommand(self, method):
1383 """ 1391 """
1404 method() 1412 method()
1405 db = 1 1413 db = 1
1406 if db and ac and self.racEnabled and self.completionText: 1414 if db and ac and self.racEnabled and self.completionText:
1407 delta = len(self.text(line)) - oldLength 1415 delta = len(self.text(line)) - oldLength
1408 self.dbs.remoteCompletion( 1416 self.dbs.remoteCompletion(
1409 self.__debugUI.getSelectedDebuggerId(), 1417 self.__getSelectedDebuggerId(),
1410 self.completionText[:delta] 1418 self.completionText[:delta]
1411 ) 1419 )
1412 1420
1413 def __QScintillaDeleteBack(self): 1421 def __QScintillaDeleteBack(self):
1414 """ 1422 """
1797 if cmd != "" and ( 1805 if cmd != "" and (
1798 len(self.__history) == 0 or self.__history[-1] != cmd): 1806 len(self.__history) == 0 or self.__history[-1] != cmd):
1799 if len(self.__history) == self.__maxHistoryEntries: 1807 if len(self.__history) == self.__maxHistoryEntries:
1800 del self.__history[0] 1808 del self.__history[0]
1801 self.__history.append(cmd) 1809 self.__history.append(cmd)
1802 if self.__historyStyle == ShellHistoryStyle.LinuxStyle: 1810 if self.__historyStyle == ShellHistoryStyle.LINUXSTYLE:
1803 self.__setHistoryIndex(index=-1) 1811 self.__setHistoryIndex(index=-1)
1804 elif self.__historyStyle == ShellHistoryStyle.WindowsStyle: 1812 elif self.__historyStyle == ShellHistoryStyle.WINDOWSSTYLE:
1805 if historyIndex is None: 1813 if historyIndex is None:
1806 if ( 1814 if (
1807 self.__histidx - 1 > 0 and 1815 self.__histidx - 1 > 0 and
1808 cmd != self.__history[self.__histidx - 1] 1816 cmd != self.__history[self.__histidx - 1]
1809 ): 1817 ):
1887 # call main window quit() 1895 # call main window quit()
1888 self.vm.quit() 1896 self.vm.quit()
1889 return 1897 return
1890 else: 1898 else:
1891 self.dbs.remoteStatement( 1899 self.dbs.remoteStatement(
1892 self.__debugUI.getSelectedDebuggerId(), cmd) 1900 self.__getSelectedDebuggerId(), cmd)
1893 while self.inCommandExecution: 1901 while self.inCommandExecution:
1894 try: 1902 with contextlib.suppress(KeyboardInterrupt):
1895 QApplication.processEvents() 1903 QApplication.processEvents()
1896 except KeyboardInterrupt:
1897 pass
1898 else: 1904 else:
1899 if not self.__echoInput: 1905 if not self.__echoInput:
1900 cmd = self.buff 1906 cmd = self.buff
1901 self.buff = "" 1907 self.buff = ""
1902 elif cmd: 1908 elif cmd:
1960 @type int 1966 @type int
1961 @return tuple containing the index of found entry and a flag indicating 1967 @return tuple containing the index of found entry and a flag indicating
1962 that something was found 1968 that something was found
1963 @rtype tuple of (int, bool) 1969 @rtype tuple of (int, bool)
1964 """ 1970 """
1965 if startIdx == -1: 1971 idx = 0 if startIdx == -1 else startIdx + 1
1966 idx = 0
1967 else:
1968 idx = startIdx + 1
1969 while ( 1972 while (
1970 idx < len(self.__history) and 1973 idx < len(self.__history) and
1971 not self.__history[idx].startswith(txt) 1974 not self.__history[idx].startswith(txt)
1972 ): 1975 ):
1973 idx += 1 1976 idx += 1
1985 @type int 1988 @type int
1986 @return tuple containing the index of found entry and a flag indicating 1989 @return tuple containing the index of found entry and a flag indicating
1987 that something was found 1990 that something was found
1988 @rtype tuple of (int, bool) 1991 @rtype tuple of (int, bool)
1989 """ 1992 """
1990 if startIdx == -1: 1993 idx = len(self.__history) - 1 if startIdx == -1 else startIdx - 1
1991 idx = len(self.__history) - 1
1992 else:
1993 idx = startIdx - 1
1994 while ( 1994 while (
1995 idx >= 0 and 1995 idx >= 0 and
1996 not self.__history[idx].startswith(txt) 1996 not self.__history[idx].startswith(txt)
1997 ): 1997 ):
1998 idx -= 1 1998 idx -= 1
2156 event.mimeData().hasText() 2156 event.mimeData().hasText()
2157 ) 2157 )
2158 if self.inDragDrop: 2158 if self.inDragDrop:
2159 event.acceptProposedAction() 2159 event.acceptProposedAction()
2160 else: 2160 else:
2161 super(Shell, self).dragEnterEvent(event) 2161 super().dragEnterEvent(event)
2162 2162
2163 def dragMoveEvent(self, event): 2163 def dragMoveEvent(self, event):
2164 """ 2164 """
2165 Protected method to handle the drag move event. 2165 Protected method to handle the drag move event.
2166 2166
2167 @param event the drag move event (QDragMoveEvent) 2167 @param event the drag move event (QDragMoveEvent)
2168 """ 2168 """
2169 if self.inDragDrop: 2169 if self.inDragDrop:
2170 event.accept() 2170 event.accept()
2171 else: 2171 else:
2172 super(Shell, self).dragMoveEvent(event) 2172 super().dragMoveEvent(event)
2173 2173
2174 def dragLeaveEvent(self, event): 2174 def dragLeaveEvent(self, event):
2175 """ 2175 """
2176 Protected method to handle the drag leave event. 2176 Protected method to handle the drag leave event.
2177 2177
2179 """ 2179 """
2180 if self.inDragDrop: 2180 if self.inDragDrop:
2181 self.inDragDrop = False 2181 self.inDragDrop = False
2182 event.accept() 2182 event.accept()
2183 else: 2183 else:
2184 super(Shell, self).dragLeaveEvent(event) 2184 super().dragLeaveEvent(event)
2185 2185
2186 def dropEvent(self, event): 2186 def dropEvent(self, event):
2187 """ 2187 """
2188 Protected method to handle the drop event. 2188 Protected method to handle the drop event.
2189 2189
2207 if s: 2207 if s:
2208 event.acceptProposedAction() 2208 event.acceptProposedAction()
2209 self.executeLines(s) 2209 self.executeLines(s)
2210 del s 2210 del s
2211 else: 2211 else:
2212 super(Shell, self).dropEvent(event) 2212 super().dropEvent(event)
2213 2213
2214 self.inDragDrop = False 2214 self.inDragDrop = False
2215 2215
2216 def focusInEvent(self, event): 2216 def focusInEvent(self, event):
2217 """ 2217 """
2232 self.__searchNext, self.__searchNext) 2232 self.__searchNext, self.__searchNext)
2233 self.__searchPrevShortcut = QShortcut( 2233 self.__searchPrevShortcut = QShortcut(
2234 self.vm.searchPrevAct.shortcut(), self, 2234 self.vm.searchPrevAct.shortcut(), self,
2235 self.__searchPrev, self.__searchPrev) 2235 self.__searchPrev, self.__searchPrev)
2236 2236
2237 try: 2237 with contextlib.suppress(AttributeError):
2238 self.vm.editActGrp.setEnabled(False) 2238 self.vm.editActGrp.setEnabled(False)
2239 self.vm.editorActGrp.setEnabled(True) 2239 self.vm.editorActGrp.setEnabled(True)
2240 self.vm.copyActGrp.setEnabled(True) 2240 self.vm.copyActGrp.setEnabled(True)
2241 self.vm.viewActGrp.setEnabled(True) 2241 self.vm.viewActGrp.setEnabled(True)
2242 self.vm.searchActGrp.setEnabled(False) 2242 self.vm.searchActGrp.setEnabled(False)
2243 except AttributeError:
2244 pass
2245 if not self.__windowed: 2243 if not self.__windowed:
2246 self.__searchShortcut.setEnabled(True) 2244 self.__searchShortcut.setEnabled(True)
2247 self.__searchNextShortcut.setEnabled(True) 2245 self.__searchNextShortcut.setEnabled(True)
2248 self.__searchPrevShortcut.setEnabled(True) 2246 self.__searchPrevShortcut.setEnabled(True)
2249 self.setCaretWidth(self.caretWidth) 2247 self.setCaretWidth(self.caretWidth)
2250 self.setCursorFlashTime(QApplication.cursorFlashTime()) 2248 self.setCursorFlashTime(QApplication.cursorFlashTime())
2251 2249
2252 super(Shell, self).focusInEvent(event) 2250 super().focusInEvent(event)
2253 2251
2254 def focusOutEvent(self, event): 2252 def focusOutEvent(self, event):
2255 """ 2253 """
2256 Protected method called when the shell loses focus. 2254 Protected method called when the shell loses focus.
2257 2255
2258 @param event the event object (QFocusEvent) 2256 @param event the event object (QFocusEvent)
2259 """ 2257 """
2260 try: 2258 with contextlib.suppress(AttributeError):
2261 self.vm.editorActGrp.setEnabled(False) 2259 self.vm.editorActGrp.setEnabled(False)
2262 except AttributeError:
2263 pass
2264 if not self.__windowed: 2260 if not self.__windowed:
2265 self.__searchShortcut.setEnabled(False) 2261 self.__searchShortcut.setEnabled(False)
2266 self.__searchNextShortcut.setEnabled(False) 2262 self.__searchNextShortcut.setEnabled(False)
2267 self.__searchPrevShortcut.setEnabled(False) 2263 self.__searchPrevShortcut.setEnabled(False)
2268 self.setCaretWidth(0) 2264 self.setCaretWidth(0)
2269 super(Shell, self).focusOutEvent(event) 2265 super().focusOutEvent(event)
2270 2266
2271 def insert(self, txt): 2267 def insert(self, txt):
2272 """ 2268 """
2273 Public slot to insert text at the current cursor position. 2269 Public slot to insert text at the current cursor position.
2274 2270
2376 Public method to check, if the history is enabled. 2372 Public method to check, if the history is enabled.
2377 2373
2378 @return flag indicating if history is enabled 2374 @return flag indicating if history is enabled
2379 @rtype bool 2375 @rtype bool
2380 """ 2376 """
2381 return self.__historyStyle != ShellHistoryStyle.Disabled 2377 return self.__historyStyle != ShellHistoryStyle.DISABLED
2382 2378
2383 ################################################################# 2379 #################################################################
2384 ## Project Support 2380 ## Project Support
2385 ################################################################# 2381 #################################################################
2386 2382

eric ide

mercurial