Skip to content

Instantly share code, notes, and snippets.

@oficsu
Last active March 19, 2026 16:58
Show Gist options
  • Select an option

  • Save oficsu/57c7aace2d69855066a783b1d3a300eb to your computer and use it in GitHub Desktop.

Select an option

Save oficsu/57c7aace2d69855066a783b1d3a300eb to your computer and use it in GitHub Desktop.
An alterntive to __fzf_history__ that supports immediate execution, moving around match and showing date with arbitrary HISTTIMEFORMAT
#!/bin/bash
# Demonstration: https://youtu.be/Uj3nmYq5LnQ
# MIT License
#
# Copyright (c) 2024 Ofee Oficsu
#
# 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.
# yes: print warnings and hints on initialization
# no: print only errors
: ${MY_FZF_VERBOSE:=yes}
# fzf, sk or auto
# auto is not default until sk of my version stops crashing on unwrap()
: ${MY_FZF_COMMAND:=fzf}
# a positive number: count of history search results in lines
: ${MY_FZF_WIDGET_HEIGHT:=5}
# disable or a fzf key, toggle-sort fzf action
: ${MY_FZF_TOGGLE_SORT_SHORTCUT:=ctrl-s}
# disable or a fzf key, toggle-preview fzf action
: ${MY_FZF_PREVIEW_SHORTCUT:=ctrl-p}
# disable or a fzf key, execute selected item immediately
: ${MY_FZF_EVAL_SHORTCUT:=disable}
# disable or a fzf key, if an item is selected, execute it; otherwise, paste the query
# please, use the new MY_FZF_ACCEPT_SHORTCUT variable instead of old MY_FZF_EVAL_SHORTCUT_OR_PASTE_QUERY
: ${MY_FZF_ACCEPT_SHORTCUT:=${MY_FZF_EVAL_SHORTCUT_OR_PASTE_QUERY:-enter,double-click}}
# disable or a fzf key, paste the item with the cursor at the end of the last pattern
: ${MY_FZF_EDIT_SHORTCUT:=disable}
# disable or a fzf key, paste the item with the cursor at the end of the line
: ${MY_FZF_EDIT_AT_END_SHORTCUT:=ctrl-e}
# disable or a fzf key, paste the item with the cursor at the start of the line
: ${MY_FZF_EDIT_AT_START_SHORTCUT:=ctrl-a}
# disable or a fzf key, paste the query
: ${MY_FZF_PASTE_QUERY_SHORTCUT:=ctrl-w}
# disable: arrow keys used for navigation inside the history search query
# enable: an arrow key immediately substitutes the history element,
# closes history search, and moves cursor one character left/right
: ${MY_FZF_ARROWS_SHORTCUTS:=enable}
# disable: will display commands only if they match the query
# enable: will also include commands whose date matches the query
: ${MY_FZF_SEARCH_BY_DATE:=disable}
# dropdown: latest history elements at the top of dropdown
# dynamic: use dropdown until there is enough space, then invert items order
# dropdown-reserve: dropdown + reserves space at the bottom of the terminal
: ${MY_FZF_LAYOUT:=dropdown-reserve}
# enable: try using bat for bash syntax highlighting in preview window
# disable: don't try using bat at all
: ${MY_FZF_HIGHLIGHT_SYNTAX:=enable}
# preview highlighting theme, full list available with 'bat --list-themes'
: ${MY_FZF_HIGHLIGHT_THEME:=Nord}
# any extra argument list to pass to underlying fzf invocation
MY_FZF_EXTRA_ARGS+=()
# charcters used for conditional logic in ctrl+r bash binding,
# they were chosen randomly from the private-use unicode range
# MY_FZF_BINING_HISTORY_DEFAULT=$'\xEE\x8C\xB5'
: ${MY_FZF_BINING_HISTORY:=$'\xEE\x8C\xB5'} # U+E335
: ${MY_FZF_BINING_ACCEPT:=$'\xEE\x8C\xB6'} # U+E336
# is an invisible separator between a command and its date;
# probably you don't want to change it, it will be stripped from the dates
: ${MY_FZF_CMD_DELIMITER:=$'\xE2\x81\xA3'} # U+2063
__my_fzf_history_note__() {
[ "$MY_FZF_VERBOSE" = no ] && return
local blue='\033[0;34m'
local nc='\033[0m'
echo 1>&2 -e "[${blue} note ${nc}][ my fzf history ]" "$@"
}
__my_fzf_history_warning__() {
[ "$MY_FZF_VERBOSE" = no ] && return
local yellow='\033[0;33m'
local nc='\033[0m'
echo 1>&2 -e "[${yellow} warning ${nc}][ my fzf history ]" "$@"
}
__my_fzf_history_error__() {
local red='\033[0;31m'
local nc='\033[0m'
echo 1>&2 -e "[${red} error ${nc}][ my fzf history ]" "$@"
}
__my_fzf_history_reversed__() {
tac <(HISTFILE=/dev/stdout; history -a) "$HISTFILE"
}
__my_fzf_history_highlight_preview__() {
local width="$FZF_PREVIEW_COLUMNS"
# a unique invisible character that won't
# break syntax higlighting in bat like \n
# see github.com/sharkdp/bat/issues/3079
local magic=$'\U2062'
fold --spaces --width "$width" \
` # remove trailing spaces ` \
| sed -E 's/\s*$//' \
| sed -zE "s/\n/$magic/g" \
| bat --tabs 8 ` # as in fold util ` \
--color=always \
--paging=never \
--decorations=never \
--theme "$MY_FZF_HIGHLIGHT_THEME" \
"$@" \
| sed -zE "s/$magic/\n/g"
}
__my_fzf_history_formatted_zsh__() {
local d="$MY_FZF_CMD_DELIMITER"
local fmt="$HISTTIMEFORMAT"
fc -nlr -t "${fmt}${d}" 0 -1 \
| sed --regexp-extended \
` : fix colored $HISTTIMEFORMAT ` \
-e 's/\\033|\\e/\x1b/g' \
` : remove extra spaces from fc -l ` \
-e "s/${d}\s*/${d}/g"
}
__my_fzf_history_formatted_bash__() {
__my_fzf_history_reversed__ \
| awk -v fmt="$HISTTIMEFORMAT" \
-v delim="$MY_FZF_CMD_DELIMITER" '
function print_separated(date, command) {
# everything will be broken if a date
# occasionally contains the delimiter,
# so remove it, it is invisible anyway
gsub(delim, "", date)
print date delim command
}
{
if (/#[0-9]+\s*$/) {
if (prev) {
ts = substr($0, 2)
date = strftime(fmt, ts)
print_separated(date, prev)
}
prev = ""
} else {
if (prev) { print_separated("", prev) }
gsub("\\s*$", "", $0)
if ($0 && !a[$0]++) {
prev = $0
} else {
prev = ""
}
}
}'
}
__my_fzf_history_formatted__() {
if [ -n "$ZSH_VERSION" ]; then
__my_fzf_history_formatted_zsh__
elif [ -n "$BASH_VERSION" ]; then
__my_fzf_history_formatted_bash__
fi
}
# removes datetime prefix from a fzf output
__my_fzf_history_extract_command__() {
grep -Poz "$MY_FZF_CMD_DELIMITER\K[\s\S]*" | tr -d '\0'
}
__my_fzf_history_extract_date__() {
grep -Poz "^[^$MY_FZF_CMD_DELIMITER]*" | tr -d '\0'
}
__my_fzf_history_preview__() {
local date=$(echo "$@" | __my_fzf_history_extract_date__)
local command=$(echo "$@" | __my_fzf_history_extract_command__)
if [ "$MY_FZF_HIGHLIGHT_SYNTAX" = enable ]; then
printf %s\\n "$date" | __my_fzf_history_highlight_preview__ --language log
printf %s "$command" | __my_fzf_history_highlight_preview__ --language bash
else
echo "${date}${command}"
fi
}
# old bash version: https://stackoverflow.com/questions/2575037
# a zsh compatible: https://stackoverflow.com/a/43911767
__my_fzf_history_prompt_pos__() (
if [ -n "$BASH_VERSION" ]; then
stty -echo
local t="$(stty -g)"
trap 'stty "$t"' RETURN
fi
echo -en "\033[6n" > /dev/tty
read -t 1 -s -d 'R' line < /dev/tty
line="${line##*\[}"
line="${line%;*}"
echo "$line"
)
__my_fzf_history_a_version_greater_than__() {
local command="$1"
local expected="$2"
local actual=$("$command" --version | grep -oP '\d+\.\d+\.\d+')
local greater=$(echo -e "$expected\n$actual" | sort --version-sort | tail -1)
[ "$greater" = "$actual" ]
}
__my_fzf_history_fzf_version_greater_than__() {
__my_fzf_history_a_version_greater_than__ fzf "$1"
}
__my_fzf_history_sk_version_greater_than__() {
__my_fzf_history_a_version_greater_than__ sk "$1"
}
__my_fzf_history_is_sk__() {
[ "$MY_FZF_COMMAND" = sk ]
}
__my_fzf_history_is_fzf__() {
[ "$MY_FZF_COMMAND" = fzf ]
}
__my_fzf_history_preview_command__() {
if [ -n "$ZSH_VERSION" ]; then
local file=${(%):-%x}
elif [ -n "$BASH_VERSION" ]; then
local file="$BASH_SOURCE"
fi
local preview=$(printf 'source %q; __my_fzf_history_preview__ "$@"' "$file")
local shell="$(printf %q $(readlink "/proc/$$/exe"))"
printf %s "$shell -c '$preview' $shell {}"
}
__my_fzf_history_render_prompt__() {
if [ -n "$ZSH_VERSION" ]; then
print -P "$PS1"
elif [ -n "$BASH_VERSION" ]; then
printf %s "${PS1@P}"
fi
}
__my_fzf_history_selector__() {
local __my_fzf_history_binds__=(
--print-query
)
if [ "$MY_FZF_TOGGLE_SORT_SHORTCUT" != disable ]; then
__my_fzf_history_binds__+=(--bind "${MY_FZF_TOGGLE_SORT_SHORTCUT}:toggle-sort")
fi
if [ "$MY_FZF_PREVIEW_SHORTCUT" != disable ]; then
__my_fzf_history_binds__+=(--bind "${MY_FZF_PREVIEW_SHORTCUT}:toggle-preview")
fi
if [ "$MY_FZF_EVAL_SHORTCUT" != disable ]; then
__my_fzf_history_binds__+=(--expect "$MY_FZF_EVAL_SHORTCUT")
fi
if [ "$MY_FZF_ACCEPT_SHORTCUT" != disable ]; then
__my_fzf_history_binds__+=(--expect "$MY_FZF_ACCEPT_SHORTCUT")
fi
if [ "$MY_FZF_EDIT_SHORTCUT" != disable ]; then
__my_fzf_history_binds__+=(--expect "$MY_FZF_EDIT_SHORTCUT")
fi
if [ "$MY_FZF_EDIT_AT_START_SHORTCUT" != disable ]; then
__my_fzf_history_binds__+=(--expect "$MY_FZF_EDIT_AT_START_SHORTCUT")
fi
if [ "$MY_FZF_EDIT_AT_END_SHORTCUT" != disable ]; then
__my_fzf_history_binds__+=(--expect "$MY_FZF_EDIT_AT_END_SHORTCUT")
fi
if [ "$MY_FZF_PASTE_QUERY_SHORTCUT" != disable ]; then
__my_fzf_history_binds__+=(--expect "$MY_FZF_PASTE_QUERY_SHORTCUT")
fi
if [ "$MY_FZF_ARROWS_SHORTCUTS" != disable ]; then
__my_fzf_history_binds__+=(--expect left,right)
fi
local __my_fzf_history_layout__=(
--layout=reverse
--bind=ctrl-r:down # height + prompt
--height $(( $MY_FZF_WIDGET_HEIGHT + 1 ))
)
local pos=$(__my_fzf_history_prompt_pos__)
local wanted_height=$(($pos + $MY_FZF_WIDGET_HEIGHT))
if [ "$MY_FZF_LAYOUT" = dynamic ] && (($wanted_height > $LINES)); then
__my_fzf_history_layout__=(
--layout=default
--bind=ctrl-r:up
--border top # height + prompt + border
--height $(( $MY_FZF_WIDGET_HEIGHT + 2 ))
)
fi
# make compatible with fzf < 0.27.2
local bright_blue=12
local white=7
local blue=4
local dark_blue=#10303d
local __my_fzf_history_style__=(
--color=bg+:$dark_blue
--color=hl+:$bright_blue
--color=hl:$bright_blue
--color=fg+:$white
--color=pointer:$blue
--color=gutter:0
--pointer=⟭
--ansi
)
# debian 11&12 provide too old fzf releases
if __my_fzf_history_fzf_version_greater_than__ 0.52.0; then
__my_fzf_history_style__+=(--highlight-line)
fi
if __my_fzf_history_is_fzf__ && __my_fzf_history_fzf_version_greater_than__ 0.42.0; then
__my_fzf_history_style__+=(--info=inline-right)
else
__my_fzf_history_style__+=(--info=hidden)
fi
if __my_fzf_history_fzf_version_greater_than__ 0.27.0; then
__my_fzf_history_style__+=(--preview-window :hidden,right,wrap,border-left)
else
__my_fzf_history_style__+=(--preview-window right:wrap:hidden)
fi
if __my_fzf_history_fzf_version_greater_than__ 0.28.0; then
__my_fzf_history_style__+=(--scroll-off 2)
fi
if __my_fzf_history_fzf_version_greater_than__ 0.36.0; then
__my_fzf_history_style__+=(--no-scrollbar)
fi
if __my_fzf_history_fzf_version_greater_than__ 0.35.0; then
__my_fzf_history_style__+=(--no-separator)
fi
local __my_fzf_history_args__=(
--no-multi
--no-info
--prompt "$(__my_fzf_history_render_prompt__)"
--preview "$(__my_fzf_history_preview_command__)"
"${__my_fzf_history_binds__[@]}"
"${__my_fzf_history_style__[@]}"
"${__my_fzf_history_layout__[@]}"
--delimiter "$MY_FZF_CMD_DELIMITER"
)
if [ "${MY_FZF_SEARCH_BY_DATE}" = disable ]; then
__my_fzf_history_args__+=(--nth="2..")
fi
if __my_fzf_history_fzf_version_greater_than__ 0.33.0; then
__my_fzf_history_args__+=(--scheme=history)
fi
__my_fzf_history_args__+=("${MY_FZF_EXTRA_ARGS[@]}")
(
export FZF_DEFAULT_OPTS=
export FZF_DEFAULT_OPTS_FILE=
export FZF_API_KEY=
export MY_FZF_VERBOSE=no
__my_fzf_history_formatted__ | "$MY_FZF_COMMAND" "${__my_fzf_history_args__[@]}"
)
}
__my_fzf_history_reserve_space__() (
local lines=5
# move cursor down with scrolling
for _ in `seq $lines`; do
echo -ne "\eD"
done
# move cursor up without scrolling
echo -ne "\e[${lines}A"
)
__my_fzf_history_add_readline_bind__() {
# add binding for all modes
for m in emacs-standard vi-{command,insert}; do
bind -m $m "$@" 2>/dev/null
done
}
__my_fzf_history_add_zsh_bind__() {
for m in emacs vi{cmd,ins}; do
bindkey -M $m "$@"
done
}
__my_fzf_history_eval_immediately__() {
if [ -z "$1" ]; then
return
fi
if [ -n "$ZSH_VERSION" ]; then
LBUFFER="$1"
RBUFFER=""
zle accept-line
elif [ -n "$BASH_VERSION" ]; then
READLINE_LINE="$1"
# $MY_FZF_BINING_ACCEPT will be executed unconditionally, make it really do accept-line
__my_fzf_history_add_readline_bind__ '"'$MY_FZF_BINING_ACCEPT'"':accept-line
fi
}
__my_fzf_history_edit_selection__() {
local cli="$1"
local pos="$2"
if [ -n "$ZSH_VERSION" ]; then
LBUFFER="${cli:0:$pos}"
RBUFFER="${cli:$pos}"
elif [ -n "$BASH_VERSION" ]; then
READLINE_LINE="$cli"
READLINE_POINT="$pos"
# $MY_FZF_BINING_ACCEPT will be executed unconditionally
# we don't want it to really do accept-line
__my_fzf_history_add_readline_bind__ -x '"'$MY_FZF_BINING_ACCEPT'"':
fi
}
__my_fzf_history_eval_immediately_or_paste_query__() {
local item="$1"
local quey="$2"
if [ -n "$item" ]; then
__my_fzf_history_eval_immediately__ "$item"
else
__my_fzf_history_edit_selection__ "$query" "${#query}"
fi
}
__my_fzf_history_last_index_of__() {
local word="$1"
local string="$2"
echo "$string" | grep -Fobie "$word" | cut -d: -f1 | tail -1
}
__my_fzf_history_last_match_index__() {
local query="$1"
local item="$2"
local expr="$3"
local result="${#item}"
for q in ${query[*]}; do
local index="$(__my_fzf_history_last_index_of__ "$q" "$item")"
if [ -n "$index" ]; then
result=$(( "$index" + "${#q}" ))
fi
done
echo $(("$result" "$expr"))
}
__my_fzf_history_value_of__() {
if [ -n "$ZSH_VERSION" ]; then
printf %s "${(P)1}"
elif [ -n "$BASH_VERSION" ]; then
printf %s "${!1}"
fi
}
__my_fzf_history__() {
# workaround: sometimes bash bindings are
# broken right after the current function
# exits or when a command is invoked from
# the history, so use stty to fix the bug
# https://unix.stackexchange.com/a/535654
if [ -n "$ZSH_VERSION" ]; then
emulate -L zsh
# we want fzf to overlay current prompt
printf '\r\033[K'
zle reset-prompt
elif [ -n "$BASH_VERSION" ]; then
stty_save="$(stty -g)"
stty sane
trap "stty '$stty_save'; trap - RETURN SIGINT" RETURN SIGINT
fi
local output
output=$(__my_fzf_history_selector__)
local ret=$?
local query=$(echo "$output" | sed -n 1p)
local key=$(echo "$output" | sed -n 2p)
local item=$(echo "$output" | sed -n 3p | __my_fzf_history_extract_command__)
case "$ret" in
0) ;;
# fzf's "no match"
1) ;;
# fzf's error
2) return;;
# interrupted by ctrl+c or esc
130) return;;
# ooops...
127) __my_fzf_history_error__ invalid shell command for become; return;;
*) __my_fzf_history_error__ unexpected exit code: $ret; return;;
esac
# tests if the key is inside of a list
__my_fzf_within__() (
local __key_list__="$(__my_fzf_history_value_of__ "MY_FZF_$1_SHORTCUT")"
local __pattern__="^${__key_list__//,/|}$"
[[ "$key" =~ $__pattern__ ]]
)
# if a command is selected, execute it; otherwise, paste the query
# default: enter or double-click
if __my_fzf_within__ ACCEPT; then
__my_fzf_history_eval_immediately_or_paste_query__ "$item" "$query"
# if a command is selected, execute it; otherwise, do nothing
# default: disabled
elif __my_fzf_within__ EVAL; then
__my_fzf_history_eval_immediately__ "$item"
# edit at the end of the last match
# default: disabled
elif __my_fzf_within__ EDIT; then
local pos="$(__my_fzf_history_last_match_index__ "$query" "$item")"
__my_fzf_history_edit_selection__ "$item" "$pos"
# edit at the end of the line
# default: ctrl-e
elif __my_fzf_within__ EDIT_AT_END; then
__my_fzf_history_edit_selection__ "$item" "${#item}"
# edit at the start of the line
# default: ctrl-a
elif __my_fzf_within__ EDIT_AT_START; then
__my_fzf_history_edit_selection__ "$item" 0
# paste the current query
# default: ctrl-w
elif __my_fzf_within__ PASTE_QUERY; then
__my_fzf_history_edit_selection__ "$query" "${#query}"
# edit at the end of the last match
# when the right keyboard is key pressed
elif [[ "$key" = right ]]; then
local pos="$(__my_fzf_history_last_match_index__ "$query" "$item" +1)"
__my_fzf_history_edit_selection__ "$item" "$pos"
# the same, but moves the cursor one symbol left
# when the left keyboard key is pressed
elif [[ "$key" = left ]]; then
local pos="$(__my_fzf_history_last_match_index__ "$query" "$item" -1)"
__my_fzf_history_edit_selection__ "$item" "$pos"
else
__my_fzf_history_error__ unexpected key: "'$key'"
fi
}
__my_fzf_history_hook_reserve_space__() {
if [ -n "$ZSH_VERSION" ]; then
autoload -U add-zsh-hook
add-zsh-hook precmd __my_fzf_history_reserve_space__
elif [ -n "$BASH_VERSION" ]; then
# i wish i had a better way to do this
PROMPT_COMMAND+=$'\n__my_fzf_history_reserve_space__'
fi
}
__my_fzf_history_select_command_auto__() {
if command -v 1>/dev/null fzf; then
echo fzf
return
fi
if ! command -v 1>/dev/null sk; then
echo sk
return
fi
__my_fzf_history_warning__ "neither fzf nor any compatible replacement is installed, history widget won't be enabled"
}
__my_fzf_history_select_command__() {
if [ "$MY_FZF_COMMAND" = auto ]; then
__my_fzf_history_select_command_auto__
return
fi
if ! command -v 1>/dev/null "$MY_FZF_COMMAND"; then
__my_fzf_history_warning__ "$MY_FZF_COMMAND was selected but is not installed, history widget won't be enabled"
return
fi
echo "$MY_FZF_COMMAND"
}
MY_FZF_COMMAND="$(__my_fzf_history_select_command__)"
if [ -e "$MY_FZF_COMMAND" ]; then
return
fi
if [ "$MY_FZF_HIGHLIGHT_SYNTAX" = enable ]; then
if ! command -v bat &>/dev/null; then
__my_fzf_history_note__ "bat is not installed, syntax highlight will be disabled"
MY_FZF_HIGHLIGHT_SYNTAX=disable
elif ! bat --list-themes | grep -qF "$MY_FZF_HIGHLIGHT_THEME"; then
__my_fzf_history_warning__ "bat doesn't support selected theme: '$MY_FZF_HIGHLIGHT_THEME'"
__my_fzf_history_warning__ " you can set a new theme by setting MY_FZF_HIGHLIGHT_THEME environment variable"
__my_fzf_history_warning__ " see 'bat --list-themes' for theme names"
MY_FZF_HIGHLIGHT_THEME=$(bat --list-themes | head -1)
fi
fi
if [ -n "$ZSH_VERSION" ]; then
zle -N fzf-history __my_fzf_history__
__my_fzf_history_add_zsh_bind__ '^r' fzf-history
elif [ -n "$BASH_VERSION" ]; then
__my_fzf_history_add_readline_bind__ -x '"'$MY_FZF_BINING_HISTORY'"':__my_fzf_history__
__my_fzf_history_add_readline_bind__ '"'$MY_FZF_BINING_ACCEPT'"':accept-line
__my_fzf_history_add_readline_bind__ '"\C-r": "'${MY_FZF_BINING_HISTORY}${MY_FZF_BINING_ACCEPT}'"'
fi
if [ "$MY_FZF_LAYOUT" = dropdown-reserve ]; then
__my_fzf_history_hook_reserve_space__
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment