Added nvim / ghostty / starship config

Signed-off-by: Rack Lin <racklin@gmail.com>
This commit is contained in:
2026-02-10 14:50:15 +08:00
parent 6cc8e616c3
commit 86b71b9554
61 changed files with 2928 additions and 68 deletions

View File

@@ -0,0 +1,199 @@
# GitNow — Speed up your Git workflow. 🐠
# https://github.com/joseluisq/gitnow
set -g gitnow_xpaste
set -g gitnow_commands 'all' 'assume' 'bitbucket' 'bugfix' 'commit' 'feature' 'github' 'gitnow' 'hotfix' 'logs' 'merge' 'move' 'pull' 'push' 'release' 'show' 'stage' 'state' 'tag' 'unstage' 'untracked' 'upstream'
function __gitnow_read_config -d "Reads the GitNow config file"
# Sets a clipboard program
set gitnow_xpaste (__gitnow_get_clip_program)
# Config file path used by default
set -l config_file "$fish_snippets/.gitnow"
# Download the default .gitnow file
# Used as workaround for Fisher. see https://github.com/jorgebucaran/fisher/pull/573
if not test -e $config_file
curl -sSo $config_file https://raw.githubusercontent.com/joseluisq/gitnow/master/conf.d/.gitnow
end
# Prefer custom config file if it exists
if test -e $GITNOW_CONFIG_FILE
set config_file $GITNOW_CONFIG_FILE
else if not test -e $config_file
# Otherwise checks if default `.gitnow` file exists,
# if doesn't then skip out file parsing
return
end
# Parse `.gitnow` file content
# 2 = keybindings
# 3 = options
set -l v_section 0
# Valid sections
set -l v_keybindings "keybindings"
set -l v_options "options"
# Options set
set -l v_clipboard 0
# Loop every line
while read -la l
set -l v_str ""
set -l v_comment 0
set -l v_command_sep 0
set -l v_command_key ""
set -l v_command_val ""
# Loop every char for current line
echo $l | while read -n 1 -la c;
switch $c
case '['
if test $v_comment -eq 1; continue; end
# if test $v_section -gt 0
# set v_section 0
# continue
# end
# Start section
if test $v_section -eq 0; set v_section 1; end
case ']'
if test $v_comment -eq 1; continue; end
# Check section name
if test $v_section -eq 1
# options
if [ "$v_str" = "$v_options" ]
set v_section 3
continue
end
# keybindings
if [ "$v_str" = "$v_keybindings" ]
set v_section 2
continue
end
end
set v_section 0
case ' '
case '\n'
case '\t'
case '\r'
continue
case '#'
if test $v_comment -eq 0; set v_comment 1; end
continue
case '*'
if test $v_comment -eq 1; continue; end
# If section has started then accumulate chars and continue
if test $v_section -eq 1
set v_str "$v_str$c"
continue
end
# A [ abcde ] section is found so proceed with chars handling
# NOTE: only alphabetic and hyphens chars are allowed
if test $v_section -eq 2; or test $v_section -eq 3
switch $c
case 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z' '-'
if test $v_command_sep -eq 0
set v_command_key "$v_command_key$c"
continue
end
if test $v_command_sep -eq 2
set v_command_val "$v_command_val$c"
continue
end
case \\
if test $v_command_sep -eq 1
set v_command_sep 2
end
continue
case '='
set v_command_sep 1
if test $v_section -eq 3
set v_command_sep 2
continue
end
case '*'
continue
end
end
end
end
# 1. Handle options set
if test $v_section -eq 3
switch $v_command_key
# Clipboard option
case 'clipboard'
if [ "$v_command_val" = "true" ]
set v_clipboard 1
end
# NOTE: handle future new options using a new case
case '*'
continue
end
# continue loop after current option processed
set v_section 0
continue
end
# 2. Handle keybindings set
if not [ "$v_command_key" = "" ]; and not [ "$v_command_val" = "" ]
set -l cmd
switch $v_command_key
case 'release' 'hotfix' 'feature' 'bugfix'
# Read text from clipboard if there is a valid clipboard program
# and if the "clipboard" option is "true"
if test -n $gitnow_xpaste; and test $v_clipboard -eq 1
set cmd (echo -n "bind \\$v_command_val \"echo; if $v_command_key ($gitnow_xpaste); commandline -f repaint; else ; end\"")
else
# Otherwise read text from standard input
set cmd (echo -n "bind \\$v_command_val \"echo; if $v_command_key (read); commandline -f repaint; else ; end\"")
end
case '*'
# Check command key against a list of valid commands
set -l v_valid 0
for v in $gitnow_commands
if [ "$v" = "$v_command_key" ]
set v_valid 1
break
end
end
# If command key is not valid then just skip out
if test $v_valid -eq 0; continue; end
set cmd (echo -n "bind \\$v_command_val \"echo; $v_command_key; commandline -f repaint;\"")
end
eval $cmd
end
end < $config_file
end
function __gitnow_get_clip_program -d "Gets the current clip installed program"
set -l v_paste
if type -q xclip
set v_paste "xclip -selection clipboard -o"
else if type -q wl-clipboard
set v_paste "wl-paste"
else if type -q xsel
set v_paste "xsel --clipboard --output"
else if type -q pbpaste
set v_paste "pbpaste"
end
echo -n $v_paste
end

View File

