Browse Source

init: Change the history file according to the current mapset for Bash and GUI (#930)

Each new prompt checks the current mapset and when it is different, it write history (HISTFILE) to the old one, clears history list, and read history from the file in the new mapset. This is only for Bash.

There no need to warn about history for Bash in g.mapset anymore.

This also reloads history on change of current mapset in Console which at this point uses .bash_history too. Given the implementation of GConsoleWindow, even when the prompt/interactive console is hidden, giface needs to be passed, so the change needs to be present everywhere when GConsoleWindow is used. Direct usages of GPromptSTC could avoid the updates if history is not needed, but that's not implemented.

The old behavior was always there (with warning in g.mapset), but it became more important with always starting in a mapset as opposed to startup screen.
Vaclav Petras 4 năm trước cách đây
mục cha
commit
2d24a42cd4

+ 11 - 10
general/g.mapset/main.c

@@ -202,16 +202,17 @@ int main(int argc, char *argv[])
 
     G_free(mapset_old_path);
 
-    G_important_message(_("Mapset switched. Your shell continues to use the history "
-			  "for the old mapset"));
-
-    if ((shell = getenv("SHELL"))) {
-	if (strstr(shell, "bash")) {
-	    G_important_message(_("You can switch the history by commands:\n"
-				  "history -w; history -r %s/.bash_history; HISTFILE=%s/.bash_history"),
-				mapset_new_path, mapset_new_path);
-	}
-	else if (strstr(shell, "tcsh")) {
+    shell = getenv("SHELL");
+    /* For bash, we support switching of history, others not (yet). */
+    if (shell && strstr(shell, "bash")) {
+	G_important_message(_("Mapset switched."));
+    }
+    else {
+	G_important_message(_("Mapset switched. Your shell continues "
+			      "to use the history for the old mapset"));
+    }
+    if (shell) {
+	if (strstr(shell, "tcsh")) {
 	    G_important_message(_("You can switch the history by commands:\n"
 				  "history -S; history -L %s/.history; setenv histfile=%s/.history"),
 				mapset_new_path, mapset_new_path);

+ 2 - 2
gui/wxpython/gmodeler/dialogs.py

@@ -162,7 +162,7 @@ class ModelDataDialog(SimpleDialog):
 
 class ModelSearchDialog(wx.Dialog):
 
-    def __init__(self, parent, title=_("Add GRASS command to the model"),
+    def __init__(self, parent, giface, title=_("Add GRASS command to the model"),
                  style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
         """Graphical modeler module search window
 
@@ -199,7 +199,7 @@ class ModelSearchDialog(wx.Dialog):
         menuModel = LayerManagerMenuData()
 
         self.cmd_prompt = GPromptSTC(
-            parent=self, menuModel=menuModel.GetModel())
+            parent=self, giface=giface, menuModel=menuModel.GetModel())
         self.cmd_prompt.promptRunCmd.connect(self.OnCommand)
         self.cmd_prompt.commandSelected.connect(
             lambda command: self.label.SetValue(command))

+ 4 - 2
gui/wxpython/gmodeler/frame.py

@@ -140,7 +140,9 @@ class ModelFrame(wx.Frame):
         self.pythonPanel = PythonPanel(parent=self)
 
         self._gconsole = GConsole(guiparent=self)
-        self.goutput = GConsoleWindow(parent=self, gconsole=self._gconsole)
+        self.goutput = GConsoleWindow(
+            parent=self, giface=giface, gconsole=self._gconsole
+        )
         self.goutput.showNotification.connect(
             lambda message: self.SetStatusText(message))
 
@@ -794,7 +796,7 @@ class ModelFrame(wx.Frame):
     def OnAddAction(self, event):
         """Add action to model"""
         if self.searchDialog is None:
-            self.searchDialog = ModelSearchDialog(self)
+            self.searchDialog = ModelSearchDialog(parent=self, giface=self._giface)
             self.searchDialog.CentreOnParent()
         else:
             self.searchDialog.Reset()

+ 1 - 0
gui/wxpython/gui_core/forms.py

@@ -2286,6 +2286,7 @@ class CmdPanel(wx.Panel):
                 guiparent=self.notebook, giface=self._giface)
             self.goutput = GConsoleWindow(
                 parent=self.notebook,
+                giface=self._giface,
                 gconsole=self._gconsole,
                 margin=False)
             self._gconsole.Bind(

+ 4 - 2
gui/wxpython/gui_core/goutput.py

@@ -53,7 +53,7 @@ class GConsoleWindow(wx.SplitterWindow):
     """Create and manage output console for commands run by GUI.
     """
 
-    def __init__(self, parent, gconsole, menuModel=None, margin=False,
+    def __init__(self, parent, giface, gconsole, menuModel=None, margin=False,
                  style=wx.TAB_TRAVERSAL | wx.FULL_REPAINT_ON_RESIZE,
                  gcstyle=GC_EMPTY,
                  **kwargs):
@@ -116,7 +116,9 @@ class GConsoleWindow(wx.SplitterWindow):
         # search & command prompt
         # move to the if below
         # search depends on cmd prompt
-        self.cmdPrompt = GPromptSTC(parent=self, menuModel=self._menuModel)
+        self.cmdPrompt = GPromptSTC(
+            parent=self, giface=giface, menuModel=self._menuModel
+        )
         self.cmdPrompt.promptRunCmd.connect(lambda cmd:
                                             self._gconsole.RunCmd(command=cmd))
         self.cmdPrompt.showNotification.connect(self.showNotification)

+ 10 - 5
gui/wxpython/gui_core/prompt.py

@@ -44,7 +44,7 @@ class GPrompt(object):
     See subclass GPromptPopUp and GPromptSTC.
     """
 
-    def __init__(self, parent, menuModel):
+    def __init__(self, parent, giface, menuModel):
         self.parent = parent                 # GConsole
         self.panel = self.parent.GetPanel()
 
@@ -63,8 +63,8 @@ class GPrompt(object):
         # command description (gtask.grassTask)
         self.cmdDesc = None
 
-        self.cmdbuffer = self._readHistory()
-        self.cmdindex = len(self.cmdbuffer)
+        self._loadHistory()
+        giface.currentMapsetChanged.connect(self._loadHistory)
 
         # list of traced commands
         self.commands = list()
@@ -94,6 +94,11 @@ class GPrompt(object):
 
         return hist
 
+    def _loadHistory(self):
+        """Load history from a history file to data structures"""
+        self.cmdbuffer = self._readHistory()
+        self.cmdindex = len(self.cmdbuffer)
+
     def _getListOfMaps(self):
         """Get list of maps"""
         result = dict()
@@ -134,8 +139,8 @@ class GPrompt(object):
 class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
     """Styled wxGUI prompt with autocomplete and calltips"""
 
-    def __init__(self, parent, menuModel, margin=False):
-        GPrompt.__init__(self, parent=parent, menuModel=menuModel)
+    def __init__(self, parent, giface, menuModel, margin=False):
+        GPrompt.__init__(self, parent=parent, giface=giface, menuModel=menuModel)
         wx.stc.StyledTextCtrl.__init__(self, self.panel, id=wx.ID_ANY)
 
         #

+ 1 - 0
gui/wxpython/lmgr/frame.py

@@ -364,6 +364,7 @@ class GMFrame(wx.Frame):
             '^cd$|^cd .*')
         self.goutput = GConsoleWindow(
             parent=self.notebook,
+            giface=self._giface,
             gconsole=self._gconsole,
             menuModel=self._moduleTreeBuilder.GetModel(),
             gcstyle=GC_PROMPT)

+ 3 - 2
gui/wxpython/vnet/dialogs.py

@@ -357,8 +357,9 @@ class VNETDialog(wx.Dialog):
                               name='output')
 
         goutput = self.vnet_mgr.goutput  # TODO make interface
-        self.gwindow = GConsoleWindow(parent=outputPanel, gconsole=goutput)
-
+        self.gwindow = GConsoleWindow(
+            parent=outputPanel, giface=self.giface, gconsole=goutput
+        )
         # Layout
         outputSizer = wx.BoxSizer(wx.VERTICAL)
         outputSizer.Add(self.gwindow, proportion=1, flag=wx.EXPAND)

+ 21 - 5
lib/init/grass.py

@@ -1834,7 +1834,9 @@ def sh_like_startup(location, location_name, grass_env_file, sh):
             'Only bash-like and zsh shells are supported by sh_like_startup()')
 
     # save command history in mapset dir and remember more
-    os.environ['HISTFILE'] = os.path.join(location, sh_history)
+    # bash histroy file handled in specific_addition
+    if not sh == "bash":
+        os.environ['HISTFILE'] = os.path.join(location, sh_history)
     if not os.getenv('HISTSIZE') and not os.getenv('HISTFILESIZE'):
         os.environ['HISTSIZE'] = "3000"
 
@@ -1876,13 +1878,27 @@ def sh_like_startup(location, location_name, grass_env_file, sh):
     mask2d_test = 'test -f "$MAPSET_PATH/cell/MASK"'
     mask3d_test = 'test -d "$MAPSET_PATH/grid3/RASTER3D_MASK"'
 
-    zsh_addition = ""
+    specific_addition = ""
     if sh == 'zsh':
-        zsh_addition = """
+        specific_addition = """
     local z_lo=`g.gisenv get=LOCATION_NAME`
     local z_ms=`g.gisenv get=MAPSET`
     ZLOC="Mapset <$z_ms> in <$z_lo>"
     """
+    elif sh == "bash":
+        # Append existing history to file ("flush").
+        # Clear the (in-memory) history.
+        # Change the file.
+        # Read history from that file.
+        specific_addition = """
+    if [ "$_grass_old_mapset" != "$MAPSET_PATH" ] ; then
+        history -a
+        history -c
+        HISTFILE="$MAPSET_PATH/{sh_history}"
+        history -r
+        _grass_old_mapset="$MAPSET_PATH"
+    fi
+    """.format(sh_history=sh_history)
 
     # double curly brackets means single one for format function
     # setting LOCATION for backwards compatibility
@@ -1890,7 +1906,7 @@ def sh_like_startup(location, location_name, grass_env_file, sh):
         """grass_prompt() {{
     MAPSET_PATH="`g.gisenv get=GISDBASE,LOCATION_NAME,MAPSET separator='/'`"
     _GRASS_DB_PLACE="`g.gisenv get=LOCATION_NAME,MAPSET separator='/'`"
-    {zsh_addition}
+    {specific_addition}
     if {mask2d_test} && {mask3d_test} ; then
         echo "[{both_masks}]"
     elif {mask2d_test} ; then
@@ -1904,7 +1920,7 @@ PROMPT_COMMAND=grass_prompt\n""".format(
             mask2d=_("Raster MASK present"),
             mask3d=_("3D raster MASK present"),
             mask2d_test=mask2d_test, mask3d_test=mask3d_test,
-            zsh_addition=zsh_addition
+            specific_addition=specific_addition
             ))
 
     if sh == 'zsh':