QScintilla/Shell.py

branch
maintenance
changeset 6602
331ac8f99cf8
parent 6487
d3ca83d691e7
parent 6581
8eb6220f2bb7
child 6646
51eefa621de4
--- 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()

eric ide

mercurial