@@ -0,0 +1,187 @@
# GitNow — Speed up your Git workflow. 🐠
# https://github.com/joseluisq/gitnow
function __gitnow_new_branch_switch
set -l branch_name $argv[1]
if test (count $argv) -eq 1
set branch_name $branch_name
command git checkout -b $branch_name
else
echo "Provide a branch name."
end
end
# adapted from https://gist.github.com/oneohthree/f528c7ae1e701ad990e6
function __gitnow_slugify
echo $argv | LC_ALL=C command iconv -t ascii//TRANSLIT | LC_ALL=C command sed -E 's/[^a-zA-Z0-9\-]+/_/g' | LC_ALL=C command sed -E 's/^(-|_)+|(-|_)+$//g'
end
function __gitnow_clone_repo
set -l repo $argv[1]
set -l platform $argv[2]
if test -n "$repo"
set -l ok 1
if echo $repo | LC_ALL=C command grep -q -E '^[\%S].+'
set -l user (command git config --global user.$platform)
if test -n "$user"
set -l repor (echo $repo | LC_ALL=C command sed -e "s/^%S/$user/")
set repo $repor
else
set ok 0
end
end
if test $ok -eq 1
if [ "$platform" = "github" ]
set url github.com
end
if [ "$platform" = "bitbucket" ]
set url bitbucket.org
end
set -l repo_url git@$url:$repo.git
echo "📦 Remote repository: $repo_url"
command git clone $repo_url
else
__gitnow_clone_msg $platform
end
else
__gitnow_clone_msg $platform
end
end
function __gitnow_clone_msg
set -l msg $argv[1]
echo "Repository name is required!"
echo "Example: $msg your-repo-name"
echo "Usages:"
echo " a) $msg username/repo-name"
echo " b) $msg username repo-name"
echo " c) $msg repo-name"
echo " For this, it's necessary to set your $msg username (login)"
echo " to global config before like: "
echo " git config --global user.$msg \"your-username\""
echo
end
function __gitnow_check_if_branch_exist
set -l xfound 0
if test (count $argv) -eq 1
set -l xbranch $argv[1]
set -l xbranch_list (__gitnow_current_branch_list)
for b in $xbranch_list
if [ "$xbranch" = "$b" ]
set xfound 1
break
end
end
end
echo $xfound
end
function __gitnow_clone_params
set -l repo
if count $argv >/dev/null
if test (count $argv) -gt 1
set repo $argv[1]/$argv[2]
else if echo $argv | LC_ALL=C command grep -q -E '^([a-zA-Z0-9\_\-]+)\/([a-zA-Z0-9\_\-]+)$'
set repo $argv
else
set repo "%S/$argv"
end
end
echo $repo
end
function __gitnow_gitflow_branch -a xprefix -a xbranch
set xbranch (__gitnow_slugify $xbranch)
set -l xbranch_full "$xprefix/$xbranch"
set -l xfound (__gitnow_check_if_branch_exist $xbranch_full)
if test $xfound -eq 1
echo "Branch `$xbranch_full` already exists. Nothing to do."
else
command git stash
__gitnow_new_branch_switch "$xbranch_full"
command git stash pop
end
end
function __gitnow_msg_not_valid_repository -a cmd
echo "Gitnow ($cmd): Current directory is not a valid Git repository."
end
function __gitnow_current_branch_name
command git symbolic-ref --short HEAD 2>/dev/null
end
function __gitnow_current_branch_list
command git branch --list --no-color | LC_ALL=C command sed -E "s/^(\*?[ \t]*)//g" 2>/dev/null
end
function __gitnow_current_remote
set -l branch_name (__gitnow_current_branch_name)
command git config "branch.$branch_name.remote" 2>/dev/null; or echo origin
end
function __gitnow_is_git_repository
command git rev-parse --git-dir >/dev/null 2>&1
end
function __gitnow_has_uncommited_changes
command git diff-index --quiet HEAD -- || echo "1" 2>&1
end
function __gitnow_get_latest_tag
command git tag --sort=-creatordate | head -n1 2>/dev/null
end
# lexicographic order and tag names treated as versions
# https://stackoverflow.com/a/52680984/2510591
function __gitnow_get_tags_ordered
command git -c 'versionsort.suffix=-' tag --list --sort=-version:refname
end
function __gitnow_get_latest_semver_release_tag
for tg in (__gitnow_get_tags_ordered)
if echo $tg | LC_ALL=C command grep -qE '^v?([0-9]+).([0-9]+).([0-9]+)$'
echo $tg 2>/dev/null
break
end
end
end
function __gitnow_increment_number -a strv
command echo $strv | LC_ALL=C command awk '
function increment(val) {
if (val ~ /[0-9]+/) { return val + 1 }
return val
}
{ print increment($0) }
' 2>/dev/null
end
function __gitnow_get_valid_semver_release_value -a tagv
command echo $tagv | LC_ALL=C command sed -n 's/^v\\{0,1\\}\([0-9].[0-9].[0-9]*\)\([}]*\)/\1/p' 2>/dev/null
end
function __gitnow_get_valid_semver_prerelease_value -a tagv
command echo $tagv | LC_ALL=C command sed -n 's/^v\\{0,1\\}\([0-9].[0-9].[0-9]-[a-zA-Z0-9\-_.]*\)\([}]*\)/\1/p' 2>/dev/null
end
function __gitnow_is_number -a strv
command echo -n $strv | LC_ALL=C command grep -qE '^([0-9]+)$'
end

View File

