浏览代码

feat(history-substring-search): update to upstream (#12164)

Tabrez Mohammed 1 年之前
父节点
当前提交
d93401c642

+ 70 - 10
plugins/history-substring-search/README.md

@@ -23,7 +23,15 @@ Install
 Using the [Homebrew]( https://brew.sh ) package manager:
 Using the [Homebrew]( https://brew.sh ) package manager:
 
 
     brew install zsh-history-substring-search
     brew install zsh-history-substring-search
-    echo 'source /usr/local/share/zsh-history-substring-search/zsh-history-substring-search.zsh' >> ~/.zshrc
+    echo 'source $(brew --prefix)/share/zsh-history-substring-search/zsh-history-substring-search.zsh' >> ~/.zshrc
+
+Using [Fig](https://fig.io):
+
+Fig adds apps, shortcuts, and autocomplete to your existing terminal.
+
+Install `zsh-history-substring-search` in just one click.
+
+<a href="https://fig.io/plugins/other/zsh-history-substring-search" target="_blank"><img src="https://fig.io/badges/install-with-fig.svg" /></a>
 
 
 Using [Oh-my-zsh](https://github.com/robbyrussell/oh-my-zsh):
 Using [Oh-my-zsh](https://github.com/robbyrussell/oh-my-zsh):
 
 
@@ -33,24 +41,63 @@ Using [Oh-my-zsh](https://github.com/robbyrussell/oh-my-zsh):
 
 
 2. Activate the plugin in `~/.zshrc`:
 2. Activate the plugin in `~/.zshrc`:
 
 
-        plugins=( [plugins...] history-substring-search)
+        plugins=( [plugins...] zsh-history-substring-search)
+
+3. Run `exec zsh`  to take changes into account:
+
+        exec zsh
+
+Using [zplug](https://github.com/zplug/zplug):
+
+1. Add this repo to `~/.zshrc`:
+
+        zplug "zsh-users/zsh-history-substring-search", as: plugin
+
+Using [antigen](https://github.com/zsh-users/antigen):
 
 
-3. Source `~/.zshrc`  to take changes into account:
+1. Add the `antigen bundle` command just before `antigen apply`, like this:
 
 
-        source ~/.zshrc
+``` 
+antigen bundle zsh-users/zsh-history-substring-search
+antigen apply
+```
+ 
+2. Then, **after** `antigen apply`, add the key binding configurations, like this:
+ 
+```
+# zsh-history-substring-search configuration
+bindkey '^[[A' history-substring-search-up # or '\eOA'
+bindkey '^[[B' history-substring-search-down # or '\eOB'
+HISTORY_SUBSTRING_SEARCH_ENSURE_UNIQUE=1
+```
+
+Using [Zinit](https://github.com/zdharma-continuum/zinit):
+
+1. Use the `Oh-my-zsh` Zinit snippet in `~/.zshrc`:
+
+        zinit snippet OMZ::plugins/git/git.plugin.zsh`
+
+2. Load the plugin in `~/.zshrc`:
+
+        zinit load 'zsh-users/zsh-history-substring-search
+        zinit ice wait atload'_history_substring_search_config'
+
+3. Run `exec zsh` to take changes into account:
+
+        exec zsh
 
 
 Usage
 Usage
 ------------------------------------------------------------------------------
 ------------------------------------------------------------------------------
 
 
 1.  Load this script into your interactive ZSH session:
 1.  Load this script into your interactive ZSH session:
 
 
-        % source zsh-history-substring-search.zsh
+        source zsh-history-substring-search.zsh
 
 
     If you want to use [zsh-syntax-highlighting][6] along with this script,
     If you want to use [zsh-syntax-highlighting][6] along with this script,
     then make sure that you load it *before* you load this script:
     then make sure that you load it *before* you load this script:
 
 
-        % source zsh-syntax-highlighting.zsh
-        % source zsh-history-substring-search.zsh
+        source zsh-syntax-highlighting.zsh
+        source zsh-history-substring-search.zsh
 
 
 2.  Bind keyboard shortcuts to this script's functions.
 2.  Bind keyboard shortcuts to this script's functions.
 
 
@@ -73,6 +120,10 @@ Usage
           bindkey "$terminfo[kcuu1]" history-substring-search-up
           bindkey "$terminfo[kcuu1]" history-substring-search-up
           bindkey "$terminfo[kcud1]" history-substring-search-down
           bindkey "$terminfo[kcud1]" history-substring-search-down
 
 
+      Users have also observed that `[OA` and `[OB` are correct values, 
+      _even if_ these were not the observed values. If you are having trouble
+      with the observed values, give these a try.
+
       You might also want to bind the Control-P/N keys for use in EMACS mode:
       You might also want to bind the Control-P/N keys for use in EMACS mode:
 
 
           bindkey -M emacs '^P' history-substring-search-up
           bindkey -M emacs '^P' history-substring-search-up
@@ -115,7 +166,7 @@ Configuration
 ------------------------------------------------------------------------------
 ------------------------------------------------------------------------------
 
 
 This script defines the following global variables. You may override their
 This script defines the following global variables. You may override their
-default values only after having loaded this script into your ZSH session.
+default values.
 
 
 * `HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND` is a global variable that defines
 * `HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND` is a global variable that defines
   how the query should be highlighted inside a matching command. Its default
   how the query should be highlighted inside a matching command. Its default
@@ -141,6 +192,12 @@ default values only after having loaded this script into your ZSH session.
   value, causes this script to perform a fuzzy search by words, matching in
   value, causes this script to perform a fuzzy search by words, matching in
   given order e.g. `ab c` will match `*ab*c*`
   given order e.g. `ab c` will match `*ab*c*`
 
 
+* `HISTORY_SUBSTRING_SEARCH_PREFIXED` is a global variable that defines how
+  the command history will be searched for your query. If set to a non-empty
+  value, your query will be matched against the start of each history entry.
+  For example, if this variable is empty, `ls` will match `ls -l` and `echo
+  ls`; if it is non-empty, `ls` will only match `ls -l`.
+
 * `HISTORY_SUBSTRING_SEARCH_ENSURE_UNIQUE` is a global variable that defines
 * `HISTORY_SUBSTRING_SEARCH_ENSURE_UNIQUE` is a global variable that defines
   whether all search results returned are _unique_. If set to a non-empty
   whether all search results returned are _unique_. If set to a non-empty
   value, then only unique search results are presented. This behaviour is off
   value, then only unique search results are presented. This behaviour is off
@@ -155,6 +212,9 @@ default values only after having loaded this script into your ZSH session.
   receive globally unique search results only once, then use this
   receive globally unique search results only once, then use this
   configuration variable, or use `setopt HIST_IGNORE_ALL_DUPS`.
   configuration variable, or use `setopt HIST_IGNORE_ALL_DUPS`.
 
 
+* `HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_TIMEOUT` is a global variable that
+  defines a timeout in seconds for clearing the search highlight.
+
 
 
 History
 History
 ------------------------------------------------------------------------------
 ------------------------------------------------------------------------------
@@ -187,8 +247,8 @@ https://github.com/zsh-users/zsh-history-substring-search.
 
 
 This downstream copy was last updated from the following upstream commit:
 This downstream copy was last updated from the following upstream commit:
 
 
-  SHA:          0f80b8eb3368b46e5e573c1d91ae69eb095db3fb
-  Commit date:  2019-05-12 17:35:54 -0700
+  SHA:          8dd05bfcc12b0cd1ee9ea64be725b3d9f713cf64
+  Commit date:  2023-11-23 12:12:14 +0200
 
 
 Everything above this section is a copy of the original upstream's README, so things
 Everything above this section is a copy of the original upstream's README, so things
 may differ slightly when you're using this inside OMZ. In particular, you do not
 may differ slightly when you're using this inside OMZ. In particular, you do not

+ 2 - 6
plugins/history-substring-search/history-substring-search.plugin.zsh

@@ -1,9 +1,5 @@
-# Handle $0 according to the standard:
-# https://zdharma-continuum.github.io/Zsh-100-Commits-Club/Zsh-Plugin-Standard.html
-0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}"
-0="${${(M)0:#/*}:-$PWD/$0}"
-
-source ${0:A:h}/history-substring-search.zsh
+0=${(%):-%N}
+source ${0:A:h}/zsh-history-substring-search.zsh
 
 
 
 
 # Bind terminal-specific up and down keys
 # Bind terminal-specific up and down keys

+ 165 - 78
plugins/history-substring-search/history-substring-search.zsh

@@ -43,11 +43,12 @@
 # declare global configuration variables
 # declare global configuration variables
 #-----------------------------------------------------------------------------
 #-----------------------------------------------------------------------------
 
 
-typeset -g HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND='bg=magenta,fg=white,bold'
-typeset -g HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND='bg=red,fg=white,bold'
-typeset -g HISTORY_SUBSTRING_SEARCH_GLOBBING_FLAGS='i'
-typeset -g HISTORY_SUBSTRING_SEARCH_ENSURE_UNIQUE=''
-typeset -g HISTORY_SUBSTRING_SEARCH_FUZZY=''
+: ${HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND='bg=magenta,fg=white,bold'}
+: ${HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND='bg=red,fg=white,bold'}
+: ${HISTORY_SUBSTRING_SEARCH_GLOBBING_FLAGS='i'}
+: ${HISTORY_SUBSTRING_SEARCH_ENSURE_UNIQUE=''}
+: ${HISTORY_SUBSTRING_SEARCH_FUZZY=''}
+: ${HISTORY_SUBSTRING_SEARCH_PREFIXED=''}
 
 
 #-----------------------------------------------------------------------------
 #-----------------------------------------------------------------------------
 # declare internal global variables
 # declare internal global variables
@@ -64,6 +65,7 @@ typeset -g -i _history_substring_search_raw_match_index
 typeset -g -a _history_substring_search_matches
 typeset -g -a _history_substring_search_matches
 typeset -g -i _history_substring_search_match_index
 typeset -g -i _history_substring_search_match_index
 typeset -g -A _history_substring_search_unique_filter
 typeset -g -A _history_substring_search_unique_filter
+typeset -g -i _history_substring_search_zsh_5_9
 
 
 #-----------------------------------------------------------------------------
 #-----------------------------------------------------------------------------
 # the main ZLE widgets
 # the main ZLE widgets
@@ -97,6 +99,11 @@ zle -N history-substring-search-down
 #-----------------------------------------------------------------------------
 #-----------------------------------------------------------------------------
 
 
 zmodload -F zsh/parameter
 zmodload -F zsh/parameter
+autoload -Uz is-at-least
+
+if is-at-least 5.9 $ZSH_VERSION; then
+  _history_substring_search_zsh_5_9=1
+fi
 
 
 #
 #
 # We have to "override" some keys and widgets if the
 # We have to "override" some keys and widgets if the
@@ -117,80 +124,125 @@ if [[ $+functions[_zsh_highlight] -eq 0 ]]; then
   }
   }
 
 
   #
   #
-  # The following snippet was taken from the zsh-syntax-highlighting project:
-  #
-  # https://github.com/zsh-users/zsh-syntax-highlighting/blob/56b134f5d62ae3d4e66c7f52bd0cc2595f9b305b/zsh-syntax-highlighting.zsh#L126-161
-  #
-  # Copyright (c) 2010-2011 zsh-syntax-highlighting contributors
-  # All rights reserved.
-  #
-  # Redistribution and use in source and binary forms, with or without
-  # modification, are permitted provided that the following conditions are
-  # met:
-  #
-  #  * Redistributions of source code must retain the above copyright
-  #    notice, this list of conditions and the following disclaimer.
-  #
-  #  * Redistributions in binary form must reproduce the above copyright
-  #    notice, this list of conditions and the following disclaimer in the
-  #    documentation and/or other materials provided with the distribution.
-  #
-  #  * Neither the name of the zsh-syntax-highlighting contributors nor the
-  #    names of its contributors may be used to endorse or promote products
-  #    derived from this software without specific prior written permission.
-  #
-  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-  # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-  # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-  # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
-  # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-  # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-  # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-  # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-  # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-  # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-  #
-  #--------------8<-------------------8<-------------------8<-----------------
-  # Rebind all ZLE widgets to make them invoke _zsh_highlights.
-  _zsh_highlight_bind_widgets()
-  {
-    # Load ZSH module zsh/zleparameter, needed to override user defined widgets.
-    zmodload zsh/zleparameter 2>/dev/null || {
-      echo 'zsh-syntax-highlighting: failed loading zsh/zleparameter.' >&2
-      return 1
-    }
-
-    # Override ZLE widgets to make them invoke _zsh_highlight.
-    local cur_widget
-    for cur_widget in ${${(f)"$(builtin zle -la)"}:#(.*|_*|orig-*|run-help|which-command|beep|yank*)}; do
-      case $widgets[$cur_widget] in
+  # Check if $1 denotes the name of a callable function, i.e. it is fully
+  # defined or it is marked for autoloading and autoloading it at the first
+  # call to it will succeed. In particular, if $1 has been marked for
+  # autoloading but is not available in $fpath, then it will return 1 (false).
+  #
+  # This is based on the zsh-syntax-highlighting plugin.
+  #
+  _history-substring-search-function-callable() {
+    if (( ${+functions[$1]} )) && ! [[ "$functions[$1]" == *"builtin autoload -X"* ]]; then
+      return 0 # already fully loaded
+    else
+      # "$1" is either an autoload stub, or not a function at all.
+      # We expect 'autoload +X' to return non-zero if it fails to fully load
+      # the function.
+      ( autoload -U +X -- "$1" 2>/dev/null )
+      return $?
+    fi
+  }
 
 
-        # Already rebound event: do nothing.
-        user:$cur_widget|user:_zsh_highlight_widget_*);;
+  #
+  # The zsh-syntax-highlighting plugin uses zle-line-pre-redraw hook instead
+  # of the legacy "bind all widgets" if 1) zsh has the memo= feature (added in
+  # version 5.9) and 2) add-zle-hook-widget is available.
+  #
+  if [[ $_history_substring_search_zsh_5_9 -eq 1 ]] && _history-substring-search-function-callable add-zle-hook-widget; then
+    #
+    # The following code is based on the zsh-syntax-highlighting plugin.
+    #
+    autoload -U add-zle-hook-widget
 
 
-        # User defined widget: override and rebind old one with prefix "orig-".
-        user:*) eval "zle -N orig-$cur_widget ${widgets[$cur_widget]#*:}; \
-                      _zsh_highlight_widget_$cur_widget() { builtin zle orig-$cur_widget -- \"\$@\" && _zsh_highlight }; \
-                      zle -N $cur_widget _zsh_highlight_widget_$cur_widget";;
+    _history-substring-search-zle-line-finish() {
+      #
+      # Reset $WIDGET since the 'main' highlighter depends on it.
+      #
+      # Since $WIDGET is declared by zle as read-only in this function's scope,
+      # a nested function is required in order to shadow its built-in value;
+      # see "User-defined widgets" in zshall.
+      #
+      () {
+        local -h -r WIDGET=zle-line-finish
+        _zsh_highlight
+      }
+    }
 
 
-        # Completion widget: override and rebind old one with prefix "orig-".
-        completion:*) eval "zle -C orig-$cur_widget ${${widgets[$cur_widget]#*:}/:/ }; \
-                            _zsh_highlight_widget_$cur_widget() { builtin zle orig-$cur_widget -- \"\$@\" && _zsh_highlight }; \
-                            zle -N $cur_widget _zsh_highlight_widget_$cur_widget";;
+    _history-substring-search-zle-line-pre-redraw() {
+      #
+      # If the zsh-syntax-highlighting plugin has been loaded (after our plugin
+      # plugin, otherwise this hook wouldn't be called), remove our hooks.
+      #
+      if [[ $+ZSH_HIGHLIGHT_VERSION -eq 1 ]]; then
+        autoload -U add-zle-hook-widget
+        add-zle-hook-widget -d zle-line-pre-redraw _history-substring-search-zle-line-pre-redraw
+        add-zle-hook-widget -d zle-line-finish _history-substring-search-zle-line-finish
+        return 0
+      fi
+      #
+      # Set $? to 0 for _zsh_highlight.  Without this, subsequent
+      # zle-line-pre-redraw hooks won't run, since add-zle-hook-widget happens to
+      # call us with $? == 1 in the common case.
+      #
+      true && _zsh_highlight "$@"
+    }
 
 
-        # Builtin widget: override and make it call the builtin ".widget".
-        builtin) eval "_zsh_highlight_widget_$cur_widget() { builtin zle .$cur_widget -- \"\$@\" && _zsh_highlight }; \
-                       zle -N $cur_widget _zsh_highlight_widget_$cur_widget";;
+    if [[ -o zle ]]; then
+      add-zle-hook-widget zle-line-pre-redraw _history-substring-search-zle-line-pre-redraw
+      add-zle-hook-widget zle-line-finish _history-substring-search-zle-line-finish
+    fi
+  else
+    #
+    # The following snippet was taken from the zsh-syntax-highlighting project:
+    # https://github.com/zsh-users/zsh-syntax-highlighting/blob/56b134f5d62ae3d4e66c7f52bd0cc2595f9b305b/zsh-syntax-highlighting.zsh#L126-161
+    #
+    # SPDX-SnippetBegin
+    # SPDX-License-Identifier: BSD-3-Clause
+    # SPDX-SnippetCopyrightText: 2010-2011 zsh-syntax-highlighting contributors
+    #--------------8<-------------------8<-------------------8<-----------------
+    # Rebind all ZLE widgets to make them invoke _zsh_highlights.
+    _zsh_highlight_bind_widgets()
+    {
+      # Load ZSH module zsh/zleparameter, needed to override user defined widgets.
+      zmodload zsh/zleparameter 2>/dev/null || {
+        echo 'zsh-syntax-highlighting: failed loading zsh/zleparameter.' >&2
+        return 1
+      }
+
+      # Override ZLE widgets to make them invoke _zsh_highlight.
+      local cur_widget
+      for cur_widget in ${${(f)"$(builtin zle -la)"}:#(.*|_*|orig-*|run-help|which-command|beep|yank*)}; do
+        case $widgets[$cur_widget] in
+
+          # Already rebound event: do nothing.
+          user:$cur_widget|user:_zsh_highlight_widget_*);;
+
+          # User defined widget: override and rebind old one with prefix "orig-".
+          user:*) eval "zle -N orig-$cur_widget ${widgets[$cur_widget]#*:}; \
+                        _zsh_highlight_widget_$cur_widget() { builtin zle orig-$cur_widget -- \"\$@\" && _zsh_highlight }; \
+                        zle -N $cur_widget _zsh_highlight_widget_$cur_widget";;
+
+          # Completion widget: override and rebind old one with prefix "orig-".
+          completion:*) eval "zle -C orig-$cur_widget ${${widgets[$cur_widget]#*:}/:/ }; \
+                              _zsh_highlight_widget_$cur_widget() { builtin zle orig-$cur_widget -- \"\$@\" && _zsh_highlight }; \
+                              zle -N $cur_widget _zsh_highlight_widget_$cur_widget";;
+
+          # Builtin widget: override and make it call the builtin ".widget".
+          builtin) eval "_zsh_highlight_widget_$cur_widget() { builtin zle .$cur_widget -- \"\$@\" && _zsh_highlight }; \
+                         zle -N $cur_widget _zsh_highlight_widget_$cur_widget";;
+
+          # Default: unhandled case.
+          *) echo "zsh-syntax-highlighting: unhandled ZLE widget '$cur_widget'" >&2 ;;
+        esac
+      done
+    }
+    #-------------->8------------------->8------------------->8-----------------
+    # SPDX-SnippetEnd
 
 
-        # Default: unhandled case.
-        *) echo "zsh-syntax-highlighting: unhandled ZLE widget '$cur_widget'" >&2 ;;
-      esac
-    done
-  }
-  #-------------->8------------------->8------------------->8-----------------
+    _zsh_highlight_bind_widgets
+  fi
 
 
-  _zsh_highlight_bind_widgets
+  unfunction _history-substring-search-function-callable
 fi
 fi
 
 
 _history-substring-search-begin() {
 _history-substring-search-begin() {
@@ -243,10 +295,17 @@ _history-substring-search-begin() {
     fi
     fi
 
 
     #
     #
-    # Escape and join query parts with wildcard character '*' as separator
-    # `(j:CHAR:)` join array to string with CHAR as separator
+    # Escape and join query parts with wildcard character '*' as seperator
+    # `(j:CHAR:)` join array to string with CHAR as seperator
+    #
+    local search_pattern="${(j:*:)_history_substring_search_query_parts[@]//(#m)[\][()|\\*?#<>~^]/\\$MATCH}*"
+
+    #
+    # Support anchoring history search to the beginning of the command
     #
     #
-    local search_pattern="*${(j:*:)_history_substring_search_query_parts[@]//(#m)[\][()|\\*?#<>~^]/\\$MATCH}*"
+    if [[ -z $HISTORY_SUBSTRING_SEARCH_PREFIXED ]]; then
+      search_pattern="*${search_pattern}"
+    fi
 
 
     #
     #
     # Find all occurrences of the search pattern in the history file.
     # Find all occurrences of the search pattern in the history file.
@@ -304,12 +363,21 @@ _history-substring-search-begin() {
 _history-substring-search-end() {
 _history-substring-search-end() {
   setopt localoptions extendedglob
   setopt localoptions extendedglob
 
 
+  local highlight_memo=
   _history_substring_search_result=$BUFFER
   _history_substring_search_result=$BUFFER
 
 
+  if [[ $_history_substring_search_zsh_5_9 -eq 1 ]]; then
+    highlight_memo='memo=history-substring-search'
+  fi
+
   # the search was successful so display the result properly by clearing away
   # the search was successful so display the result properly by clearing away
   # existing highlights and moving the cursor to the end of the result buffer
   # existing highlights and moving the cursor to the end of the result buffer
   if [[ $_history_substring_search_refresh_display -eq 1 ]]; then
   if [[ $_history_substring_search_refresh_display -eq 1 ]]; then
-    region_highlight=()
+    if [[ -n $highlight_memo ]]; then
+      region_highlight=( "${(@)region_highlight:#*${highlight_memo}*}" )
+    else
+      region_highlight=()
+    fi
     CURSOR=${#BUFFER}
     CURSOR=${#BUFFER}
   fi
   fi
 
 
@@ -329,7 +397,9 @@ _history-substring-search-end() {
       if [[ $query_part_match_index -le ${#BUFFER:$highlight_start_index} ]]; then
       if [[ $query_part_match_index -le ${#BUFFER:$highlight_start_index} ]]; then
         highlight_start_index=$(( $highlight_start_index + $query_part_match_index ))
         highlight_start_index=$(( $highlight_start_index + $query_part_match_index ))
         highlight_end_index=$(( $highlight_start_index + ${#query_part} ))
         highlight_end_index=$(( $highlight_start_index + ${#query_part} ))
-        region_highlight+=("$(($highlight_start_index - 1)) $(($highlight_end_index - 1)) $_history_substring_search_query_highlight")
+        region_highlight+=(
+          "$(($highlight_start_index - 1)) $(($highlight_end_index - 1)) ${_history_substring_search_query_highlight}${highlight_memo:+,$highlight_memo}"
+        )
       fi
       fi
     done
     done
   fi
   fi
@@ -338,6 +408,23 @@ _history-substring-search-end() {
   # zle -R "mn: "$_history_substring_search_match_index" m#: "${#_history_substring_search_matches}
   # zle -R "mn: "$_history_substring_search_match_index" m#: "${#_history_substring_search_matches}
   # read -k -t 200 && zle -U $REPLY
   # read -k -t 200 && zle -U $REPLY
 
 
+  #
+  # When this function returns, z-sy-h runs its line-pre-redraw hook. It has no
+  # logic for determining highlight priority, when two different memo= marked
+  # region highlights overlap; instead, it always prioritises itself. Below is
+  # a workaround for dealing with it.
+  #
+  if [[ $_history_substring_search_zsh_5_9 -eq 1 ]]; then
+    zle -R
+    #
+    # After line redraw with desired highlight, wait for timeout or user input
+    # before removing search highlight and exiting. This ensures no highlights
+    # are left lingering after search is finished.
+    #
+    read -k -t ${HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_TIMEOUT:-1} && zle -U $REPLY
+    region_highlight=( "${(@)region_highlight:#*${highlight_memo}*}" )
+  fi
+
   # Exit successfully from the history-substring-search-* widgets.
   # Exit successfully from the history-substring-search-* widgets.
   return 0
   return 0
 }
 }