Comments (1)
Details
## .zshrc
source "$ZPLUGINS/zsh-autosuggestions/zsh-autosuggestions.zsh"
# Atuin (history replacement)
# Keeps the history in a sqlite db (Needs to be after zsh-autosuggestions)
source "$ZDOTDIR/plugins/atuin.zsh"
# bind UP and DOWN arrow keys to history search
bindkey "$terminfo[kcuu1]" atuin_fzf_inline_up
bindkey "$terminfo[kcud1]" atuin_fzf_inline_down
bindkey '^[[A' atuin_fzf_inline_up
bindkey '^[[B' atuin_fzf_inline_down
bindkey '^r' _atuin_search_widget # Default
bindkey '^[r' _atuin_fzf # Using fzf interactively
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=#555'
ZSH_AUTOSUGGEST_STRATEGY=('atuin')
ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE='20'
ZSH_AUTOSUGGEST_USE_ASYNC='true'
ZSH_AUTOSUGGEST_MANUAL_REBIND='true'
## plugins/atuin.zsh
# Enable atuin history plugin
# Import atuin history into zsh array
# Support multiline commands
# Support interactive atuin, interactive fzf,
# inline history navigation with or without fzf filtering by query
# Support zsh-autosuggestions using atuin history
# Support highlighting of query terms
## Source the atuin zsh plugin
# Equivalent to `eval "$(atuin init zsh)"` but a little bit faster (~2ms)
# Yeah, probably not worth it but it's already written so ¯\_(ツ)_/¯
atuin_version="# $(atuin --version)"
current_atuin_version="$(head -1 $ZCACHE/_atuin_init_zsh.zsh)"
if [[ "$atuin_version" != "$current_atuin_version" ]]; then
# Check for new atuin version and update the cache
echo "$atuin_version" > "$ZCACHE/_atuin_init_zsh.zsh"
atuin init zsh >> "$ZCACHE/_atuin_init_zsh.zsh"
fi
export ATUIN_NOBIND='true'
source "$ZCACHE/_atuin_init_zsh.zsh"
## Import atuin history into zsh array
declare -U _history_atuin # -U indicates unique array so duplicates are not inserted
# Get the last 1500 (More than the 'max_lenght' below, to account for duplicates) commands from the atuin history database
# Separate each command with ';;\n;;' instead of '\n', this allows support for multiline commands.
# Split them using the same separator and insert them into the array: (@pws:;;\n;;:).
# Sort them by timestamp in descending order (Last executed command first), this helps with:
# Limiting the number of sqlite rows (otherwise it woulds trim the latest rows)
# As latest commands are inserted first they have preference to earlier ones when checking for duplicates.
# Bonus points for simplifying 'Limit history array size' below.
_history_atuin=("${(@pws:;;\n;;:)"$(sqlite3 --newline $';;\n;;' ~/.local/share/atuin/history.db 'SELECT command FROM history ORDER BY timestamp DESC LIMIT 2000')"}")
# Redeclare as normal array, this allows us to store new duplicate commands for keeping a better session history.
# Ofc those duplicates get removed when opening a new terminal.
declare -a history_atuin
## Limit history array size to 1000
# _history_atuin:0:1000, Get slice from 0 to 1000, effectively trimming it's size
# (@Oa), Reverse the order so last executed commands are first
history_atuin=(${(@Oa)_history_atuin:0:1000})
unset _history_atuin
if [[ -z $history_atuin ]]; then
echo 'Error: Atuin history array is empty'
echo 'Atuin may not be installed, history not imported, wrong path for the database or something else.'
exit
fi
## Keep history array updated with new executed commands
_atuin_update_history_preexec(){
if [[ $history_atuin[-1] != "$1" ]]; then
# Store the new command in history if it's different than the last one
# This basically avoids adding duplicate commands one right after the other
history_atuin+=("$1") # Store the command in our zsh array (cache)
fi
}
add-zsh-hook preexec _atuin_update_history_preexec
## Set selected command as new buffer and move the cursor to the end
__atuin_set_buffer() {
BUFFER="$1"
CURSOR=${#BUFFER}
}
## Invoque fzf with atuin history
_atuin_fzf() {
# Print history splitting commands with nulls `print -rNC1 -- $var`
# Get the history inverted `${(@Oa)history_atuin}` for the print command.
# Get and parse history using nulls as separators
# Nulls are used as separators instead of newlines to keep support for multiline commands
output=$(print -rNC1 -- "${(@Oa)history_atuin}" | fzf --read0)
__atuin_set_buffer "$output"
}
zle -N _atuin_fzf
typeset -g _atuin_fzf_inline_query
typeset -g _atuin_fzf_inline_query_matches
typeset -g _atuin_fzf_inline_result
typeset -i _atuin_fzf_inline_result_index=0
## Emulate history-substring-search but with atuin results
__atuin_fzf_inline() {
typeset -g suggestion=''
local add_or_substract="$1"
# Move the cursor instead of changing history command
# but only if he current command is multiline, and we are not in the first or last line
# (depending if we are moving up or downon the history)
if (( $BUFFERLINES > 1 )); then
local lines_before_end
if [[ "$add_or_substract" == '+' ]]; then
lines_before_end=(${(f)LBUFFER})
else
lines_before_end=(${(f)RBUFFER})
fi
if (( ${#lines_before_end} > 1 )); then
if [[ "$add_or_substract" == '+' ]]; then
zle up-line # zsh builtin, move cursor one line up
else
zle down-line # zsh builtin, move cursor one line down
fi
return
fi
fi
# Add or substract one from the index, depending if we are going up or down on the history
_atuin_fzf_inline_result_index=$(( _atuin_fzf_inline_result_index $add_or_substract 1 ))
if (( _atuin_fzf_inline_result_index < 0 )); then
# Drop the current query, clear the buffer.
_atuin_fzf_inline_result_index=0
unset _atuin_fzf_inline_query_matches
__atuin_set_buffer ''
return
elif [[ "$BUFFER" == "$_atuin_fzf_inline_result" ]]; then
if (( _atuin_fzf_inline_result_index == 0 )); then
__atuin_set_buffer "$_atuin_fzf_inline_query"
return
fi
else
_atuin_fzf_inline_result_index=1
unset _atuin_fzf_inline_query_matches
_atuin_fzf_inline_query="$BUFFER"
fi
## Get the history array we are gonna use
local query="$_atuin_fzf_inline_query"
local index="$_atuin_fzf_inline_result_index"
if [[ -n "$query" ]]; then # Filter commands by query using fzf
# Create array of matches, filtered by fzf if not already created
# Print history splitting commands with nulls `print -rNC1 -- $var`
# Filter results with fzf, using nulls as separators for both input and output
# Create array using nulls as separators `(@0)`
# Nulls are used as separators instead of newlines to keep support for multiline commands
if [[ -z "$_atuin_fzf_inline_query_matches" ]]; then
_atuin_fzf_inline_query_matches=("${(@0)"$(print -rNC1 -- "$history_atuin[@]" | fzf --read0 --print0 --exact --no-sort --filter="$query")"}")
shift -p _atuin_fzf_inline_query_matches # Pop the last item, it's an empty string
fi
matches='_atuin_fzf_inline_query_matches'
else # No need to filter with fzf if there is no query, use the entire history array
matches='history_atuin'
fi
max_index=${#${(P)matches}} # Get size of array named $matches
if (( index > max_index )); then
_atuin_fzf_inline_result_index="$max_index"
# We already reached the end of the history, do nothing
return
fi
## Get next command from history
# From array named $matches get element -$index, negative number to start from the last element
_atuin_fzf_inline_result=${${(P)matches}[-$index]}
## Set command as current edit buffer
__atuin_set_buffer "$_atuin_fzf_inline_result"
# Find all matches of $query in $BUFFER and highlight them
if [[ -n "$query" ]]; then
_zsh_highlight # This needs to be before region_highlight+= or the added highlight will be ignored, does _zsh_highlight clear the region_highlight array?.
local last_match_end=0
while true; do
# (i) Search $query inside $BUFFER and return the index of the first matching character
# (e) Match using $query as a literal string (i.e. query=* will match the literal character `*`)
# (b:last_match_end:) Start matching from index=$last_match_end
local match_start="${BUFFER[(ieb:last_match_end:)${query}]}"
if (( $match_start <= ${#BUFFER} )); then
local match_end=$(( $match_start + ${#query} ))
last_match_end=$match_end
# Highlight from index $match_start to $match_end, they need an offset of 1
region_highlight+=("$(($match_start - 1)) $(($match_end - 1)) underline") # bold
else
# The query didn't match anything
break
fi
done
fi
}
atuin_fzf_inline_up() {
__atuin_fzf_inline '+'
}
atuin_fzf_inline_down() {
__atuin_fzf_inline '-'
}
zle -N atuin_fzf_inline_up
zle -N atuin_fzf_inline_down
## Add compatibility with zsh-autosuggest
# Tell autosuggest to clear suggestions when atuin changes the edit buffer
ZSH_AUTOSUGGEST_CLEAR_WIDGETS+=('atuin_fzf_inline_up')
ZSH_AUTOSUGGEST_CLEAR_WIDGETS+=('atuin_fzf_inline_down')
_zsh_autosuggest_strategy_atuin() {
# Straight copy from: https://github.com/zsh-users/zsh-autosuggestions/blob/master/src/strategies/history.zsh
# Only changes are the history source and inverting the matching order.
#
# Copyright (c) 2013 Thiago de Arruda
# Copyright (c) 2016-2021 Eric Freese
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# Reset options to defaults and enable LOCAL_OPTIONS
emulate -L zsh
# Enable globbing flags so that we can use (#m) and (x~y) glob operator
setopt EXTENDED_GLOB
# Escape backslashes and all of the glob operators so we can use
# this string as a pattern to search the $history associative array.
# - (#m) globbing flag enables setting references for match data
# TODO: Use (b) flag when we can drop support for zsh older than v5.0.8
local prefix="${1//(#m)[\\*?[\]<>()|^~#]/\\$MATCH}"
# Get the history items that match the prefix, excluding those that match
# the ignore pattern
local pattern="$prefix*"
if [[ -n $ZSH_AUTOSUGGEST_HISTORY_IGNORE ]]; then
pattern="($pattern)~($ZSH_AUTOSUGGEST_HISTORY_IGNORE)"
fi
# Give the first history item matching the pattern as the suggestion
# - (r) subscript flag makes the pattern match on values
# - (R) same as r, but gives the last match
typeset -g suggestion="${history_atuin[(R)$pattern]}"
}
from dotfiles.
Related Issues (4)
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from dotfiles.