@@ -0,0 +1,115 @@
# GitNow — Speed up your Git workflow. 🐠
# https://github.com/joseluisq/gitnow
function __gitnow_manual -d "Gitnow: Manual page like"
echo (set_color --bold)"NAME"(set_color normal)
echo " GitNow — Speed up your Git workflow. 🐠"
echo
echo (set_color --bold)"VERSION"(set_color normal)
echo " $gitnow_version"
echo
echo (set_color --bold)"DESCRIPTION"(set_color normal)
echo " GitNow contains a rich command set that provides high-level operations on the top of Git(1)."
echo " A Fish Shell(2) alternative inspired by git-friendly(3)."
echo
echo " (1) https://git-scm.com/"
echo " (2) https://fishshell.com/"
echo " (3) https://github.com/jamiew/git-friendly"
echo
echo (set_color --bold)"COMMANDS"(set_color normal)
echo " "(set_color --bold)"state"(set_color normal)
echo " Show the working tree status in a compact way."
echo
echo " "(set_color --bold)"stage"(set_color normal)
echo " Stage files in the current working directory."
echo
echo " "(set_color --bold)"unstage"(set_color normal)
echo " Unstage files in the current working directory."
echo
echo " "(set_color --bold)"show"(set_color normal)
echo " Show commit detail objects."
echo
echo " "(set_color --bold)"untracked"(set_color normal)
echo " Check for untracked files and directories that could be removed."
echo
echo " "(set_color --bold)"commit"(set_color normal)
echo " Commit changes to the current repository."
echo
echo " "(set_color --bold)"commit-all"(set_color normal)
echo " Add and commit all changes to the current repository."
echo
echo " "(set_color --bold)"pull"(set_color normal)
echo " Pull changes from remote server but auto-stashing uncommitted changes."
echo
echo " "(set_color --bold)"push"(set_color normal)
echo " Push commit changes to the current remote repository."
echo
echo " "(set_color --bold)"upstream"(set_color normal)
echo " Commit all changes and push them to the current remote server."
echo
echo " "(set_color --bold)"move"(set_color normal)
echo " Switch from current branch to another but stashing uncommitted changes."
echo
echo " "(set_color --bold)"merge"(set_color normal)
echo " Merge given branch into the active one"
echo
echo " "(set_color --bold)"tag"(set_color normal)
echo " List and create release tag versions following Semver 2.0."
echo
echo " "(set_color --bold)"assume"(set_color normal)
echo " Ignore changes in certain files temporarily."
echo
echo " "(set_color --bold)"feature"(set_color normal)
echo " Create a new Gitflow feature branch from the current branch."
echo
echo " "(set_color --bold)"hotfix"(set_color normal)
echo " Create a new Gitflow hotfix branch from the current branch."
echo
echo " "(set_color --bold)"bugfix"(set_color normal)
echo " Create a new Gitflow bugfix branch from the current branch."
echo
echo " "(set_color --bold)"release"(set_color normal)
echo " Create a new Gitflow release branch from the current branch."
echo
echo " "(set_color --bold)"logs"(set_color normal)
echo " Show logs in a fancy way."
echo
echo " "(set_color --bold)"github"(set_color normal)
echo " Clone a GitHub repository over SSH."
echo
echo " "(set_color --bold)"bitbucket"(set_color normal)
echo " Clone a Bitbucket Cloud repository over SSH."
echo
echo (set_color --bold)"KEYBINDINGS"(set_color normal)
echo " state Alt + S"
echo " stage Alt + E"
echo " unstage Ctrl + E"
echo " show Alt + M"
echo " commit-all Alt + C"
echo " pull Alt + D"
echo " push Alt + P"
echo " upstream Alt + U"
echo " feature(1) Alt + F"
echo " hotfix(1) Alt + H"
echo " logs Alt + L"
echo
echo " (1) This command key binding will creates a new branch taking as name some text of the clipboard."
echo
echo (set_color --bold)"CONFIGURATION"(set_color normal)
echo " For a custom configuration (for example keybindings) place a "(set_color --bold)"~/.gitnow"(set_color normal)" file (1) in your home directory."
echo
echo " (1) An example file it can be found on "(set_color --bold)https://github.com/joseluisq/gitnow/tree/master/.gitnow(set_color normal)
echo
echo (set_color --bold)"FURTHER DOCUMENTATION"(set_color normal)
echo " For more details and examples check out "(set_color --bold)https://github.com/joseluisq/gitnow/blob/master/README.md(set_color normal)
echo
echo (set_color --bold)"CONTRIBUTIONS"(set_color normal)
echo " Send bug reports or pull requests to "(set_color --bold)https://github.com/joseluisq/gitnow(set_color normal)
echo
echo (set_color --bold)"LICENSE"(set_color normal)
echo " GitNow licensed under the MIT License "(set_color --bold)https://github.com/joseluisq/gitnow/blob/master/LICENSE.md(set_color normal)
echo
echo (set_color --bold)"AUTHOR"(set_color normal)
echo " (c) 2016-present Jose Quintana "(set_color --bold)"https://git.io/joseluisq"(set_color normal)
echo
end

View File

@@ -0,0 +1,14 @@
function __ssh_agent_is_started -d "check if ssh agent is already started"
if begin; test -f $SSH_ENV; and test -z "$SSH_AGENT_PID"; end
source $SSH_ENV > /dev/null
end
if begin; test -z "$SSH_AGENT_PID"; and test -z "$SSH_CONNECTION"; end
return 1
end
ssh-add -l > /dev/null 2>&1
if test $status -eq 2
return 1
end
end

View File

@@ -0,0 +1,5 @@
function __ssh_agent_start -d "start a new ssh agent"
ssh-agent -c | sed 's/^echo/#echo/' > $SSH_ENV
chmod 600 $SSH_ENV
source $SSH_ENV > /dev/null
end

View File

@@ -0,0 +1,9 @@
function _autopair_backspace
set --local index (commandline --cursor)
set --local buffer (commandline)
test $index -ge 1 &&
contains -- (string sub --start=$index --length=2 -- "$buffer") $autopair_pairs &&
commandline --function delete-char
commandline --function backward-delete-char
end

View File

@@ -0,0 +1,13 @@
function _autopair_insert_left --argument-names left right
set --local buffer (commandline)
set --local before (commandline --cut-at-cursor)
commandline --insert -- $left
switch "$buffer"
case "$before"{," "\*,$autopair_right\*}
set --local index (commandline --cursor)
commandline --insert -- $right
commandline --cursor $index
end
end

View File

@@ -0,0 +1,11 @@
function _autopair_insert_right --argument-names key
set --local buffer (commandline)
set --local before (commandline --cut-at-cursor)
switch "$buffer"
case "$before$key"\*
commandline --cursor (math (commandline --cursor) + 1)
case \*
commandline --insert -- $key
end
end

View File

