Finished improving the Shell window history handling.

Mon, 10 Jul 2017 18:24:35 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Mon, 10 Jul 2017 18:24:35 +0200
changeset 5799
e87f52c0374a
parent 5798
e4f9552f7f93
child 5800
c3379bf35654

Finished improving the Shell window history handling.

Preferences/ConfigurationPages/ShellPage.py file | annotate | diff | comparison | revisions
Preferences/ConfigurationPages/ShellPage.ui file | annotate | diff | comparison | revisions
Preferences/__init__.py file | annotate | diff | comparison | revisions
QScintilla/Shell.py file | annotate | diff | comparison | revisions
QScintilla/ShellHistoryDialog.py file | annotate | diff | comparison | revisions
QScintilla/ShellWindow.py file | annotate | diff | comparison | revisions
--- a/Preferences/ConfigurationPages/ShellPage.py	Sun Jul 09 19:44:33 2017 +0200
+++ b/Preferences/ConfigurationPages/ShellPage.py	Mon Jul 10 18:24:35 2017 +0200
@@ -60,6 +60,10 @@
         index = self.shellHistoryStyleComboBox.findData(
             Preferences.getShell("HistoryStyle"))
         self.shellHistoryStyleComboBox.setCurrentIndex(index)
+        self.shellHistoryWrapCheckBox.setChecked(
+            Preferences.getShell("HistoryWrap"))
+        self.shellHistoryCursorKeysCheckBox.setChecked(
+            Preferences.getShell("HistoryNavigateByCursor"))
         self.stdOutErrCheckBox.setChecked(
             Preferences.getShell("ShowStdOutErr"))
         
@@ -96,6 +100,12 @@
             "HistoryStyle",
             self.shellHistoryStyleComboBox.currentData())
         Preferences.setShell(
+            "HistoryWrap",
+            self.shellHistoryWrapCheckBox.isChecked())
+        Preferences.setShell(
+            "HistoryNavigateByCursor",
+            self.shellHistoryCursorKeysCheckBox.isChecked())
+        Preferences.setShell(
             "ShowStdOutErr",
             self.stdOutErrCheckBox.isChecked())
         
--- a/Preferences/ConfigurationPages/ShellPage.ui	Sun Jul 09 19:44:33 2017 +0200
+++ b/Preferences/ConfigurationPages/ShellPage.ui	Mon Jul 10 18:24:35 2017 +0200
@@ -150,6 +150,30 @@
         </property>
        </widget>
       </item>
+      <item row="2" column="0" colspan="3">
+       <widget class="QCheckBox" name="shellHistoryWrapCheckBox">
+        <property name="toolTip">
+         <string>Select to wrap around while navigating through the history</string>
+        </property>
+        <property name="text">
+         <string>Wrap around while navigating</string>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="0" colspan="3">
+       <widget class="QCheckBox" name="shellHistoryCursorKeysCheckBox">
+        <property name="toolTip">
+         <string>Select to make Up- and Down-keys move in history</string>
+        </property>
+        <property name="whatsThis">
+         <string>&lt;b&gt;Up/Down keys navigate in history&lt;b&gt;
+&lt;p&gt;Select this entry to make Up- and Down-keys navigate in history. If unselected history navigation may be performed by Ctrl-Up or Ctrl-Down.&lt;/p&gt;</string>
+        </property>
+        <property name="text">
+         <string>Up/Down keys navigate in history</string>
+        </property>
+       </widget>
+      </item>
      </layout>
     </widget>
    </item>
@@ -263,6 +287,8 @@
   <tabstop>shellCTEnabledCheckBox</tabstop>
   <tabstop>shellHistorySpinBox</tabstop>
   <tabstop>shellHistoryStyleComboBox</tabstop>
+  <tabstop>shellHistoryWrapCheckBox</tabstop>
+  <tabstop>shellHistoryCursorKeysCheckBox</tabstop>
   <tabstop>monospacedFontButton</tabstop>
   <tabstop>monospacedCheckBox</tabstop>
   <tabstop>linenumbersFontButton</tabstop>
--- a/Preferences/__init__.py	Sun Jul 09 19:44:33 2017 +0200
+++ b/Preferences/__init__.py	Mon Jul 10 18:24:35 2017 +0200
@@ -1228,6 +1228,8 @@
         "WrapEnabled": True,
         "MaxHistoryEntries": 100,
         "HistoryStyle": ShellHistoryStyle.LinuxStyle,
