#!/usr/bin/env zsh ############################################################################## # # Copyright (c) 2023 Sophie Tyalie # Copyright (c) 2023 @Nezteb # 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 FIZSH 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. # ##############################################################################
#---------------------------------- # main #----------------------------------
# global configuration : ${ATUIN_HISTORY_SEARCH_FILTER_MODE='global'}
# iteratively use the next mechanism to process down if the previous didn't succeed _atuin-history-down-buffer || _atuin-history-down-search # || # zle atuin-search
_atuin-history-search-begin() { # assume we will not render anything _atuin_history_refresh_display=
# If the buffer is the same as the previously displayed history substring # search result, then just keep stepping through the match list. Otherwise # start a new search. if [[ -n $BUFFER && $BUFFER == ${_atuin_history_search_result:-} ]]; then return; fi
# Clear the previous result. _atuin_history_search_result=''
# setup our search query if [[ -z $BUFFER ]]; then _atuin_history_search_query= else _atuin_history_search_query="$BUFFER" fi
# reset search index _atuin_history_match_index=0 }
_atuin-history-search-end() { # if our index is <= 0 just print the query we started with if [[ $_atuin_history_match_index -le 0 ]]; then _atuin_history_search_result="$_atuin_history_search_query" fi
# draw buffer if requested if [[ $_atuin_history_refresh_display -eq 1 ]]; then BUFFER="$_atuin_history_search_result" CURSOR="${#BUFFER}" fi
# for debug purposes only #zle -R "mn: "$_atuin_history_match_index" / qr: $_atuin_history_search_result" #read -k -t 1 && zle -U $REPLY
}
_atuin-history-up-buffer() { # attribution to zsh-history-substring-search # # Check if the UP arrow was pressed to move the cursor within a multi-line # buffer. This amounts to three tests: # # 1. $#buflines -gt 1. # # 2. $CURSOR -ne $#BUFFER. # # 3. Check if we are on the first line of the current multi-line buffer. # If so, pressing UP would amount to leaving the multi-line buffer. # # We check this by adding an extra "x" to $LBUFFER, which makes # sure that xlbuflines is always equal to the number of lines # until $CURSOR (including the line with the cursor on it). # local buflines XLBUFFER xlbuflines buflines=(${(f)BUFFER}) XLBUFFER=$LBUFFER"x" xlbuflines=(${(f)XLBUFFER})
if [[ $#buflines -gt 1 && $CURSOR -ne $#BUFFER && $#xlbuflines -ne 1 ]]; then zle up-line-or-history return 0 fi
return 1 }
_atuin-history-down-buffer() { # attribution to zsh-history-substring-search # # Check if the DOWN arrow was pressed to move the cursor within a multi-line # buffer. This amounts to three tests: # # 1. $#buflines -gt 1. # # 2. $CURSOR -ne $#BUFFER. # # 3. Check if we are on the last line of the current multi-line buffer. # If so, pressing DOWN would amount to leaving the multi-line buffer. # # We check this by adding an extra "x" to $RBUFFER, which makes # sure that xrbuflines is always equal to the number of lines # from $CURSOR (including the line with the cursor on it). # local buflines XRBUFFER xrbuflines buflines=(${(f)BUFFER}) XRBUFFER="x"$RBUFFER xrbuflines=(${(f)XRBUFFER})
if [[ $#buflines -gt 1 && $CURSOR -ne $#BUFFER && $#xrbuflines -ne 1 ]]; then zle down-line-or-history return 0 fi
# 3. 简单的去重处理 # 如果 Atuin 返回的结果和我们刚刚展示的本地命令完全一样,说明该命令也被记录在 DB 里了。 # 为了不让用户按两次上看到同样的内容,我们跳过这个结果,自动取下一条。 if [[ -n "$_atuin_local_last_cmd" && "$search_result" == "$_atuin_local_last_cmd" ]]; then offset=$((offset + 1)) # 增加全局 index 以保持同步 _atuin_history_match_index+=1 search_result=$(_atuin-history-do-search $offset "$_atuin_history_search_query") fi
if [[ -z $search_result ]]; then # if search result is empty, there's no more history # so just show the previous result _atuin_history_match_index+=-1 # 如果是因为刚跳过了重复项导致为空,这里需要回退两步吗? # 简单处理:只要为空,就停止移动 return 1 fi
# 1. 如果回退到了 index 1,且本地命令有效,则显示本地命令 if [[ $_atuin_history_match_index -eq 1 ]]; then if _atuin_is_local_cmd_valid; then _atuin_history_search_result="$_atuin_local_last_cmd" return 0 fi fi
# 2. 否则,从 Atuin 搜索 local offset if _atuin_is_local_cmd_valid; then offset=$((_atuin_history_match_index - 2)) else offset=$((_atuin_history_match_index - 1)) fi
# 如果 offset < 0,说明我们回到了 index 0 或 1(且无本地缓存)的状态 # 但由于 index 0 在 search-end 会处理(显示原始 query),这里只需要处理需要查库的情况 if [[ $offset -lt 0 ]]; then # 这种情况通常是 index=0,search_result 在 _atuin-history-search-end 会被覆盖为 query # 但如果在 index=1 且无 local cmd,我们需要查 offset 0 if [[ $_atuin_history_match_index -eq 1 ]]; then offset=0 else return 0 fi fi
# 去重逻辑同 Up if [[ -n "$_atuin_local_last_cmd" && "$search_result" == "$_atuin_local_last_cmd" ]]; then # 在 Down 的时候,如果遇到重复,说明我们正从更老的历史往下翻 # 实际上这里的逻辑比较复杂,为了简化体验,我们直接显示即可, # 或者为了严格对应 Up 的行为,我们不应该在这里处理跳过,因为用户是往下翻。 # 保持原样即可。 : fi
_atuin_history_search_result="$search_result"
return 0 }
_atuin-history-do-search() { local offset=$1 local query="$2"
# 防御性编程:offset 小于 0 不执行 if [[ $offset -lt 0 ]]; then return; fi
local mode="prefix" local final_query="$query"
# 【核心修改】:检测大写字母以启用 Smart Case # 如果包含 A-Z,则切换到 fuzzy 模式,并转义正则字符,最后加 ^ 锚定行首 if [[ "$query" =~ [A-Z] ]]; then mode="fuzzy"