@@ -0,0 +1,20 @@
function _autopair_insert_same --argument-names key
set --local buffer (commandline)
set --local index (commandline --cursor)
set --local next (string sub --start=(math $index + 1) --length=1 -- "$buffer")
if test (math (count (string match --all --regex -- "$key" "$buffer")) % 2) = 0
test $key = $next && commandline --cursor (math $index + 1) && return
commandline --insert -- $key
if test $index -lt 1 ||
contains -- (string sub --start=$index --length=1 -- "$buffer") "" " " $autopair_left &&
contains -- $next "" " " $autopair_right
commandline --insert -- $key
commandline --cursor (math $index + 1)
end
else
commandline --insert -- $key
end
end

View File

@@ -0,0 +1,7 @@
function _autopair_tab
commandline --paging-mode && down-or-search && return
string match --quiet --regex -- '\$[^\s]*"$' (commandline --current-token) &&
commandline --function delete-char
commandline --function complete
end

View File

@@ -0,0 +1,43 @@
function _fzf_configure_bindings_help --description "Prints the help message for fzf_configure_bindings."
echo "\
USAGE:
fzf_configure_bindings [--FEATURE[=KEY_SEQUENCE]...]
DESCRIPTION
By default, fzf_configure_bindings installs mnemonic key bindings for fzf.fish's features. Each
feature's binding can be customized through a corresponding namesake option:
FEATURE | MNEMONIC KEY SEQUENCE | CORRESPONDING OPTION
Search directory | Ctrl+Alt+F (F for file) | --directory
Search git log | Ctrl+Alt+L (L for log) | --git_log
Search git status | Ctrl+Alt+S (S for status) | --git_status
Search history | Ctrl+R (R for reverse) | --history
Search variables | Ctrl+V (V for variable) | --variables
Search processes | Ctrl+Alt+P (P for process) | --processes
An option with a key sequence value overrides the binding for its feature, while an option
without a value disables the binding. A feature that is not customized retains its default
menomonic binding specified above. Key bindings are installed for default and insert modes.
In terms of validation, fzf_configure_bindings fails if passed unknown options. Furthermore, it
expects an equals sign between an option's name and value. However, it does not validate key
sequences. Rather, consider using fish_key_reader to manually validate them.
In terms of experimentation, fzf_configure_bindings erases any bindings it previously installed
before installing new ones so it can be repeatedly executed in the same fish session without
problem. Once the desired fzf_configure_bindings command has been found, add it to config.fish
in order to persist the bindings.
The -h and --help options print this help message.
EXAMPLES
Install the default mnemonic bindings
\$ fzf_configure_bindings
Install the default bindings but override git log's binding to Ctrl+G
\$ fzf_configure_bindings --git_log=\cg
Install the default bindings but leave search history unbound
\$ fzf_configure_bindings --history
Alternative style of disabling search history
\$ fzf_configure_bindings --history=
An agglomeration of all the options
\$ fzf_configure_bindings --git_status=\cg --history=\ch --variables --directory --git_log
"
end

View File

@@ -0,0 +1,15 @@
# helper function for _fzf_search_variables
function _fzf_extract_var_info --argument-names variable_name set_show_output --description "Extract and reformat lines pertaining to \$variable_name from \$set_show_output."
# Extract only the lines about the variable, all of which begin with either
# $variable_name: ...or... $variable_name[
string match --regex "^\\\$$variable_name(?::|\[).*" <$set_show_output |
# Strip the variable name prefix, including ": " for scope info lines
string replace --regex "^\\\$$variable_name(?:: )?" '' |
# Distill the lines of values, replacing...
# [1]: |value|
# ...with...
# [1] value
string replace --regex ": \|(.*)\|" ' $1'
end

View File

@@ -0,0 +1,43 @@
# helper function for _fzf_search_directory
function _fzf_preview_file --description "Print a preview for the given file based on its file type."
# because there's no way to guarantee that _fzf_search_directory passes the path to _fzf_preview_file
# as one argument, we collect all the arguments into one single variable and treat that as the path
set file_path $argv
if test -L "$file_path" # symlink
# notify user and recurse on the target of the symlink, which can be any of these file types
set -l target_path (realpath "$file_path")
set_color yellow
echo "'$file_path' is a symlink to '$target_path'."
set_color normal
_fzf_preview_file "$target_path"
else if test -f "$file_path" # regular file
if set --query fzf_preview_file_cmd
# need to escape quotes to make sure eval receives file_path as a single arg
eval "$fzf_preview_file_cmd '$file_path'"
else
bat --style=numbers --color=always "$file_path"
end
else if test -d "$file_path" # directory
if set --query fzf_preview_dir_cmd
# see above
eval "$fzf_preview_dir_cmd '$file_path'"
else
# -A list hidden files as well, except for . and ..
# -F helps classify files by appending symbols after the file name
command ls -A -F "$file_path"
end
else if test -c "$file_path"
_fzf_report_file_type "$file_path" "character device file"
else if test -b "$file_path"
_fzf_report_file_type "$file_path" "block device file"
else if test -S "$file_path"
_fzf_report_file_type "$file_path" socket
else if test -p "$file_path"
_fzf_report_file_type "$file_path" "named pipe"
else
echo "$file_path doesn't exist." >&2
end
end

View File

@@ -0,0 +1,6 @@
# helper function for _fzf_preview_file
function _fzf_report_file_type --argument-names file_path file_type --description "Explain the file type for a file."
set_color red
echo "Cannot preview '$file_path': it is a $file_type."
set_color normal
end

View File