+        "HistoryWrap": False,
+        "HistoryNavigateByCursor": False,
         "SyntaxHighlightingEnabled": True,
         "ShowStdOutErr": True,
         "UseMonospacedFont": False,
--- a/QScintilla/Shell.py	Sun Jul 09 19:44:33 2017 +0200
+++ b/QScintilla/Shell.py	Mon Jul 10 18:24:35 2017 +0200
@@ -106,11 +106,11 @@
     
     @signal searchStringFound(bool) emitted to indicate the search
         result
-    @signal historyStyleChanged(int) emitted to indicate a change of
-        the history style
+    @signal historyStyleChanged(ShellHistoryStyle) emitted to indicate a
+        change of the history style
     """
     searchStringFound = pyqtSignal(bool)
-    historyStyleChanged = pyqtSignal(int)
+    historyStyleChanged = pyqtSignal(ShellHistoryStyle)
     
     def __init__(self, dbs, vm, windowedVariant, parent=None):
         """
@@ -220,18 +220,19 @@
         self.lexer_ = None
         self.completionText = ""
         
+        self.clientType = ''
+        
         # Initialize history
         self.__historyLists = {}
         self.__maxHistoryEntries = Preferences.getShell("MaxHistoryEntries")
         self.__historyStyle = Preferences.getShell("HistoryStyle")
+        self.__historyWrap = Preferences.getShell("HistoryWrap")
         self.__history = []
         self.__setHistoryIndex()
         # remove obsolete shell histories (Python and Ruby)
         for clientType in ["Python", "Ruby"]:
             Preferences.Prefs.settings.remove("Shell/Histories/" + clientType)
         
-        self.clientType = ''
-        
         # clear QScintilla defined keyboard commands
         # we do our own handling through the view manager
         self.clearAlternateKeys()
@@ -316,10 +317,6 @@
             QsciScintilla.SCI_WORDRIGHT: self.__QScintillaWordRight,
             QsciScintilla.SCI_VCHOME: self.__QScintillaVCHome,
             QsciScintilla.SCI_LINEEND: self.__QScintillaLineEnd,
-            QsciScintilla.SCI_LINEUP: self.__QScintillaCommand,
-            QsciScintilla.SCI_LINEDOWN: self.__QScintillaCommand,
-            QsciScintilla.SCI_LINESCROLLUP: self.__QScintillaHistoryUp,
-            QsciScintilla.SCI_LINESCROLLDOWN: self.__QScintillaHistoryDown,
             
             QsciScintilla.SCI_PAGEUP: self.__QScintillaAutoCompletionCommand,
             QsciScintilla.SCI_PAGEDOWN: self.__QScintillaAutoCompletionCommand,
@@ -334,9 +331,29 @@
             
             QsciScintilla.SCI_CANCEL: self.__QScintillaCancel,
         }
+        self.__setupCursorKeys()
         
         self.grabGesture(Qt.PinchGesture)
-        
+    
+    def __setupCursorKeys(self):
+        """
+        Private method to setup the cursor up and down mode.
+        """
+        if Preferences.getShell("HistoryNavigateByCursor"):
+            self.supportedEditorCommands.update({
+                QsciScintilla.SCI_LINEUP: self.__QScintillaHistoryUp,
+                QsciScintilla.SCI_LINEDOWN: self.__QScintillaHistoryDown,
+                QsciScintilla.SCI_LINESCROLLUP: self.__QScintillaLineUp,
+                QsciScintilla.SCI_LINESCROLLDOWN: self.__QScintillaLineDown,
+            })
+        else:
+            self.supportedEditorCommands.update({
+                QsciScintilla.SCI_LINEUP: self.__QScintillaLineUp,
+                QsciScintilla.SCI_LINEDOWN: self.__QScintillaLineDown,
+                QsciScintilla.SCI_LINESCROLLUP: self.__QScintillaHistoryUp,
+                QsciScintilla.SCI_LINESCROLLDOWN: self.__QScintillaHistoryDown,
+            })
+    
     def __showLanguageMenu(self):
         """
         Private slot to prepare the language submenu.
@@ -594,9 +611,12 @@
         """
         if index is None:
             # determine based on history style
-            if self.__historyStyle == ShellHistoryStyle.WindowsStyle:
+            if self.clientType and \
+                    self.__historyStyle == ShellHistoryStyle.WindowsStyle:
                 idx = int(Preferences.Prefs.settings.value(
                     "Shell/HistoryIndexes/" + self.clientType, -1))
+                if idx >= len(self.__history):
+                    idx = -1
                 self.__histidx = idx
             else:
                 self.__histidx = -1
@@ -618,6 +638,15 @@
         """
         return (0 <= self.__histidx < len(self.__history))
     
+    def getHistoryIndex(self):
+        """
+        Public method to get the current value of the history index.
+        
+        @return history index
+        @rtype int
+        """
+        return self.__histidx
+    
     def loadHistory(self, clientType):
         """
         Public method to load the history for the given client type.
@@ -963,12 +992,15 @@
         lines = QApplication.clipboard().text(QClipboard.Selection)
         self.executeLines(lines)
         
-    def executeLines(self, lines):
+    def executeLines(self, lines, historyIndex=None):
         """
         Public method to execute a set of lines as multiple commands.
         
-        @param lines multiple lines of text to be executed as single
-            commands (string)
+        @param lines multiple lines of text to be executed as
+            single commands
+        @type str
+        @param historyIndex history index to be set
+        @type int
         """
         for line in lines.splitlines(True):
             if line.endswith("\r\n"):
@@ -989,7 +1021,7 @@
                 elif cmd.startswith(sys.ps2):
                     cmd = cmd[len(sys.ps2):]
                 
-                self.__executeCommand(cmd)
+                self.__executeCommand(cmd, historyIndex=historyIndex)
                 if self.interruptCommandExecution:
                     self.__executeCommand("")
                     break
@@ -1394,10 +1426,26 @@
             self.SendScintilla(cmd)
         elif self.__isCursorOnLastLine():
             self.moveCursorToEOL()
+    
+    def __QScintillaLineUp(self, cmd):
+        """
+        Private method to handle the cursor up command.
         
+        @param cmd QScintilla command
+        """
+        self.SendScintilla(QsciScintilla.SCI_LINEUP)
+    
+    def __QScintillaLineDown(self, cmd):
+        """
+        Private method to handle the cursor down command.
+        
+        @param cmd QScintilla command
+        """
+        self.SendScintilla(QsciScintilla.SCI_LINEDOWN)
+    
     def __QScintillaHistoryUp(self, cmd):
         """
-        Private method to handle the Ctrl+Up key.
+        Private method to handle the history up command.
         
         @param cmd QScintilla command
         """
@@ -1423,16 +1471,24 @@
                         self.incrementalSearchString = buf
                         self.__useHistory()
             else:
-                if self.__histidx < 0:
-                    # wrap around
-                    self.__setHistoryIndex(index=len(self.__history) - 1)
+                if self.__historyWrap:
+                    if self.__histidx < 0:
+                        # wrap around
+                        self.__setHistoryIndex(index=len(self.__history) - 1)
+                    else:
+                        self.__setHistoryIndex(index=self.__histidx - 1)
+                    self.__useHistory()
                 else:
-                    self.__setHistoryIndex(index=self.__histidx - 1)
-                self.__useHistory()
+                    if self.__histidx < 0:
+                        self.__setHistoryIndex(index=len(self.__history) - 1)
+                        self.__useHistory()
+                    elif self.__histidx > 0:
+                        self.__setHistoryIndex(index=self.__histidx - 1)
+                        self.__useHistory()
     
     def __QScintillaHistoryDown(self, cmd):
         """
-        Private method to handle the Ctrl+Down key.
+        Private method to handle the history down command.
         
         @param cmd QScintilla command
         """
@@ -1458,12 +1514,17 @@
                         self.incrementalSearchString = buf
                         self.__useHistory()
             else:
-                if self.__histidx >= len(self.__history) - 1:
-                    # wrap around
-                    self.__setHistoryIndex(index=0)
+                if self.__historyWrap:
+                    if self.__histidx >= len(self.__history) - 1:
+                        # wrap around
+                        self.__setHistoryIndex(index=0)
+                    else:
+                        self.__setHistoryIndex(index=self.__histidx + 1)
+                    self.__useHistory()
                 else:
