Skip to content

Commit fd27842

Browse files
qoomongezalore
authored andcommitted
feat: add fuzzy search
1 parent 563c88c commit fd27842

File tree

2 files changed

+40
-16
lines changed

2 files changed

+40
-16
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ default values only after having loaded this script into your ZSH session.
117117
Flags" section in the zshexpn(1) man page to learn about the kinds of
118118
values you may assign to this variable.
119119

120+
* `HISTORY_SUBSTRING_SEARCH_FUZZY` is a global variable that defines
121+
how the command history will be searched for your query. If set to a non-empty
122+
value, causes this script to perform a fuzzy search by words, matching in
123+
given order e.g. `ab c` will match `*ab*c*`
124+
120125
* `HISTORY_SUBSTRING_SEARCH_ENSURE_UNIQUE` is a global variable that defines
121126
whether all search results returned are _unique_. If set to a non-empty
122127
value, then only unique search results are presented. This behaviour is off

zsh-history-substring-search.zsh

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
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
5152
typeset -g _history_substring_search_query_highlight
5253
typeset -g _history_substring_search_result
5354
typeset -g _history_substring_search_query
55+
typeset -g -a _history_substring_search_query_parts
5456
typeset -g -A _history_substring_search_raw_matches
5557
typeset -g -i _history_substring_search_raw_match_index
5658
typeset -g -A _history_substring_search_matches
@@ -65,6 +67,7 @@ typeset -g HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND='bg=magenta,fg=white,bold'
6567
typeset -g HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND='bg=red,fg=white,bold'
6668
typeset -g HISTORY_SUBSTRING_SEARCH_GLOBBING_FLAGS='i'
6769
typeset -g HISTORY_SUBSTRING_SEARCH_ENSURE_UNIQUE=''
70+
typeset -g HISTORY_SUBSTRING_SEARCH_FUZZY=''
6871
typeset -g _history_substring_search_{refresh_display,query_highlight,result,query,match_index,raw_match_index}
6972
typeset -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

Comments
 (0)