@@ -0,0 +1,43 @@
function _fzf_search_directory --description "Search the current directory. Replace the current token with the selected file paths."
# --string-cwd-prefix prevents fd >= 8.3.0 from prepending ./ to relative paths
set fd_opts --color=always --strip-cwd-prefix $fzf_fd_opts
set fzf_arguments --multi --ansi $fzf_dir_opts
set token (commandline --current-token)
# expand any variables or leading tilde (~) in the token
set expanded_token (eval echo -- $token)
# unescape token because it's already quoted so backslashes will mess up the path
set unescaped_exp_token (string unescape -- $expanded_token)
# If the current token is a directory and has a trailing slash,
# then use it as fd's base directory.
if string match --quiet -- "*/" $unescaped_exp_token && test -d "$unescaped_exp_token"
set --append fd_opts --base-directory=$unescaped_exp_token
# use the directory name as fzf's prompt to indicate the search is limited to that directory
set --prepend fzf_arguments --prompt="$unescaped_exp_token" --preview="_fzf_preview_file $expanded_token{}"
set file_paths_selected $unescaped_exp_token(fd $fd_opts 2>/dev/null | _fzf_wrapper $fzf_arguments)
else
set --prepend fzf_arguments --query="$unescaped_exp_token" --preview='_fzf_preview_file {}'
set file_paths_selected (fd $fd_opts 2>/dev/null | _fzf_wrapper $fzf_arguments)
end
if test $status -eq 0
# Fish will cd implicitly if a directory name ending in a slash is provided.
# To help the user leverage this feature, we automatically append / to the selected path if
# - only one path was selected,
# - the user was in the middle of inputting the first token,
# - the path is a directory
# Then, the user only needs to hit Enter once more to cd into that directory.
if test (count $file_paths_selected) = 1
set commandline_tokens (commandline --tokenize)
if test "$commandline_tokens" = "$token" -a -d "$file_paths_selected"
set file_paths_selected $file_paths_selected/
end
end
commandline --current-token --replace -- (string escape -- $file_paths_selected | string join ' ')
end
commandline --function repaint
end

View File

@@ -0,0 +1,28 @@
function _fzf_search_git_log --description "Search the output of git log and preview commits. Replace the current token with the selected commit hash."
if not git rev-parse --git-dir >/dev/null 2>&1
echo '_fzf_search_git_log: Not in a git repository.' >&2
else
# see documentation for git format placeholders at https://git-scm.com/docs/git-log#Documentation/git-log.txt-emnem
# %h gives you the abbreviated commit hash, which is useful for saving screen space, but we will have to expand it later below
set log_fmt_str '%C(bold blue)%h%C(reset) - %C(cyan)%ad%C(reset) %C(yellow)%d%C(reset) %C(normal)%s%C(reset) %C(dim normal)[%an]%C(reset)'
set selected_log_lines (
git log --color=always --format=format:$log_fmt_str --date=short | \
_fzf_wrapper --ansi \
--multi \
--tiebreak=index \
--preview='git show --color=always --stat --patch {1}' \
--query=(commandline --current-token) \
$fzf_git_log_opts
)
if test $status -eq 0
for line in $selected_log_lines
set abbreviated_commit_hash (string split --field 1 " " $line)
set full_commit_hash (git rev-parse $abbreviated_commit_hash)
set --append commit_hashes $full_commit_hash
end
commandline --current-token --replace (string join ' ' $commit_hashes)
end
end
commandline --function repaint
end

View File

@@ -0,0 +1,33 @@
function _fzf_search_git_status --description "Search the output of git status. Replace the current token with the selected file paths."
if not git rev-parse --git-dir >/dev/null 2>&1
echo '_fzf_search_git_status: Not in a git repository.' >&2
else
set selected_paths (
# Pass configuration color.status=always to force status to use colors even though output is sent to a pipe
git -c color.status=always status --short |
_fzf_wrapper --ansi \
--multi \
--query=(commandline --current-token) \
$fzf_git_status_opts
)
if test $status -eq 0
# git status --short automatically escapes the paths of most files for us so not going to bother trying to handle
# the few edges cases of weird file names that should be extremely rare (e.g. "this;needs;escaping")
set cleaned_paths
for path in $selected_paths
if test (string sub --length 1 $path) = R
# path has been renamed and looks like "R LICENSE -> LICENSE.md"
# extract the path to use from after the arrow
set --append cleaned_paths (string split -- "-> " $path)[-1]
else
set --append cleaned_paths (string sub --start=4 $path)
end
end
commandline --current-token --replace -- (string join ' ' $cleaned_paths)
end
end
commandline --function repaint
end

View File

@@ -0,0 +1,24 @@
function _fzf_search_history --description "Search command history. Replace the command line with the selected command."
# history merge incorporates history changes from other fish sessions
builtin history merge
set command_with_ts (
# Reference https://devhints.io/strftime to understand strftime format symbols
builtin history --null --show-time="%m-%d %H:%M:%S │ " |
_fzf_wrapper --read0 \
--tiebreak=index \
--query=(commandline) \
# preview current command using fish_ident in a window at the bottom 3 lines tall
--preview="echo -- {4..} | fish_indent --ansi" \
--preview-window="bottom:3:wrap" \
$fzf_history_opts |
string collect
)
if test $status -eq 0
set command_selected (string split --max 1 " │ " $command_with_ts)[2]
commandline --replace -- $command_selected
end
commandline --function repaint
end

View File

@@ -0,0 +1,28 @@
function _fzf_search_processes --description "Search all running processes. Replace the current token with the pid of the selected process."
# use all caps to be consistent with ps default format
# snake_case because ps doesn't seem to allow spaces in the field names
set ps_preview_fmt (string join ',' 'pid' 'ppid=PARENT' 'user' '%cpu' 'rss=RSS_IN_KB' 'start=START_TIME' 'command')
set processes_selected (
ps -A -opid,command | \
_fzf_wrapper --multi \
--query (commandline --current-token) \
--ansi \
# first line outputted by ps is a header, so we need to mark it as so
--header-lines=1 \
# ps uses exit code 1 if the process was not found, in which case show an message explaining so
--preview="ps -o '$ps_preview_fmt' -p {1} || echo 'Cannot preview {1} because it exited.'" \
--preview-window="bottom:4:wrap" \
$fzf_processes_opts
)
if test $status -eq 0
for process in $processes_selected
set --append pids_selected (string split --no-empty --field=1 -- " " $process)
end
# string join to replace the newlines outputted by string split with spaces
commandline --current-token --replace -- (string join ' ' $pids_selected)
end
commandline --function repaint
end

View File

