77# Copyright (c) 2011 Sorin Ionescu
88# Copyright (c) 2011 Vincent Guerci
99# Copyright (c) 2016 Geza Lore
10+ # Copyright (c) 2017 Bengt Brodersen
1011# All rights reserved.
1112#
1213# Redistribution and use in source and binary forms, with or without
@@ -51,6 +52,7 @@ typeset -g _history_substring_search_refresh_display
5152typeset -g _history_substring_search_query_highlight
5253typeset -g _history_substring_search_result
5354typeset -g _history_substring_search_query
55+ typeset -g -a _history_substring_search_query_parts
5456typeset -g -A _history_substring_search_raw_matches
5557typeset -g -i _history_substring_search_raw_match_index
5658typeset -g -A _history_substring_search_matches
@@ -65,6 +67,7 @@ typeset -g HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND='bg=magenta,fg=white,bold'
6567typeset -g HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND=' bg=red,fg=white,bold'
6668typeset -g HISTORY_SUBSTRING_SEARCH_GLOBBING_FLAGS=' i'
6769typeset -g HISTORY_SUBSTRING_SEARCH_ENSURE_UNIQUE=' '
70+ typeset -g HISTORY_SUBSTRING_SEARCH_FUZZY=' '
6871typeset -g _history_substring_search_{refresh_display,query_highlight,result,query,match_index,raw_match_index}
6972typeset -ga _history_substring_search{,_raw}_matches
7073
@@ -223,6 +226,7 @@ _history-substring-search-begin() {
223226 # speed things up a little.
224227 #
225228 _history_substring_search_query=
229+ _history_substring_search_query_parts=()
226230 _history_substring_search_raw_matches=()
227231
228232 else
@@ -233,20 +237,31 @@ _history-substring-search-begin() {
233237 _history_substring_search_query=$BUFFER
234238
235239 #
236- # $BUFFER contains the text that is in the command-line currently.
237- # we put an extra "\\" before meta characters such as "\(" and "\)",
238- # so that they become "\\\(" and "\\\)".
240+ # compose search pattern
239241 #
240- local escaped_query=${BUFFER// (# m)[\][()|\\*?#<>~^]/ \\ $MATCH }
242+ if [[ -n $HISTORY_SUBSTRING_SEARCH_FUZZY ]]; then
243+ #
244+ # `=` split string in arguments
245+ #
246+ _history_substring_search_query_parts=(${=_history_substring_search_query} )
247+ else
248+ _history_substring_search_query_parts=(${_history_substring_search_query} )
249+ fi
241250
242251 #
243- # Find all occurrences of the search query in the history file.
252+ # Escape and join query parts with wildcard character '*' as seperator
253+ # `(j:CHAR:)` join array to string with CHAR as seperator
254+ #
255+ local search_pattern=" *${(j:*: )_history_substring_search_query_parts[@]// (# m)[\][()|\\*?#<>~^]/ \\ $MATCH } *"
256+
257+ #
258+ # Find all occurrences of the search pattern in the history file.
244259 #
245260 # (k) returns the "keys" (history index numbers) instead of the values
246261 # (R) returns values in reverse older, so the index of the youngest
247262 # matching history entry is at the head of the list.
248263 #
249- _history_substring_search_raw_matches=(${(k)history[(R)(#$HISTORY_SUBSTRING_SEARCH_GLOBBING_FLAGS)*${escaped_query}* ]} )
264+ _history_substring_search_raw_matches=(${(k)history[(R)(#$HISTORY_SUBSTRING_SEARCH_GLOBBING_FLAGS)${search_pattern} ]} )
250265 fi
251266
252267 #
@@ -309,16 +324,20 @@ _history-substring-search-end() {
309324 _zsh_highlight
310325
311326 # highlight the search query inside the command line
312- if [[ -n $_history_substring_search_query_highlight && -n $_history_substring_search_query ]]; then
313- #
314- # The following expression yields a variable $MBEGIN, which
315- # indicates the begin position + 1 of the first occurrence
316- # of _history_substring_search_query in $BUFFER.
317- #
318- : ${(S)BUFFER## (# m$HISTORY_SUBSTRING_SEARCH_GLOBBING_FLAGS )($_history_substring_search_query ## )}
319- local begin=$(( MBEGIN - 1 ))
320- local end=$(( begin + $# _history_substring_search_query ))
321- region_highlight+=(" $begin $end $_history_substring_search_query_highlight " )
327+ if [[ -n $_history_substring_search_query_highlight ]]; then
328+ # highlight first matching query parts
329+ local highlight_start_index=0
330+ local highlight_end_index=0
331+ for query_part in $_history_substring_search_query_parts ; do
332+ local escaped_query_part=${query_part// (# m)[\][()|\\*?#<>~^]/ \\ $MATCH }
333+ # (i) get index of pattern
334+ local query_part_match_index=${${BUFFER: $highlight_start_index } [(i)(#$HISTORY_SUBSTRING_SEARCH_GLOBBING_FLAGS)${escaped_query_part}]}
335+ if [[ $query_part_match_index -le ${# BUFFER: $highlight_start_index } ]]; then
336+ highlight_start_index=$(( $highlight_start_index + $query_part_match_index ))
337+ highlight_end_index=$(( $highlight_start_index + ${# query_part} ))
338+ region_highlight+=(" $(( $highlight_start_index - 1 )) $(( $highlight_end_index - 1 )) $_history_substring_search_query_highlight " )
339+ fi
340+ done
322341 fi
323342
324343 # For debugging purposes:
0 commit comments