-                    self.__setHistoryIndex(index=self.__histidx + 1)
-                self.__useHistory()
+                    if self.__isHistoryIndexValid():
+                        self.__setHistoryIndex(index=self.__histidx + 1)
+                        self.__useHistory()
     
     def __QScintillaCancel(self):
         """
@@ -1511,11 +1572,14 @@
         if self.isListActive() or self.isCallTipActive():
             self.SendScintilla(cmd)
         
-    def __executeCommand(self, cmd):
+    def __executeCommand(self, cmd, historyIndex=None):
         """
         Private slot to execute a command.
         
-        @param cmd command to be executed by debug client (string)
+        @param cmd command to be executed by debug client
+        @type str
+        @param historyIndex history index to be set
+        @type int
         """
         if not self.inRawMode:
             self.inCommandExecution = True
@@ -1523,16 +1587,23 @@
             if not cmd:
                 # make sure cmd is a string
                 cmd = ''
-            # TODO: change according to history style
-            #       Linux - append (i.e. keep as is)
-            #       Windows - modify current entry if index not at end, add otherwise
+            
+            # History Handling
             if self.isHistoryEnabled():
                 if cmd != "" and (
                         len(self.__history) == 0 or self.__history[-1] != cmd):
                     if len(self.__history) == self.__maxHistoryEntries:
                         del self.__history[0]
                     self.__history.append(cmd)
-                self.__histidx = -1
+                if self.__historyStyle == ShellHistoryStyle.LinuxStyle:
+                    self.__setHistoryIndex(index=-1)
+                elif self.__historyStyle == ShellHistoryStyle.WindowsStyle:
+                    if historyIndex is None:
+                        if cmd != self.__history[self.__histidx - 1]:
+                            self.__setHistoryIndex(index=-1)
+                    else:
+                        self.__setHistoryIndex(historyIndex)
+            
             if cmd.startswith('start '):
                 if not self.passive:
                     cmdList = cmd.split(None, 1)
@@ -1751,10 +1822,12 @@
             self.__historyLists[key] = \
                 self.__historyLists[key][-self.__maxHistoryEntries:]
         self.__historyStyle = Preferences.getShell("HistoryStyle")
+        self.__historyWrap = Preferences.getShell("HistoryWrap")
         self.__setHistoryIndex()
-        self.historyStyleChanged.emit(self.__historyStyle)
         if not self.__windowed:
             self.hmenu.menuAction().setEnabled(self.isHistoryEnabled())
+        self.__setupCursorKeys()
+        self.historyStyleChanged.emit(self.__historyStyle)
         
         # do stdout /stderr stuff
         showStdOutErr = Preferences.getShell("ShowStdOutErr")
--- a/QScintilla/ShellHistoryDialog.py	Sun Jul 09 19:44:33 2017 +0200
+++ b/QScintilla/ShellHistoryDialog.py	Mon Jul 10 18:24:35 2017 +0200
@@ -11,7 +11,7 @@
 
 import os
 
-from PyQt5.QtCore import pyqtSlot, QItemSelectionModel
+from PyQt5.QtCore import pyqtSlot, Qt, QItemSelectionModel
 from PyQt5.QtWidgets import QListWidgetItem, QDialog
 
 from .Ui_ShellHistoryDialog import Ui_ShellHistoryDialog
@@ -25,20 +25,27 @@
         """
         Constructor
         
-        @param history reference to the current shell history (list of strings)
+        @param history reference to the current shell history
+        @type list of str
         @param vm reference to the viewmanager object
+        @type ViewManager
         @param shell reference to the shell object
+        @type Shell
         """
         super(ShellHistoryDialog, self).__init__(shell)
         self.setupUi(self)
         
+        self.__vm = vm
+        self.__shell = shell
+        
         self.historyList.addItems(history)
-        self.historyList.setCurrentRow(
-            self.historyList.count() - 1, QItemSelectionModel.Clear)
+        index = shell.getHistoryIndex()
+        if index < 0 or index >= len(history):
+            self.historyList.setCurrentRow(
+                self.historyList.count() - 1, QItemSelectionModel.Select)
+        else:
+            self.historyList.setCurrentRow(index, QItemSelectionModel.Select)
         self.historyList.scrollToItem(self.historyList.currentItem())
-        
-        self.vm = vm
-        self.shell = shell
     
     @pyqtSlot()
     def on_historyList_itemSelectionChanged(self):
@@ -47,7 +54,7 @@
         """
         selected = len(self.historyList.selectedItems()) > 0
         self.copyButton.setEnabled(selected and
-                                   self.vm.activeWindow() is not None)
+                                   self.__vm.activeWindow() is not None)
         self.deleteButton.setEnabled(selected)
         self.executeButton.setEnabled(selected)
     