@@ -0,0 +1,46 @@
# This function expects the following two arguments:
# argument 1 = output of (set --show | psub), i.e. a file with the scope info and values of all variables
# argument 2 = output of (set --names | psub), i.e. a file with all variable names
function _fzf_search_variables --argument-names set_show_output set_names_output --description "Search and preview shell variables. Replace the current token with the selected variable."
if test -z "$set_names_output"
printf '%s\n' '_fzf_search_variables requires 2 arguments.' >&2
commandline --function repaint
return 22 # 22 means invalid argument in POSIX
end
# Exclude the history variable from being piped into fzf because
# 1. it's not included in $set_names_output
# 2. it tends to be a very large value => increases computation time
# 3._fzf_search_history is a much better way to examine history anyway
set all_variable_names (string match --invert history <$set_names_output)
set current_token (commandline --current-token)
# Use the current token to pre-populate fzf's query. If the current token begins
# with a $, remove it from the query so that it will better match the variable names
set cleaned_curr_token (string replace -- '$' '' $current_token)
set variable_names_selected (
printf '%s\n' $all_variable_names |
_fzf_wrapper --preview "_fzf_extract_var_info {} $set_show_output" \
--preview-window="wrap" \
--multi \
--query=$cleaned_curr_token \
$fzf_shell_vars_opts
)
if test $status -eq 0
# If the current token begins with a $, do not overwrite the $ when
# replacing the current token with the selected variable.
# Uses brace expansion to prepend $ to each variable name.
commandline --current-token --replace (
if string match --quiet -- '$*' $current_token
string join " " \${$variable_names_selected}
else
string join " " $variable_names_selected
end
)
end
commandline --function repaint
end

View File

@@ -0,0 +1,20 @@
function _fzf_wrapper --description "Prepares some environment variables before executing fzf."
# Make sure fzf uses fish to execute preview commands, some of which
# are autoloaded fish functions so don't exist in other shells.
# Use --local so that it doesn't clobber SHELL outside of this function.
set --local --export SHELL (command --search fish)
# If FZF_DEFAULT_OPTS is not set, then set some sane defaults.
# See https://github.com/junegunn/fzf#environment-variables
if not set --query FZF_DEFAULT_OPTS
# cycle allows jumping between the first and last results, making scrolling faster
# layout=reverse lists results top to bottom, mimicking the familiar layouts of git log, history, and env
# border shows where the fzf window begins and ends
# height=90% leaves space to see the current command and some scrollback, maintaining context of work
# preview-window=wrap wraps long lines in the preview window, making reading easier
# marker=* makes the multi-select marker more distinguishable from the pointer (since both default to >)
set --export FZF_DEFAULT_OPTS '--cycle --layout=reverse --border --height=90% --preview-window=wrap --marker="*"'
end
fzf $argv
end

View File

@@ -0,0 +1,3 @@
function fish_user_key_bindings
fzf_key_bindings
end

View File

@@ -0,0 +1,46 @@
# Always installs bindings for insert and default mode for simplicity and b/c it has almost no side-effect
# https://gitter.im/fish-shell/fish-shell?at=60a55915ee77a74d685fa6b1
function fzf_configure_bindings --description "Installs the default key bindings for fzf.fish with user overrides passed as options."
# no need to install bindings if not in interactive mode or running tests
status is-interactive || test "$CI" = true; or return
set options_spec h/help 'directory=?' 'git_log=?' 'git_status=?' 'history=?' 'variables=?' 'processes=?'
argparse --max-args=0 --ignore-unknown $options_spec -- $argv 2>/dev/null
if test $status -ne 0
echo "Invalid option or a positional argument was provided." >&2
_fzf_configure_bindings_help
return 22
else if set --query _flag_help
_fzf_configure_bindings_help
return
else
# Initialize with default key sequences and then override or disable them based on flags
# index 1 = directory, 2 = git_log, 3 = git_status, 4 = history, 5 = variables, 6 = processes
set key_sequences \e\cf \e\cl \e\cs \cr \cv \e\cp # \c = control, \e = escape
set --query _flag_directory && set key_sequences[1] "$_flag_directory"
set --query _flag_git_log && set key_sequences[2] "$_flag_git_log"
set --query _flag_git_status && set key_sequences[3] "$_flag_git_status"
set --query _flag_history && set key_sequences[4] "$_flag_history"
set --query _flag_variables && set key_sequences[5] "$_flag_variables"
set --query _flag_processes && set key_sequences[6] "$_flag_processes"
# If fzf bindings already exists, uninstall it first for a clean slate
if functions --query _fzf_uninstall_bindings
_fzf_uninstall_bindings
end
for mode in default insert
test -n $key_sequences[1] && bind --mode $mode $key_sequences[1] _fzf_search_directory
test -n $key_sequences[2] && bind --mode $mode $key_sequences[2] _fzf_search_git_log
test -n $key_sequences[3] && bind --mode $mode $key_sequences[3] _fzf_search_git_status
test -n $key_sequences[4] && bind --mode $mode $key_sequences[4] _fzf_search_history
test -n $key_sequences[5] && bind --mode $mode $key_sequences[5] "$_fzf_search_vars_command"
test -n $key_sequences[6] && bind --mode $mode $key_sequences[6] _fzf_search_processes
end
function _fzf_uninstall_bindings --inherit-variable key_sequences
bind --erase -- $key_sequences
bind --erase --mode insert -- $key_sequences
end
end
end

View File

