--- a/QScintilla/Shell.py Thu Nov 01 11:48:02 2018 +0100 +++ b/QScintilla/Shell.py Sat Dec 01 11:44:34 2018 +0100 @@ -41,18 +41,24 @@ """ Class implementing the containing widget for the shell. """ - def __init__(self, dbs, vm, horizontal=True, parent=None): + def __init__(self, dbs, vm, project, horizontal=True, parent=None): """ Constructor @param dbs reference to the debug server object + @type DebugServer @param vm reference to the viewmanager object - @param horizontal flag indicating a horizontal layout (boolean) - @param parent parent widget (QWidget) + @type ViewManager + @param project reference to the project object + @type Project + @param horizontal flag indicating a horizontal layout + @type bool + @param parent parent widget + @type QWidget """ super(ShellAssembly, self).__init__(parent) - self.__shell = Shell(dbs, vm, False, self) + self.__shell = Shell(dbs, vm, project, False, self) from UI.SearchWidget import SearchWidget self.__searchWidget = SearchWidget(self.__shell, self, horizontal) @@ -111,20 +117,28 @@ @signal historyStyleChanged(ShellHistoryStyle) emitted to indicate a change of the history style @signal queueText(str) emitted to queue some text for processing + @signal virtualEnvironmentChanged(str) emitted to signal the new virtual + environment of the shell """ searchStringFound = pyqtSignal(bool) historyStyleChanged = pyqtSignal(ShellHistoryStyle) queueText = pyqtSignal(str) + virtualEnvironmentChanged = pyqtSignal(str) - def __init__(self, dbs, vm, windowedVariant, parent=None): + def __init__(self, dbs, vm, project, windowedVariant, parent=None): """ Constructor @param dbs reference to the debug server object + @type DebugServer @param vm reference to the viewmanager object + @type ViewManager + @param project reference to the project object + @type Project @param windowedVariant flag indicating the shell window variant - (boolean) - @param parent parent widget (QWidget) + @type bool + @param parent parent widget + @type QWidget """ super(Shell, self).__init__(parent) self.setUtf8(True) @@ -133,6 +147,7 @@ self.__mainWindow = parent self.__lastSearch = () self.__windowed = windowedVariant + self.__currentVenv = "" self.linesepRegExp = r"\r\n|\n|\r" @@ -153,14 +168,18 @@ """ cursor keys on the Shell page of the configuration""" """ dialog. Pressing these keys after some text has been""" """ entered will start an incremental search.</p>""" - """<p>The shell has some special commands. 'reset' kills the""" - """ shell and starts a new one. 'clear' clears the display""" - """ of the shell window. 'start' is used to switch the shell""" - """ language and must be followed by a supported language.""" - """ Supported languages are listed by the 'languages'""" - """ command. 'quit' is used to exit the application.These""" - """ commands (except 'languages') are available through the""" - """ window menus as well.</p>""" + """<p>The shell has some special commands. 'restart' kills""" + """ the shell and starts a new one. 'clear' clears the""" + """ display of the shell window. 'start' is used to start a""" + """ shell for a virtual environment and should be followed""" + """ by a virtual environment name. start' without a virtual""" + """ environment name starts the default shell. Available""" + """ virtual environments may be listed with the 'envs' or""" + """ 'environments' commands. The active virtual environment""" + """ can be questioned by the 'which' command. 'quit' or""" + """ 'exit' is used to exit the application. These commands""" + """ (except environments', 'envs' and 'which') are available""" + """ through the window menus as well.</p>""" """<p>Pressing the Tab key after some text has been entered""" """ will show a list of possible completions. The relevant""" """ entry may be selected from this list. If only one entry""" @@ -180,13 +199,17 @@ """ cursor keys on the Shell page of the configuration""" """ dialog. Pressing these keys after some text has been""" """ entered will start an incremental search.</p>""" - """<p>The shell has some special commands. 'reset' kills the""" - """ shell and starts a new one. 'clear' clears the display""" - """ of the shell window. 'start' is used to switch the shell""" - """ language and must be followed by a supported language.""" - """ Supported languages are listed by the 'languages'""" - """ command. These commands (except 'languages') are""" - """ available through the context menu as well.</p>""" + """<p>The shell has some special commands. 'restart' kills""" + """ the shell and starts a new one. 'clear' clears the""" + """ display of the shell window. 'start' is used to start a""" + """ shell for a virtual environment and should be followed""" + """ by a virtual environment name. start' without a virtual""" + """ environment name starts the default shell. Available""" + """ virtual environments may be listed with the 'envs' or""" + """ 'environments' commands. The active virtual environment""" + """ can be questioned by the 'which' command. These commands""" + """ (except environments' and 'envs') are available through""" + """ the context menu as well.</p>""" """<p>Pressing the Tab key after some text has been entered""" """ will show a list of possible completions. The relevant""" """ entry may be selected from this list. If only one entry""" @@ -266,7 +289,7 @@ if not self.__windowed: # Create a little language context menu self.lmenu = QMenu(self.tr('Start')) - self.lmenu.aboutToShow.connect(self.__showLanguageMenu) + self.lmenu.aboutToShow.connect(self.__showStartMenu) self.lmenu.triggered.connect(self.__startDebugClient) # Create the history context menu @@ -286,11 +309,12 @@ self.menu.addAction(self.tr('Find'), self.__find) self.menu.addSeparator() self.menu.addAction(self.tr('Clear'), self.clear) - self.menu.addAction(self.tr('Reset'), self.__reset) + self.menu.addAction(self.tr('Restart'), self.doRestart) self.menu.addAction( - self.tr('Reset and Clear'), self.__resetAndClear) + self.tr('Restart and Clear'), self.doClearRestart) self.menu.addSeparator() self.menu.addMenu(self.lmenu) + self.menu.addAction(self.tr('Active Name'), self.__showVenvName) self.menu.addSeparator() self.menu.addAction(self.tr("Configure..."), self.__configure) @@ -351,17 +375,24 @@ self.__blockTextProcessing = False self.queueText.connect(self.__concatenateText, Qt.QueuedConnection) + self.__project = project + if self.__project: + self.__project.projectOpened.connect(self.__projectOpened) + self.__project.projectClosed.connect(self.__projectClosed) + self.grabGesture(Qt.PinchGesture) - def __showLanguageMenu(self): + def __showStartMenu(self): """ - Private slot to prepare the language submenu. + Private slot to prepare the start submenu. """ self.lmenu.clear() - clientLanguages = self.dbs.getSupportedLanguages(shellOnly=True) - for language in sorted(clientLanguages): - act = self.lmenu.addAction(language) - act.setData(language) + venvManager = e5App().getObject("VirtualEnvManager") + for venvName in sorted(venvManager.getVirtualenvNames()): + self.lmenu.addAction(venvName) + if self.__project.isOpen(): + self.lmenu.addSeparator() + self.lmenu.addAction(self.tr("Project")) def __resizeLinenoMargin(self): """ @@ -579,14 +610,19 @@ self.inCommandExecution = False self.interruptCommandExecution = False - def __clientCapabilities(self, cap, clType): + def __clientCapabilities(self, cap, clType, venvName): """ Private slot to handle the reporting of the clients capabilities. - @param cap client capabilities (integer) - @param clType type of the debug client (string) + @param cap client capabilities + @type int + @param clType type of the debug client + @type str + @param venvName name of the virtual environment + @type str """ self.clientCapabilities = cap + self.__currentVenv = venvName if clType != self.clientType: self.clientType = clType self.__bindLexer(self.clientType) @@ -603,6 +639,9 @@ self.loadHistory(self.clientType) self.__history = self.__historyLists[self.clientType] self.__setHistoryIndex() + + self.virtualEnvironmentChanged.emit(venvName) + Preferences.setShell("LastVirtualEnvironment", venvName) def __setHistoryIndex(self, index=None): """ @@ -767,19 +806,25 @@ else: self.dbs.remoteBanner() - def __writeBanner(self, version, platform, dbgclient): + def __writeBanner(self, version, platform, dbgclient, venvName): """ Private method to write a banner with info from the debug client. - @param version interpreter version string (string) - @param platform platform of the remote interpreter (string) - @param dbgclient debug client variant used (string) + @param version interpreter version string + @type str + @param platform platform of the remote interpreter + @type str + @param dbgclient debug client variant used + @type str + @param venvName name of the virtual environment + @type str """ super(Shell, self).clear() if self.passive and not self.dbs.isConnected(): self.__write(self.tr('Passive Debug Mode')) self.__write(self.tr('\nNot connected')) else: + self.__currentVenv = venvName version = version.replace("#", self.tr("No.")) if platform != "" and dbgclient != "": self.__write( @@ -787,19 +832,21 @@ .format(version, platform, dbgclient)) else: self.__write(version) + if venvName: + self.__write("\n[{0}]".format(venvName)) + + self.virtualEnvironmentChanged.emit(venvName) + Preferences.setShell("LastVirtualEnvironment", venvName) self.__write('\n') self.__write(sys.ps1) def __writePrompt(self): """ - Private method to write the prompt. + Private method to write the prompt using a write queue. """ - self.__write(self.inContinue and sys.ps2 or sys.ps1) - # little trick to get the cursor position registered within QScintilla - self.SendScintilla(QsciScintilla.SCI_CHARLEFT) - self.SendScintilla(QsciScintilla.SCI_CHARRIGHT) - + self.queueText.emit(self.inContinue and sys.ps2 or sys.ps1) + def __clientStatement(self, more): """ Private method to handle the response from the debugger client. @@ -810,7 +857,7 @@ self.inContinue = more self.__writePrompt() self.inCommandExecution = False - + def __clientException(self, exceptionType, exceptionMessage, stackTrace): """ Private method to handle an exception of the client. @@ -934,6 +981,10 @@ self.__queuedText = '' self.__blockTextProcessing = False + + # little trick to get the cursor position registered within QScintilla + self.SendScintilla(QsciScintilla.SCI_CHARLEFT) + self.SendScintilla(QsciScintilla.SCI_CHARRIGHT) def __write(self, s): """ @@ -1710,36 +1761,24 @@ else: self.__setHistoryIndex(historyIndex) - if cmd.startswith('start '): + if cmd == 'start' or cmd.startswith('start '): if not self.passive: cmdList = cmd.split(None, 1) if len(cmdList) < 2: - self.dbs.startClient(False) # same as reset + self.dbs.startClient(False) # start default backend else: - clientLanguages = self.dbs.getSupportedLanguages( - shellOnly=True) - language = cmdList[1] - if language not in clientLanguages: - language = cmdList[1].capitalize() - if language not in clientLanguages: - language = "" - if language: - self.dbs.startClient(False, language) + venvName = cmdList[1] + if venvName == self.tr("Project"): + if self.__project.isOpen(): + self.dbs.startClient(False, forProject=True) + else: + self.dbs.startClient( + False, venvName=self.__currentVenv) + # same as reset else: - # language not supported or typo - self.__write( - self.tr( - 'Shell language "{0}" not supported.\n') - .format(cmdList[1])) - self.__clientStatement(False) - return - cmd = '' - elif cmd == 'languages': - s = '{0}\n'.format(', '.join(sorted( - self.dbs.getSupportedLanguages(shellOnly=True)))) - self.__write(s) - self.__clientStatement(False) - return + self.dbs.startClient(False, venvName=venvName) + self.__getBanner() + return elif cmd == 'clear': # Display the banner. self.__getBanner() @@ -1747,13 +1786,29 @@ return else: cmd = '' - elif cmd == 'reset': - self.dbs.startClient(False) + elif cmd in ['reset', 'restart']: + self.dbs.startClient(False, venvName=self.__currentVenv) if self.passive: return else: cmd = '' - elif cmd in ["quit", "quit()"] and self.__windowed: + elif cmd in ['envs', 'environments']: + venvs = e5App().getObject("VirtualEnvManager")\ + .getVirtualenvNames() + s = self.tr('Available Virtual Environments:\n{0}\n').format( + '\n'.join("- {0}".format(venv) for venv in sorted(venvs)) + ) + self.__write(s) + self.__clientStatement(False) + return + elif cmd == 'which': + s = self.tr("Current Virtual Environment: '{0}'\n").format( + self.__currentVenv) + self.__write(s) + self.__clientStatement(False) + return + elif cmd in ["quit", "quit()", "exit", "exit()"] and \ + self.__windowed: # call main window quit() self.vm.quit() return @@ -1775,6 +1830,15 @@ self.dbs.remoteRawInput(cmd) + def __showVenvName(self): + """ + Private method to show the name of the active virtual environment. + """ + s = "\n" + self.tr("Current Virtual Environment: '{0}'\n").format( + self.__currentVenv) + self.__write(s) + self.__clientStatement(False) + def __useHistory(self): """ Private method to display a command from the history. @@ -1883,18 +1947,18 @@ # Display the banner. self.__getBanner() - def __resetAndClear(self): + def doClearRestart(self): """ - Private slot to handle the 'reset and clear' context menu entry. + Public slot to handle the 'restart and clear' context menu entry. """ - self.__reset() + self.doRestart() self.clear() - def __reset(self): + def doRestart(self): """ - Private slot to handle the 'reset' context menu entry. + Public slot to handle the 'restart' context menu entry. """ - self.dbs.startClient(False) + self.dbs.startClient(False, venvName=self.__currentVenv) def __startDebugClient(self, action): """ @@ -1903,8 +1967,11 @@ @param action context menu action that was triggered (QAction) """ - language = action.data() - self.dbs.startClient(False, language) + venvName = action.text() + if venvName == self.tr("Project"): + self.dbs.startClient(False, forProject=True) + else: + self.dbs.startClient(False, venvName=venvName) self.__getBanner() def handlePreferencesChanged(self): @@ -2143,19 +2210,25 @@ if self.__lastSearch: self.searchNext(*self.__lastSearch) - def searchNext(self, txt, caseSensitive, wholeWord): + def searchNext(self, txt, caseSensitive, wholeWord, regexp): """ Public method to search the next occurrence of the given text. - @param txt text to search for (string) + @param txt text to search for + @type str @param caseSensitive flag indicating to perform a case sensitive - search (boolean) + search + @type bool @param wholeWord flag indicating to search for whole words - only (boolean) + only + @type bool + @param regexp flag indicating a regular expression search + @type bool """ - self.__lastSearch = (txt, caseSensitive, wholeWord) + self.__lastSearch = (txt, caseSensitive, wholeWord, regexp) ok = self.findFirst( - txt, False, caseSensitive, wholeWord, True, forward=True) + txt, regexp, caseSensitive, wholeWord, True, forward=True, + posix=regexp) self.searchStringFound.emit(ok) def __searchPrev(self): @@ -2165,24 +2238,29 @@ if self.__lastSearch: self.searchPrev(*self.__lastSearch) - def searchPrev(self, txt, caseSensitive, wholeWord): + def searchPrev(self, txt, caseSensitive, wholeWord, regexp): """ Public method to search the previous occurrence of the given text. - @param txt text to search for (string) + @param txt text to search for + @type str @param caseSensitive flag indicating to perform a case sensitive - search (boolean) + search + @type bool @param wholeWord flag indicating to search for whole words - only (boolean) + only + @type bool + @param regexp flag indicating a regular expression search + @type bool """ - self.__lastSearch = (txt, caseSensitive, wholeWord) + self.__lastSearch = (txt, caseSensitive, wholeWord, regexp) if self.hasSelectedText(): line, index = self.getSelection()[:2] else: line, index = -1, -1 ok = self.findFirst( - txt, False, caseSensitive, wholeWord, True, - forward=False, line=line, index=index) + txt, regexp, caseSensitive, wholeWord, True, + forward=False, line=line, index=index, posix=regexp) self.searchStringFound.emit(ok) def historyStyle(self): @@ -2202,3 +2280,23 @@ @rtype bool """ return self.__historyStyle != ShellHistoryStyle.Disabled + + ################################################################# + ## Project Support + ################################################################# + + def __projectOpened(self): + """ + Private slot to start the shell for the opened project. + """ + if Preferences.getProject("RestartShellForProject"): + self.dbs.startClient(False, forProject=True) + self.__getBanner() + + def __projectClosed(self): + """ + Private slot to restart the default shell when the project is closed. + """ + if Preferences.getProject("RestartShellForProject"): + self.dbs.startClient(False) + self.__getBanner()