@@ -77,7 +84,7 @@
         """
         Private slot to copy the selected entries to the current editor.
         """
-        aw = self.vm.activeWindow()
+        aw = self.__vm.activeWindow()
         if aw is not None:
             lines = []
             for index in range(self.historyList.count()):
@@ -102,7 +109,9 @@
             if itm.isSelected():
                 lines.append(itm.text())
         cmds = os.linesep.join(lines) + os.linesep
-        self.shell.executeLines(cmds)
+        self.__shell.executeLines(
+            cmds,
+            historyIndex=self.historyList.currentRow())
         
         # reload the list because shell modified it
         self.on_reloadButton_clicked()
@@ -112,13 +121,20 @@
         """
         Private slot to reload the history.
         """
-        history = self.shell.getHistory(None)
+        history = self.__shell.getHistory(None)
+        index = self.__shell.getHistoryIndex()
+        
         self.historyList.clear()
         self.historyList.addItems(history)
-        self.historyList.setCurrentRow(
-            self.historyList.count() - 1, QItemSelectionModel.Clear)
+        if index < 0 or index >= len(history):
+            self.historyList.setCurrentRow(
+                self.historyList.count() - 1, QItemSelectionModel.Select)
+        else:
+            self.historyList.setCurrentRow(index, QItemSelectionModel.Select)
         self.historyList.scrollToItem(self.historyList.currentItem())
         
+        self.historyList.setFocus(Qt.OtherFocusReason)
+        
     def getHistory(self):
         """
         Public method to retrieve the history from the dialog.
--- a/QScintilla/ShellWindow.py	Sun Jul 09 19:44:33 2017 +0200
+++ b/QScintilla/ShellWindow.py	Mon Jul 10 18:24:35 2017 +0200
@@ -40,7 +40,6 @@
 from eric6config import getConfig
 
 
-# TODO: implement history handling changes (Shell.historyStyleChanged)
 class ShellWindow(E5MainWindow):
     """
     Class implementing a stand alone shell window.
@@ -93,6 +92,8 @@
         
         self.__readSettings()
         
+        self.__shell.historyStyleChanged.connect(self.__historyStyleChanged)
+        
         # now start the debug client
         self.__debugServer.startClient(False)
         
@@ -1133,6 +1134,7 @@
         self.__historyMenu.addAction(self.selectHistoryAct)
         self.__historyMenu.addAction(self.showHistoryAct)
         self.__historyMenu.addAction(self.clearHistoryAct)
+        self.__historyMenu.setEnabled(self.__shell.isHistoryEnabled())
         
         self.__startMenu = self.menuBar().addMenu(self.tr("&Start"))
         self.__startMenu.aboutToShow.connect(self.__showLanguageMenu)
@@ -1204,10 +1206,11 @@
         viewtb.addAction(self.zoomResetAct)
         viewtb.addAction(self.zoomToAct)
         
-        historytb = self.addToolBar(self.tr("History"))
-        historytb.setIconSize(UI.Config.ToolBarIconSize)
-        historytb.addAction(self.showHistoryAct)
-        historytb.addAction(self.clearHistoryAct)
+        self.__historyToolbar = self.addToolBar(self.tr("History"))
+        self.__historyToolbar.setIconSize(UI.Config.ToolBarIconSize)
+        self.__historyToolbar.addAction(self.showHistoryAct)
+        self.__historyToolbar.addAction(self.clearHistoryAct)
+        self.__historyToolbar.setEnabled(self.__shell.isHistoryEnabled())
         
         helptb = self.addToolBar(self.tr("Help"))
         helptb.setIconSize(UI.Config.ToolBarIconSize)
@@ -1236,3 +1239,14 @@
         
         self.__sbZoom.valueChanged.connect(self.__zoomTo)
         self.__sbZoom.setValue(0)
+    
+    def __historyStyleChanged(self, historyStyle):
+        """
+        Private slot to handle a change of the shell history style.
+        
+        @param historyStyle style to be used for the history
+        @type ShellHistoryStyle
+        """
+        enabled = self.__shell.isHistoryEnabled()
+        self.__historyMenu.setEnabled(enabled)
+        self.__historyToolbar.setEnabled(enabled)

eric ide

mercurial