@@ -0,0 +1,236 @@
# ____ ____
# / __/___ / __/
# / /_/_ / / /_
# / __/ / /_/ __/
# /_/ /___/_/ key-bindings.fish
#
# - $FZF_TMUX_OPTS
# - $FZF_CTRL_T_COMMAND
# - $FZF_CTRL_T_OPTS
# - $FZF_CTRL_R_COMMAND
# - $FZF_CTRL_R_OPTS
# - $FZF_ALT_C_COMMAND
# - $FZF_ALT_C_OPTS
# Key bindings
# ------------
# The oldest supported fish version is 3.1b1. To maintain compatibility, the
# command substitution syntax $(cmd) should never be used, even behind a version
# check, otherwise the source command will fail on fish versions older than 3.4.0.
function fzf_key_bindings
# Check fish version
set -l fish_ver (string match -r '^(\d+).(\d+)' $version 2> /dev/null; or echo 0\n0\n0)
if test \( "$fish_ver[2]" -lt 3 \) -o \( "$fish_ver[2]" -eq 3 -a "$fish_ver[3]" -lt 1 \)
echo "This script requires fish version 3.1b1 or newer." >&2
return 1
else if not type -q fzf
echo "fzf was not found in path." >&2
return 1
end
function __fzf_defaults
# $argv[1]: Prepend to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
# $argv[2..]: Append to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
test -n "$FZF_TMUX_HEIGHT"; or set -l FZF_TMUX_HEIGHT 40%
string join ' ' -- \
"--height $FZF_TMUX_HEIGHT --min-height=20+ --bind=ctrl-z:ignore" $argv[1] \
(test -r "$FZF_DEFAULT_OPTS_FILE"; and string join -- ' ' <$FZF_DEFAULT_OPTS_FILE) \
$FZF_DEFAULT_OPTS $argv[2..-1]
end
function __fzfcmd
test -n "$FZF_TMUX_HEIGHT"; or set -l FZF_TMUX_HEIGHT 40%
if test -n "$FZF_TMUX_OPTS"
echo "fzf-tmux $FZF_TMUX_OPTS -- "
else if test "$FZF_TMUX" = "1"
echo "fzf-tmux -d$FZF_TMUX_HEIGHT -- "
else
echo "fzf"
end
end
function __fzf_parse_commandline -d 'Parse the current command line token and return split of existing filepath, fzf query, and optional -option= prefix'
set -l fzf_query ''
set -l prefix ''
set -l dir '.'
# Set variables containing the major and minor fish version numbers, using
# a method compatible with all supported fish versions.
set -l -- fish_major (string match -r -- '^\d+' $version)
set -l -- fish_minor (string match -r -- '^\d+\.(\d+)' $version)[2]
# fish v3.3.0 and newer: Don't use option prefix if " -- " is preceded.
set -l -- match_regex '(?<fzf_query>[\s\S]*?(?=\n?$)$)'
set -l -- prefix_regex '^-[^\s=]+=|^-(?!-)\S'
if test "$fish_major" -eq 3 -a "$fish_minor" -lt 3
or string match -q -v -- '* -- *' (string sub -l (commandline -Cp) -- (commandline -p))
set -- match_regex "(?<prefix>$prefix_regex)?$match_regex"
end
# Set $prefix and expanded $fzf_query with preserved trailing newlines.
if test "$fish_major" -ge 4
# fish v4.0.0 and newer
string match -q -r -- $match_regex (commandline --current-token --tokens-expanded | string collect -N)
else if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
# fish v3.2.0 - v3.7.1 (last v3)
string match -q -r -- $match_regex (commandline --current-token --tokenize | string collect -N)
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^\\\(?=~)|\\\(?=\$\w)' '')
else
# fish older than v3.2.0 (v3.1b1 - v3.1.2)
set -l -- cl_token (commandline --current-token --tokenize | string collect -N)
set -- prefix (string match -r -- $prefix_regex $cl_token)
set -- fzf_query (string replace -- "$prefix" '' $cl_token | string collect -N)
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^\\\(?=~)|\\\(?=\$\w)|\\\n\\\n$' '')
end
if test -n "$fzf_query"
# Normalize path in $fzf_query, set $dir to the longest existing directory.
if test \( "$fish_major" -ge 4 \) -o \( "$fish_major" -eq 3 -a "$fish_minor" -ge 5 \)
# fish v3.5.0 and newer
set -- fzf_query (path normalize -- $fzf_query)
set -- dir $fzf_query
while not path is -d $dir
set -- dir (path dirname $dir)
end
else
# fish older than v3.5.0 (v3.1b1 - v3.4.1)
if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
# fish v3.2.0 - v3.4.1
string match -q -r -- '(?<fzf_query>^[\s\S]*?(?=\n?$)$)' \
(string replace -r -a -- '(?<=/)/|(?<!^)/+(?!\n)$' '' $fzf_query | string collect -N)
else
# fish v3.1b1 - v3.1.2
set -- fzf_query (string replace -r -a -- '(?<=/)/|(?<!^)/+(?!\n)$' '' $fzf_query | string collect -N)
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r '\\\n$' '')
end
set -- dir $fzf_query
while not test -d "$dir"
set -- dir (dirname -z -- "$dir" | string split0)
end
end
if not string match -q -- '.' $dir; or string match -q -r -- '^\./|^\.$' $fzf_query
# Strip $dir from $fzf_query - preserve trailing newlines.
if test "$fish_major" -ge 4
# fish v4.0.0 and newer
string match -q -r -- '^'(string escape --style=regex -- $dir)'/?(?<fzf_query>[\s\S]*)' $fzf_query
else if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
# fish v3.2.0 - v3.7.1 (last v3)
string match -q -r -- '^/?(?<fzf_query>[\s\S]*?(?=\n?$)$)' \
(string replace -- "$dir" '' $fzf_query | string collect -N)
else
# fish older than v3.2.0 (v3.1b1 - v3.1.2)
set -- fzf_query (string replace -- "$dir" '' $fzf_query | string collect -N)
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^/?|\\\n$' '')
end
end
end
string escape -n -- "$dir" "$fzf_query" "$prefix"
end
# Store current token in $dir as root for the 'find' command
function fzf-file-widget -d "List files and folders"
set -l commandline (__fzf_parse_commandline)
set -lx dir $commandline[1]
set -l fzf_query $commandline[2]
set -l prefix $commandline[3]
set -lx FZF_DEFAULT_OPTS (__fzf_defaults \
"--reverse --walker=file,dir,follow,hidden --scheme=path" \
"$FZF_CTRL_T_OPTS --multi --print0")
set -lx FZF_DEFAULT_COMMAND "$FZF_CTRL_T_COMMAND"
set -lx FZF_DEFAULT_OPTS_FILE
set -l result (eval (__fzfcmd) --walker-root=$dir --query=$fzf_query | string split0)
and commandline -rt -- (string join -- ' ' $prefix(string escape -- $result))' '
commandline -f repaint
end
function fzf-history-widget -d "Show command history"
set -l -- command_line (commandline)
set -l -- current_line (commandline -L)
set -l -- total_lines (count $command_line)
set -l -- fzf_query (string escape -- $command_line[$current_line])
set -lx FZF_DEFAULT_OPTS (__fzf_defaults '' \
'--nth=2..,.. --scheme=history --multi --wrap-sign="\t↳ "' \
'--bind=\'shift-delete:execute-silent(eval history delete --exact --case-sensitive -- (string escape -n -- {+} | string replace -r -a "^\d*\\\\\\t|(?<=\\\\\\n)\\\\\\t" ""))+reload(eval $FZF_DEFAULT_COMMAND)\'' \
"--bind=ctrl-r:toggle-sort,alt-r:toggle-raw --highlight-line $FZF_CTRL_R_OPTS" \
'--accept-nth=2.. --read0 --print0 --with-shell='(status fish-path)\\ -c)
set -lx FZF_DEFAULT_OPTS_FILE
set -lx FZF_DEFAULT_COMMAND
if type -q perl
set -a FZF_DEFAULT_OPTS '--tac'
set FZF_DEFAULT_COMMAND 'builtin history -z --reverse | command perl -0 -pe \'s/^/$.\t/g; s/\n/\n\t/gm\''
else
set FZF_DEFAULT_COMMAND \
'set -l h (builtin history -z --reverse | string split0);' \
'for i in (seq (count $h) -1 1);' \
'string join0 -- $i\t(string replace -a -- \n \n\t $h[$i] | string collect);' \
'end'
end
# Merge history from other sessions before searching
test -z "$fish_private_mode"; and builtin history merge
if set -l result (eval $FZF_DEFAULT_COMMAND \| (__fzfcmd) --query=$fzf_query | string split0)
if test "$total_lines" -eq 1
commandline -- (string replace -a -- \n\t \n $result)
else
set -l a (math $current_line - 1)
set -l b (math $current_line + 1)
commandline -- $command_line[1..$a] (string replace -a -- \n\t \n $result)
commandline -a -- '' $command_line[$b..-1]
end
end
commandline -f repaint
end
function fzf-cd-widget -d "Change directory"
set -l commandline (__fzf_parse_commandline)
set -lx dir $commandline[1]
set -l fzf_query $commandline[2]
set -l prefix $commandline[3]
set -lx FZF_DEFAULT_OPTS (__fzf_defaults \
"--reverse --walker=dir,follow,hidden --scheme=path" \
"$FZF_ALT_C_OPTS --no-multi --print0")
set -lx FZF_DEFAULT_OPTS_FILE
set -lx FZF_DEFAULT_COMMAND "$FZF_ALT_C_COMMAND"
if set -l result (eval (__fzfcmd) --query=$fzf_query --walker-root=$dir | string split0)
cd -- $result
commandline -rt -- $prefix
end
commandline -f repaint
end
if not set -q FZF_CTRL_R_COMMAND; or test -n "$FZF_CTRL_R_COMMAND"
if test -n "$FZF_CTRL_R_COMMAND"
echo "warning: FZF_CTRL_R_COMMAND is set to a custom command, but custom commands are not yet supported for CTRL-R" >&2
end
bind \cr fzf-history-widget
bind -M insert \cr fzf-history-widget
end
if not set -q FZF_CTRL_T_COMMAND; or test -n "$FZF_CTRL_T_COMMAND"
bind \ct fzf-file-widget
bind -M insert \ct fzf-file-widget
end
if not set -q FZF_ALT_C_COMMAND; or test -n "$FZF_ALT_C_COMMAND"
bind \ec fzf-cd-widget
bind -M insert \ec fzf-cd-widget
end
end

