From bb61e5c24fbf432aa2df557bf5f0c7b334a704e8 Mon Sep 17 00:00:00 2001 From: Josh Bode Date: Thu, 27 Mar 2025 11:30:35 +1100 Subject: [PATCH] Add support for custom history providers Adds `HISTORY_SUBSTRING_SEARCH_MATCH_FUNCTION` option to allow specification of a custom function for sourcing matching commands. --- README.md | 21 ++++++++++++ zsh-history-substring-search.zsh | 55 +++++++++++++++++++------------- 2 files changed, 54 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index cfd33b4..918944f 100644 --- a/README.md +++ b/README.md @@ -215,6 +215,27 @@ default values. * `HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_TIMEOUT` is a global variable that defines a timeout in seconds for clearing the search highlight. +* `HISTORY_SUBSTRING_SEARCH_MATCH_FUNCTION` is a global variable that defines + the name of a function for getting matches from custom history providers. + + For example, to integrate with [`histdb`](https://github.com/larkery/zsh-histdb) + the following function could be used: + + ```bash + function _history-substring-get-raw-matches-histdb { + local SEP=$(printf $'\1') # column separator + local results=$( + histdb --sep ${SEP} ${_history_substring_search_query_parts[@]} | + tail -n +2 | # kill header + sed 's/\(.\+\)[^\x01]\x01/&\x00/' | # add \0 to separate records + cut -d ${SEP} -f 4 # get the command column + ) + _history_substring_search_raw_matches=(${(ps:\0:)results}) + _history_substring_search_raw_matches=(${_history_substring_search_raw_matches[@]%$'\n'}) + } + + HISTORY_SUBSTRING_SEARCH_MATCH_FUNCTION="_history-substring-get-raw-matches-histdb" + ``` History ------------------------------------------------------------------------------ diff --git a/zsh-history-substring-search.zsh b/zsh-history-substring-search.zsh index 2137b79..7cf6784 100644 --- a/zsh-history-substring-search.zsh +++ b/zsh-history-substring-search.zsh @@ -49,6 +49,7 @@ : ${HISTORY_SUBSTRING_SEARCH_ENSURE_UNIQUE=''} : ${HISTORY_SUBSTRING_SEARCH_FUZZY=''} : ${HISTORY_SUBSTRING_SEARCH_PREFIXED=''} +: ${HISTORY_SUBSTRING_SEARCH_MATCH_FUNCTION=''} #----------------------------------------------------------------------------- # declare internal global variables @@ -245,6 +246,30 @@ if [[ $+functions[_zsh_highlight] -eq 0 ]]; then unfunction _history-substring-search-function-callable fi +_history-substring-get-raw-matches() { + # + # Escape and join query parts with wildcard character '*' as separator + # `(j:CHAR:)` join array to string with CHAR as separator + # + local search_pattern="${(j:*:)_history_substring_search_query_parts[@]//(#m)[\][()|\\*?#<>~^]/\\$MATCH}*" + + # + # Support anchoring history search to the beginning of the command + # + if [[ -z $HISTORY_SUBSTRING_SEARCH_PREFIXED ]]; then + search_pattern="*${search_pattern}" + fi + + # + # Find all occurrences of the search pattern in the history file. + # + # (k) returns the "keys" (history index numbers) instead of the values + # (R) returns values in reverse older, so the index of the youngest + # matching history entry is at the head of the list. + # + _history_substring_search_raw_matches=(${(k)history[(R)(#$HISTORY_SUBSTRING_SEARCH_GLOBBING_FLAGS)${search_pattern}]}) +} + _history-substring-search-begin() { setopt localoptions extendedglob @@ -294,27 +319,7 @@ _history-substring-search-begin() { _history_substring_search_query_parts=(${==_history_substring_search_query}) fi - # - # 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 - # - if [[ -z $HISTORY_SUBSTRING_SEARCH_PREFIXED ]]; then - search_pattern="*${search_pattern}" - fi - - # - # Find all occurrences of the search pattern in the history file. - # - # (k) returns the "keys" (history index numbers) instead of the values - # (R) returns values in reverse older, so the index of the youngest - # matching history entry is at the head of the list. - # - _history_substring_search_raw_matches=(${(k)history[(R)(#$HISTORY_SUBSTRING_SEARCH_GLOBBING_FLAGS)${search_pattern}]}) + ${HISTORY_SUBSTRING_SEARCH_MATCH_FUNCTION:-_history-substring-get-raw-matches} fi # @@ -655,7 +660,13 @@ _history-substring-search-found() { # 2. Use $HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND # to highlight the current buffer. # - BUFFER=$history[$_history_substring_search_matches[$_history_substring_search_match_index]] + if [[ -n $HISTORY_SUBSTRING_SEARCH_MATCH_FUNCTION ]]; then + # Custom provider functions provide entries directly + BUFFER=$_history_substring_search_matches[$_history_substring_search_match_index] + else + # Builtin search provides the index to matching history entries + BUFFER=$history[$_history_substring_search_matches[$_history_substring_search_match_index]] + fi _history_substring_search_query_highlight=$HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND }