10 import sys |
10 import sys |
11 import re |
11 import re |
12 |
12 |
13 from enum import Enum |
13 from enum import Enum |
14 |
14 |
15 from PyQt5.QtCore import pyqtSignal, 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, |
19 QVBoxLayout, QShortcut, QSizePolicy |
19 QVBoxLayout, QShortcut, QSizePolicy |
20 ) |
20 ) |
601 Public method to set the debugger UI. |
603 Public method to set the debugger UI. |
602 |
604 |
603 @param ui reference to the debugger UI object (DebugUI) |
605 @param ui reference to the debugger UI object (DebugUI) |
604 """ |
606 """ |
605 ui.exceptionInterrupt.connect(self.__writePrompt) |
607 ui.exceptionInterrupt.connect(self.__writePrompt) |
|
608 self.__debugUI = ui |
606 |
609 |
607 def __initialise(self): |
610 def __initialise(self): |
608 """ |
611 """ |
609 Private method to get ready for a new remote interpreter. |
612 Private method to get ready for a new remote interpreter. |
610 """ |
613 """ |
611 self.buff = "" |
614 self.buff = "" |
612 self.inContinue = False |
615 self.inContinue = False |
613 self.inRawMode = False |
616 self.__inRawMode = False |
614 self.echoInput = True |
617 self.__echoInput = True |
|
618 self.__rawModeDebuggerId = None |
|
619 self.__rawModeQueue = [] |
615 self.clientCapabilities = 0 |
620 self.clientCapabilities = 0 |
616 self.inCommandExecution = False |
621 self.inCommandExecution = False |
617 self.interruptCommandExecution = False |
622 self.interruptCommandExecution = False |
618 |
623 |
619 def __clientCapabilities(self, cap, clType, venvName): |
624 def __clientCapabilities(self, cap, clType, venvName): |
815 if self.passive: |
820 if self.passive: |
816 self.__writeBanner('', '', '', '') |
821 self.__writeBanner('', '', '', '') |
817 else: |
822 else: |
818 self.dbs.remoteBanner() |
823 self.dbs.remoteBanner() |
819 |
824 |
820 def __writeBanner(self, version, platform, dbgclient, venvName): |
825 def __writeBanner(self, version, platform, venvName): |
821 """ |
826 """ |
822 Private method to write a banner with info from the debug client. |
827 Private method to write a banner with info from the debug client. |
823 |
828 |
824 @param version interpreter version string |
829 @param version interpreter version string |
825 @type str |
830 @type str |
826 @param platform platform of the remote interpreter |
831 @param platform platform of the remote interpreter |
827 @type str |
832 @type str |
828 @param dbgclient debug client variant used |
|
829 @type str |
|
830 @param venvName name of the virtual environment |
833 @param venvName name of the virtual environment |
831 @type str |
834 @type str |
832 """ |
835 """ |
833 super(Shell, self).clear() |
836 super(Shell, self).clear() |
834 if self.passive and not self.dbs.isConnected(): |
837 if self.passive and not self.dbs.isConnected(): |
835 self.__write(self.tr('Passive Debug Mode')) |
838 self.__write(self.tr('Passive Debug Mode')) |
836 self.__write(self.tr('\nNot connected')) |
839 self.__write(self.tr('\nNot connected')) |
837 else: |
840 else: |
838 self.__currentVenv = venvName |
841 self.__currentVenv = venvName |
839 version = version.replace("#", self.tr("No.")) |
842 version = version.replace("#", self.tr("No.")) |
840 if platform != "" and dbgclient != "": |
843 if platform != "": |
841 self.__write( |
844 self.__write(self.tr('{0} on {1}').format(version, platform)) |
842 self.tr('{0} on {1}, {2}') |
|
843 .format(version, platform, dbgclient)) |
|
844 else: |
845 else: |
845 self.__write(version) |
846 self.__write(version) |
846 if venvName: |
847 if venvName: |
847 self.__write("\n[{0}]".format(venvName)) |
848 self.__write("\n[{0}]".format(venvName)) |
848 |
849 |
860 |
861 |
861 def __clientStatement(self, more): |
862 def __clientStatement(self, more): |
862 """ |
863 """ |
863 Private method to handle the response from the debugger client. |
864 Private method to handle the response from the debugger client. |
864 |
865 |
865 @param more flag indicating that more user input is required (boolean) |
866 @param more flag indicating that more user input is required |
866 """ |
867 @type bool |
867 if not self.inRawMode: |
868 """ |
|
869 if not self.__inRawMode: |
868 self.inContinue = more |
870 self.inContinue = more |
869 self.__writePrompt() |
871 self.__writePrompt() |
870 self.inCommandExecution = False |
872 self.inCommandExecution = False |
871 |
873 |
872 def __clientException(self, exceptionType, exceptionMessage, stackTrace): |
874 def __clientException(self, exceptionType, exceptionMessage, stackTrace): |
1035 |
1037 |
1036 @param s text to be displayed (string) |
1038 @param s text to be displayed (string) |
1037 """ |
1039 """ |
1038 self.__write(self.tr("StdErr: {0}").format(s)) |
1040 self.__write(self.tr("StdErr: {0}").format(s)) |
1039 |
1041 |
1040 def __raw_input(self, prompt, echo): |
1042 def __raw_input(self, prompt, echo, debuggerId): |
1041 """ |
1043 """ |
1042 Private method to handle raw input. |
1044 Private method to handle raw input. |
1043 |
1045 |
1044 @param prompt prompt to be displayed |
1046 @param prompt the input prompt |
1045 @type str |
1047 @type str |
1046 @param echo Flag indicating echoing of the input |
1048 @param echo flag indicating an echoing of the input |
1047 @type bool |
1049 @type bool |
1048 """ |
1050 @param debuggerId ID of the debugger backend |
1049 self.setFocus() |
1051 @type str |
1050 self.inRawMode = True |
1052 """ |
1051 self.echoInput = echo |
1053 if self.__inRawMode: |
1052 |
1054 # we are processing another raw input event already |
1053 # Get all text which is still waiting for output |
1055 self.__rawModeQueue.append((debuggerId, prompt, echo)) |
1054 QApplication.processEvents() |
1056 else: |
1055 self.__flushQueuedText() |
1057 self.setFocus() |
1056 |
1058 self.__inRawMode = True |
1057 self.__write(prompt) |
1059 self.__echoInput = echo |
1058 line, col = self.__getEndPos() |
1060 self.__rawModeDebuggerId = debuggerId |
1059 self.setCursorPosition(line, col) |
1061 |
1060 buf = self.text(line) |
1062 # Get all text which is still waiting for output |
1061 if buf.startswith(sys.ps1): |
1063 QApplication.processEvents() |
1062 buf = buf.replace(sys.ps1, "") |
1064 self.__flushQueuedText() |
1063 if buf.startswith(sys.ps2): |
1065 |
1064 buf = buf.replace(sys.ps2, "") |
1066 self.__write(self.tr("<{0}> {1}").format(debuggerId, prompt)) |
1065 self.prompt = buf |
1067 line, col = self.__getEndPos() |
1066 # move cursor to end of line |
1068 self.setCursorPosition(line, col) |
1067 self.moveCursorToEOL() |
1069 buf = self.text(line) |
1068 |
1070 if buf.startswith(sys.ps1): |
1069 def paste(self): |
1071 buf = buf.replace(sys.ps1, "") |
|
1072 if buf.startswith(sys.ps2): |
|
1073 buf = buf.replace(sys.ps2, "") |
|
1074 self.prompt = buf |
|
1075 # move cursor to end of line |
|
1076 self.moveCursorToEOL() |
|
1077 |
|
1078 def paste(self, lines=None): |
1070 """ |
1079 """ |
1071 Public slot to handle the paste action. |
1080 Public slot to handle the paste action. |
|
1081 |
|
1082 @param lines list of lines to be inserted |
|
1083 @type list of str |
1072 """ |
1084 """ |
1073 if self.__isCursorOnLastLine(): |
1085 if self.__isCursorOnLastLine(): |
1074 line, col = self.getCursorPosition() |
1086 line, col = self.getCursorPosition() |
1075 lastLine = self.text(line) |
1087 lastLine = self.text(line) |
1076 if lastLine.startswith(sys.ps1): |
1088 if lastLine.startswith(sys.ps1): |
1102 col = indexFrom |
1114 col = indexFrom |
1103 |
1115 |
1104 self.setCursorPosition(line, len(prompt)) |
1116 self.setCursorPosition(line, len(prompt)) |
1105 self.deleteLineRight() |
1117 self.deleteLineRight() |
1106 |
1118 |
1107 lines = QApplication.clipboard().text() |
1119 if lines is None: |
|
1120 lines = QApplication.clipboard().text() |
|
1121 |
1108 lines = lastLine[:col] + lines + lastLine[col:] |
1122 lines = lastLine[:col] + lines + lastLine[col:] |
1109 self.executeLines(lines) |
1123 self.executeLines(lines) |
1110 line, _ = self.getCursorPosition() |
1124 line, _ = self.getCursorPosition() |
1111 pos = len(self.text(line)) - (len(lastLine) - col) |
1125 pos = len(self.text(line)) - (len(lastLine) - col) |
1112 self.setCursorPosition(line, pos) |
1126 self.setCursorPosition(line, pos) |
1113 |
1127 |
1114 def __middleMouseButton(self): |
|
1115 """ |
|
1116 Private method to handle the middle mouse button press. |
|
1117 """ |
|
1118 lines = QApplication.clipboard().text(QClipboard.Selection) |
|
1119 self.executeLines(lines) |
|
1120 |
|
1121 def executeLines(self, lines, historyIndex=None): |
1128 def executeLines(self, lines, historyIndex=None): |
1122 """ |
1129 """ |
1123 Public method to execute a set of lines as multiple commands. |
1130 Public method to execute a set of lines as multiple commands. |
1124 |
1131 |
1125 @param lines multiple lines of text to be executed as |
1132 @param lines multiple lines of text to be executed as |
1139 elif line.startswith(sys.ps2): |
1146 elif line.startswith(sys.ps2): |
1140 line = line[len(sys.ps2) + indentLen:] |
1147 line = line[len(sys.ps2) + indentLen:] |
1141 else: |
1148 else: |
1142 line = line[indentLen:] |
1149 line = line[indentLen:] |
1143 |
1150 |
1144 if line.endswith("\r\n"): |
1151 if line.endswith(("\r\n", "\r", "\n")): |
1145 fullline = True |
1152 fullline = True |
1146 cmd = line[:-2] |
1153 cmd = line.rstrip() |
1147 elif line.endswith("\r") or line.endswith("\n"): |
|
1148 fullline = True |
|
1149 cmd = line[:-1] |
|
1150 else: |
1154 else: |
1151 fullline = False |
1155 fullline = False |
1152 |
1156 |
1153 self.incrementalSearchActive = True |
1157 self.incrementalSearchActive = True |
1154 self.__insertTextAtEnd(line) |
1158 self.__insertTextAtEnd(line) |
1318 if len(txt) and txt >= " ": |
1323 if len(txt) and txt >= " ": |
1319 if not self.__isCursorOnLastLine(): |
1324 if not self.__isCursorOnLastLine(): |
1320 line, col = self.__getEndPos() |
1325 line, col = self.__getEndPos() |
1321 self.setCursorPosition(line, col) |
1326 self.setCursorPosition(line, col) |
1322 self.prline, self.prcol = self.getCursorPosition() |
1327 self.prline, self.prcol = self.getCursorPosition() |
1323 if self.echoInput: |
1328 if self.__echoInput: |
1324 ac = self.isListActive() |
1329 ac = self.isListActive() |
1325 super(Shell, self).keyPressEvent(ev) |
1330 super(Shell, self).keyPressEvent(ev) |
1326 self.incrementalSearchActive = True |
1331 self.incrementalSearchActive = True |
1327 if ac and self.racEnabled: |
1332 if ac and self.racEnabled: |
1328 self.dbs.remoteCompletion(self.completionText + txt) |
1333 self.dbs.remoteCompletion( |
|
1334 self.__debugUI.getSelectedDebuggerId(), |
|
1335 self.completionText + txt |
|
1336 ) |
1329 else: |
1337 else: |
1330 self.__insertTextNoEcho(txt) |
1338 self.__insertTextNoEcho(txt) |
1331 else: |
1339 else: |
1332 ev.ignore() |
1340 ev.ignore() |
1333 |
1341 |
1355 if buf.startswith(sys.ps2): |
1363 if buf.startswith(sys.ps2): |
1356 buf = buf.replace(sys.ps2, "") |
1364 buf = buf.replace(sys.ps2, "") |
1357 if self.inContinue and not buf[:index - len(sys.ps2)].strip(): |
1365 if self.inContinue and not buf[:index - len(sys.ps2)].strip(): |
1358 self.SendScintilla(cmd) |
1366 self.SendScintilla(cmd) |
1359 elif self.racEnabled: |
1367 elif self.racEnabled: |
1360 self.dbs.remoteCompletion(buf) |
1368 self.dbs.remoteCompletion( |
|
1369 self.__debugUI.getSelectedDebuggerId(), |
|
1370 buf |
|
1371 ) |
1361 |
1372 |
1362 def __QScintillaLeftDeleteCommand(self, method): |
1373 def __QScintillaLeftDeleteCommand(self, method): |
1363 """ |
1374 """ |
1364 Private method to handle a QScintilla delete command working to |
1375 Private method to handle a QScintilla delete command working to |
1365 the left. |
1376 the left. |
1850 ): |
1864 ): |
1851 # call main window quit() |
1865 # call main window quit() |
1852 self.vm.quit() |
1866 self.vm.quit() |
1853 return |
1867 return |
1854 |
1868 |
1855 self.dbs.remoteStatement(cmd) |
1869 self.dbs.remoteStatement(self.__debugUI.getSelectedDebuggerId(), |
|
1870 cmd) |
1856 while self.inCommandExecution: |
1871 while self.inCommandExecution: |
1857 try: |
1872 try: |
1858 QApplication.processEvents() |
1873 QApplication.processEvents() |
1859 except KeyboardInterrupt: |
1874 except KeyboardInterrupt: |
1860 pass |
1875 pass |
1861 else: |
1876 else: |
1862 if not self.echoInput: |
1877 if not self.__echoInput: |
1863 cmd = self.buff |
1878 cmd = self.buff |
1864 self.buff = "" |
1879 self.buff = "" |
1865 elif cmd: |
1880 elif cmd: |
1866 cmd = cmd[len(self.prompt):] |
1881 cmd = cmd[len(self.prompt):] |
1867 self.inRawMode = False |
1882 self.__inRawMode = False |
1868 self.echoInput = True |
1883 self.__echoInput = True |
1869 |
1884 |
1870 self.dbs.remoteRawInput(cmd) |
1885 self.dbs.remoteRawInput(self.__rawModeDebuggerId, cmd) |
|
1886 |
|
1887 if self.__rawModeQueue: |
|
1888 debuggerId, prompt, echo = self.__rawModeQueue.pop(0) |
|
1889 self.__raw_input(prompt, echo, debuggerId) |
1871 |
1890 |
1872 def __showVenvName(self): |
1891 def __showVenvName(self): |
1873 """ |
1892 """ |
1874 Private method to show the name of the active virtual environment. |
1893 Private method to show the name of the active virtual environment. |
1875 """ |
1894 """ |
2062 self.dbs.clientProcessStderr.connect(self.__writeStdErr) |
2081 self.dbs.clientProcessStderr.connect(self.__writeStdErr) |
2063 else: |
2082 else: |
2064 self.dbs.clientProcessStdout.disconnect(self.__writeStdOut) |
2083 self.dbs.clientProcessStdout.disconnect(self.__writeStdOut) |
2065 self.dbs.clientProcessStderr.disconnect(self.__writeStdErr) |
2084 self.dbs.clientProcessStderr.disconnect(self.__writeStdErr) |
2066 self.__showStdOutErr = showStdOutErr |
2085 self.__showStdOutErr = showStdOutErr |
2067 |
2086 |
|
2087 @pyqtSlot(list, str) |
2068 def __showCompletions(self, completions, text): |
2088 def __showCompletions(self, completions, text): |
2069 """ |
2089 """ |
2070 Private method to display the possible completions. |
2090 Private method to display the possible completions. |
2071 |
2091 |
2072 @param completions list of possible completions (list of strings) |
2092 @param completions list of possible completions (list of strings) |