View File

@@ -0,0 +1,15 @@
function license
set -l base_url https://api.github.com/licenses
set -l headers 'Accept: application/vnd.github.drax-preview+json'
if test $argv[1]
set -l license $argv[1]
set -l res (curl --silent --header $headers $base_url/$license | jq .'body')
echo -e $res | sed -e 's/^"//' -e 's/"$//'
else
set -l res (curl --silent --header $headers $base_url)
echo "Available Licenses: "
echo
echo "$res" | jq .[].'key' | sed -e 's/^"//' -e 's/"$//'
end
end

View File

@@ -0,0 +1,48 @@
function replay --description "Run Bash commands replaying changes in Fish"
switch "$argv"
case -v --version
echo "replay, version 1.2.0"
case "" -h --help
echo "Usage: replay <commands> Run Bash commands replaying changes in Fish"
echo "Options:"
echo " -v or --version Print version"
echo " -h or --help Print this help message"
case \*
set --local env
set --local sep @$fish_pid(random)(command date +%s)
set --local argv $argv[1] \"$argv[2..-1]\"
set --local out (command bash -c "
$argv
status=\$?
[ \$status -gt 0 ] && exit \$status
command compgen -e | command awk -v sep=$sep '{
gsub(/\n/, \"\\\n\", ENVIRON[\$0])
print \$0 sep ENVIRON[\$0]
}' && alias
") || return
string replace --all -- \\n \n (
for line in $out
if string split $sep $line | read --local --line name value
set --append env $name
contains -- $name SHLVL PS1 BASH_FUNC || test "$$name" = "$value" && continue
if test "$name" = PATH
string replace --all : " " "set $name $value"
else if test "$name" = PWD
echo builtin cd \"$value\"
else
echo "set --global --export $name "(string escape -- $value)
end
else
set --query env[1] && string match --entire --regex -- "^alias" $line || echo "echo \"$line\""
end
end | string replace --all -- \$ \\\$
for name in (set --export --names)
contains -- $name $env || echo "set --erase $name"
end
) | source
end
end