Super supe up my fish with fzf. Batteries are now included.
This commit is contained in:
parent
4778b198d2
commit
78e80ba3d4
|
|
@ -0,0 +1,106 @@
|
|||
# From https://github.com/patrickf3139/fzf.fish/blob/main/conf.d/fzf.fish
|
||||
# Set up the default, mnemonic key bindings unless the user has chosen to customize them
|
||||
if not set --query fzf_fish_custom_keybindings
|
||||
# \cf is Ctrl+f
|
||||
bind \cj '__fzf_search_current_dir'
|
||||
#bind \cr '__fzf_search_history'
|
||||
bind \cv '__fzf_search_shell_variables'
|
||||
# The following two key binding use Alt as an additional modifier key to avoid conflicts
|
||||
bind \e\cl '__fzf_search_git_log'
|
||||
bind \e\cs '__fzf_search_git_status'
|
||||
|
||||
# set up the same key bindings for insert mode if using fish_vi_key_bindings
|
||||
if [ "$fish_key_bindings" = 'fish_vi_key_bindings' ]
|
||||
bind --mode insert \cf '__fzf_search_current_dir'
|
||||
bind --mode insert \cr '__fzf_search_history'
|
||||
bind --mode insert \cv '__fzf_search_shell_variables'
|
||||
bind --mode insert \e\cl '__fzf_search_git_log'
|
||||
bind --mode insert \e\cs '__fzf_search_git_status'
|
||||
end
|
||||
end
|
||||
|
||||
# If FZF_DEFAULT_OPTS is not set, then set some sane defaults. This also affects fzf outside of this plugin.
|
||||
# See https://github.com/junegunn/fzf#environment-variables
|
||||
if not set --query FZF_DEFAULT_OPTS
|
||||
# cycle makes scrolling easier
|
||||
# reverse layout is more familiar as it mimicks the layout of git log, history, and env
|
||||
# border makes clear where the fzf window ends
|
||||
# height 75% allows you to view what you were doing and stay in context of your work
|
||||
# preview-window wrap wraps long lines in the preview window
|
||||
set --export FZF_DEFAULT_OPTS '--cycle --layout=reverse --border --height 75% --preview-window=wrap'
|
||||
end
|
||||
|
||||
|
||||
# Forgit configuration
|
||||
set FORGIT_FZF_DEFAULT_OPTS "
|
||||
$FZF_DEFAULT_OPTS
|
||||
--ansi
|
||||
--height='80%'
|
||||
--bind='alt-k:preview-up,alt-p:preview-up'
|
||||
--bind='alt-j:preview-down,alt-n:preview-down'
|
||||
--bind='ctrl-r:toggle-all'
|
||||
--bind='ctrl-s:toggle-sort'
|
||||
--bind='?:toggle-preview'
|
||||
--bind='alt-w:toggle-preview-wrap'
|
||||
--preview-window='right:60%'
|
||||
+1
|
||||
$FORGIT_FZF_DEFAULT_OPTS
|
||||
"
|
||||
|
||||
# register aliases
|
||||
if test -z "$FORGIT_NO_ALIASES"
|
||||
if test -n "$forgit_add"
|
||||
alias $forgit_add 'forgit::add'
|
||||
else
|
||||
alias ga 'forgit::add'
|
||||
end
|
||||
|
||||
if test -n "$forgit_reset_head"
|
||||
alias $forgit_reset_head 'forgit::reset::head'
|
||||
else
|
||||
alias grh 'forgit::reset::head'
|
||||
end
|
||||
|
||||
if test -n "$forgit_log"
|
||||
alias $forgit_log 'forgit::log'
|
||||
else
|
||||
alias glo 'forgit::log'
|
||||
end
|
||||
|
||||
if test -n "$forgit_diff"
|
||||
alias $forgit_diff 'forgit::diff'
|
||||
else
|
||||
alias gd 'forgit::diff'
|
||||
end
|
||||
|
||||
if test -n "$forgit_ignore"
|
||||
alias $forgit_ignore 'forgit::ignore'
|
||||
else
|
||||
alias gi 'forgit::ignore'
|
||||
end
|
||||
|
||||
if test -n "$forgit_restore"
|
||||
alias $forgit_restore 'forgit::checkout_file'
|
||||
else
|
||||
alias gcf 'forgit::checkout_file'
|
||||
end
|
||||
|
||||
if test -n "$forgit_clean"
|
||||
alias $forgit_clean 'forgit::clean'
|
||||
else
|
||||
alias gclean 'forgit::clean'
|
||||
end
|
||||
|
||||
if test -n "$forgit_stash_show"
|
||||
alias $forgit_stash_show 'forgit::stash::show'
|
||||
else
|
||||
alias gss 'forgit::stash::show'
|
||||
end
|
||||
|
||||
if test -n "$forgit_cherry_pick"
|
||||
alias $forgit_cherry_pick 'forgit::cherry::pick'
|
||||
else
|
||||
alias gcp 'forgit::cherry::pick'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -51,4 +51,4 @@ bind \ea beginning-of-line
|
|||
#-------------------------------------------------------------------------------
|
||||
# Forgit plugin requires sourcing to activate abbreviations
|
||||
# From https://github.com/wfxr/forgit
|
||||
source ./functions/forgit.plugin.fish
|
||||
source ~/.config/fish/functions/forgit.plugin.fish
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
# helper function for __fzf_search_shell_variables
|
||||
function __fzf_display_value_or_error --argument-names variable_name --description "Displays either the value of the variable passed in, or an informative message if it is not available."
|
||||
if set --query $variable_name
|
||||
echo $$variable_name
|
||||
else
|
||||
set_color red
|
||||
echo "$variable_name was not exported to this process so its value cannot be displayed." >&2
|
||||
set_color normal
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
# helper function for __fzf_search_current_dir
|
||||
function __fzf_preview_file --argument-names file_path --description "Prints a preview for the given file based on its file type."
|
||||
if test -f "$file_path" # regular file
|
||||
bat --style=numbers --color=always "$file_path"
|
||||
else if test -d "$file_path" # directory
|
||||
set --local CLICOLOR_FORCE true
|
||||
ls -a "$file_path"
|
||||
else 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 -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 "Unexpected file symbol $file_type_char. Please open an issue at https://github.com/patrickf3139/fzf.fish." >&2
|
||||
end
|
||||
end
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# originally implemented and transposed from https://github.com/patrickf3139/dotfiles/pull/11
|
||||
function __fzf_search_current_dir --description "Search the current directory using fzf and fd. Insert the selected relative file path into the commandline at the cursor."
|
||||
# Make sure that fzf uses fish to execute __fzf_preview_file.
|
||||
# See similar comment in __fzf_search_shell_variables.fish.
|
||||
set --local --export SHELL (command --search fish)
|
||||
set file_path_selected (
|
||||
fd --hidden --follow --color=always --exclude=.git 2> /dev/null |
|
||||
fzf --ansi --preview='__fzf_preview_file {}'
|
||||
)
|
||||
|
||||
if test $status -eq 0
|
||||
commandline --insert (echo $file_path_selected | string escape)
|
||||
end
|
||||
|
||||
commandline --function repaint
|
||||
end
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# Originally implemented in and transposed from https://github.com/patrickf3139/dotfiles/pull/2
|
||||
function __fzf_search_git_log --description "Search the git log of the current git repository. Insert the selected commit hash into the commandline at the cursor."
|
||||
if not git rev-parse --git-dir >/dev/null 2>&1
|
||||
echo '__fzf_search_git_log: Not in a git repository.' >&2
|
||||
else
|
||||
set selected_log_line (
|
||||
# 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
|
||||
git log --color=always --format=format:'%C(bold blue)%h%C(reset) - %C(cyan)%as%C(reset) %C(yellow)%d%C(reset) %C(normal)%s%C(reset) %C(dim normal)[%an]%C(reset)' | \
|
||||
fzf --ansi --tiebreak=index --preview='git show --color=always (string split --max 1 " " {})[1]'
|
||||
)
|
||||
if test $status -eq 0
|
||||
set abbreviated_commit_hash (string split --max 1 " " $selected_log_line)[1]
|
||||
set commit_hash (git rev-parse $abbreviated_commit_hash)
|
||||
commandline --insert $commit_hash
|
||||
end
|
||||
end
|
||||
|
||||
commandline --function repaint
|
||||
end
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
function __fzf_search_git_status --description "Search the git status of the current git repository. Insert the selected file paths into the commandline at the cursor."
|
||||
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 --ansi --multi
|
||||
)
|
||||
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")
|
||||
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 cleaned_path (string split -- "-> " $path)[-1]
|
||||
else
|
||||
set cleaned_path (string sub --start=4 $path)
|
||||
end
|
||||
# add a space after each path to keep them separated when inserted
|
||||
set cleaned_path_padded "$cleaned_path "
|
||||
commandline --insert $cleaned_path_padded
|
||||
end
|
||||
end
|
||||
|
||||
commandline --function repaint
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# originally implemented and transposed from https://github.com/patrickf3139/dotfiles/pull/11
|
||||
function __fzf_search_history --description "Search command history using fzf. Replace the commandline with the selected command."
|
||||
# history merge incorporates history changes from other fish sessions
|
||||
history merge
|
||||
set command_with_ts (
|
||||
# Reference https://devhints.io/strftime to understand strftime format symbols
|
||||
history --null --show-time="%m/%e %H:%M:%S | " |
|
||||
fzf --read0 --tiebreak=index --query=(commandline)
|
||||
)
|
||||
|
||||
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
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
function __fzf_search_shell_variables --description "Search and inspect shell variables using fzf. Insert the selected variable into the commandline at the cursor."
|
||||
# Make sure that fzf uses fish to execute __echo_value_or_print_message, which
|
||||
# is an autoloaded fish function so doesn't exist in other shells.
|
||||
# Using --local so that it does not clobber SHELL outside of this function.
|
||||
set --local --export SHELL (command --search fish)
|
||||
|
||||
# Pipe the names of all shell variables to fzf and attempt to display the value
|
||||
# of the selected variable in fzf's preview window.
|
||||
# Non-exported variables will not be accessible to the fzf process, in which case
|
||||
# __echo_value_or_print_message will print an informative message in lieu of the value.
|
||||
set variable_name (
|
||||
set --names |
|
||||
fzf --preview '__fzf_display_value_or_error {}'
|
||||
)
|
||||
|
||||
if test $status -eq 0
|
||||
commandline --insert $variable_name
|
||||
end
|
||||
|
||||
commandline --function repaint
|
||||
end
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# Defined in /tmp/fish.azx9vq/__github_add_org.fish @ line 1
|
||||
function __github_add_org --description 'Add an organization name to the local share fzf file.' --argument org
|
||||
if ! test -n "$org"
|
||||
echo "Please provide and org name!"
|
||||
return 1
|
||||
else
|
||||
echo $org >> ~/.local/share/fzf/github_orgs
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# Defined in /tmp/fish.mAOD6Y/__github_get_org_repos.fish @ line 2
|
||||
function __github_get_org_repos --description 'Return a list of all the github repos owned by a github organization.' --argument org
|
||||
if ! test -n "$org"
|
||||
echo "Please provide a github organization name."
|
||||
return 1
|
||||
else
|
||||
curl "https://api.github.com/orgs/$org/repos?per_page=100&page=1" | jq '.[].full_name' | awk -F'"' '{print $2}'
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
function -d "Fuzzy change directory" fcd
|
||||
if set -q argv[1]
|
||||
set searchdir $argv[1]
|
||||
else
|
||||
set searchdir $HOME
|
||||
end
|
||||
|
||||
# https://github.com/fish-shell/fish-shell/issues/1362
|
||||
set -l tmpfile (mktemp)
|
||||
find $searchdir \( ! -regex '.*/\..*' \) ! -name __pycache__ -type d | fzf > $tmpfile
|
||||
set -l destdir (cat $tmpfile)
|
||||
rm -f $tmpfile
|
||||
|
||||
if test -z "$destdir"
|
||||
return 1
|
||||
end
|
||||
|
||||
cd $destdir
|
||||
end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# Defined in /tmp/fish.yoQzI0/fclone.fish @ line 2
|
||||
function fclone --argument org
|
||||
if ! test -n "$org"
|
||||
set org_file ~/.local/share/fzf/github_orgs
|
||||
if ! test -e $org_file
|
||||
echo -n "Please provide an organization name either as an argument to this command or in a list at $org_file"
|
||||
return 1
|
||||
else
|
||||
set org (cat $org_file | fzf-tmux -e --header="Please select an organization. Set additional orgs in $org_file")
|
||||
end
|
||||
end
|
||||
set repo (__github_get_org_repos $org | fzf-tmux --header="Please select a repository to clone.")
|
||||
if test -n "$repo"
|
||||
echo "Cloning '$repo' from Github"
|
||||
git clone "https://github.com/$repo.git"
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# Defined in /tmp/fish.T4Z5Kp/fclone.fish @ line 2
|
||||
function fhub --argument org
|
||||
if ! test -n "$org"
|
||||
set org_file ~/.local/share/fzf/github_orgs
|
||||
if ! test -e $org_file
|
||||
echo -n "Please provide an organization name either as an argument to this command or in a list at $org_file"
|
||||
return 1
|
||||
else
|
||||
set org (cat $org_file | fzf-tmux -e --header="Please select an organization. Set additional orgs in $org_file")
|
||||
end
|
||||
end
|
||||
set repo (__github_get_org_repos $org | fzf-tmux --header="Please select a repository to clone.")
|
||||
if test -n "$repo"
|
||||
echo "Opening '$repo' in Web Browser"
|
||||
hub browse $repo
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
function fkill -d "Fuzzy kill"
|
||||
set pid (ps -ef | sed 1d | fzf -m | awk '{print $2}')
|
||||
|
||||
if test -n "$pid"
|
||||
echo $pid | xargs kill -9
|
||||
end
|
||||
end
|
||||
|
|
@ -297,76 +297,3 @@ function forgit::ignore::clean
|
|||
setopt localoptions rmstarsilent
|
||||
[[ -d "$FORGIT_GI_REPO_LOCAL" ]] && rm -rf "$FORGIT_GI_REPO_LOCAL"
|
||||
end
|
||||
|
||||
set FORGIT_FZF_DEFAULT_OPTS "
|
||||
$FZF_DEFAULT_OPTS
|
||||
--ansi
|
||||
--height='80%'
|
||||
--bind='alt-k:preview-up,alt-p:preview-up'
|
||||
--bind='alt-j:preview-down,alt-n:preview-down'
|
||||
--bind='ctrl-r:toggle-all'
|
||||
--bind='ctrl-s:toggle-sort'
|
||||
--bind='?:toggle-preview'
|
||||
--bind='alt-w:toggle-preview-wrap'
|
||||
--preview-window='right:60%'
|
||||
+1
|
||||
$FORGIT_FZF_DEFAULT_OPTS
|
||||
"
|
||||
|
||||
# register aliases
|
||||
if test -z "$FORGIT_NO_ALIASES"
|
||||
if test -n "$forgit_add"
|
||||
alias $forgit_add 'forgit::add'
|
||||
else
|
||||
alias ga 'forgit::add'
|
||||
end
|
||||
|
||||
if test -n "$forgit_reset_head"
|
||||
alias $forgit_reset_head 'forgit::reset::head'
|
||||
else
|
||||
alias grh 'forgit::reset::head'
|
||||
end
|
||||
|
||||
if test -n "$forgit_log"
|
||||
alias $forgit_log 'forgit::log'
|
||||
else
|
||||
alias glo 'forgit::log'
|
||||
end
|
||||
|
||||
if test -n "$forgit_diff"
|
||||
alias $forgit_diff 'forgit::diff'
|
||||
else
|
||||
alias gd 'forgit::diff'
|
||||
end
|
||||
|
||||
if test -n "$forgit_ignore"
|
||||
alias $forgit_ignore 'forgit::ignore'
|
||||
else
|
||||
alias gi 'forgit::ignore'
|
||||
end
|
||||
|
||||
if test -n "$forgit_restore"
|
||||
alias $forgit_restore 'forgit::checkout_file'
|
||||
else
|
||||
alias gcf 'forgit::checkout_file'
|
||||
end
|
||||
|
||||
if test -n "$forgit_clean"
|
||||
alias $forgit_clean 'forgit::clean'
|
||||
else
|
||||
alias gclean 'forgit::clean'
|
||||
end
|
||||
|
||||
if test -n "$forgit_stash_show"
|
||||
alias $forgit_stash_show 'forgit::stash::show'
|
||||
else
|
||||
alias gss 'forgit::stash::show'
|
||||
end
|
||||
|
||||
if test -n "$forgit_cherry_pick"
|
||||
alias $forgit_cherry_pick 'forgit::cherry::pick'
|
||||
else
|
||||
alias gcp 'forgit::cherry::pick'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
# https://github.com/junegunn/fzf/wiki/Examples-(fish)
|
||||
function fzf-bcd-widget -d 'cd backwards'
|
||||
pwd | awk -v RS=/ '/\n/ {exit} {p=p $0 "/"; print p}' | tac | eval (__fzfcmd) +m --select-1 --exit-0 $FZF_BCD_OPTS | read -l result
|
||||
[ "$result" ]; and cd $result
|
||||
commandline -f repaint
|
||||
end
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# From https://github.com/junegunn/fzf/wiki/Examples-(fish)
|
||||
function fzf-cdhist-widget -d 'cd to one of the previously visited locations'
|
||||
# Clear non-existent folders from cdhist.
|
||||
set -l buf
|
||||
for i in (seq 1 (count $dirprev))
|
||||
set -l dir $dirprev[$i]
|
||||
if test -d $dir
|
||||
set buf $buf $dir
|
||||
end
|
||||
end
|
||||
set dirprev $buf
|
||||
string join \n $dirprev | tac | sed 1d | eval (__fzfcmd) +m --tiebreak=index --toggle-sort=ctrl-r $FZF_CDHIST_OPTS | read -l result
|
||||
[ "$result" ]; and cd $result
|
||||
commandline -f repaint
|
||||
end
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
# From https://github.com/junegunn/fzf/wiki/Examples-(fish)
|
||||
function fzf-complete -d 'fzf completion and print selection back to commandline'
|
||||
# As of 2.6, fish's "complete" function does not understand
|
||||
# subcommands. Instead, we use the same hack as __fish_complete_subcommand and
|
||||
# extract the subcommand manually.
|
||||
set -l cmd (commandline -co) (commandline -ct)
|
||||
switch $cmd[1]
|
||||
case env sudo
|
||||
for i in (seq 2 (count $cmd))
|
||||
switch $cmd[$i]
|
||||
case '-*'
|
||||
case '*=*'
|
||||
case '*'
|
||||
set cmd $cmd[$i..-1]
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
set cmd (string join -- ' ' $cmd)
|
||||
|
||||
set -l complist (complete -C$cmd)
|
||||
set -l result
|
||||
string join -- \n $complist | sort | eval (__fzfcmd) -m --select-1 --exit-0 --header '(commandline)' | cut -f1 | while read -l r; set result $result $r; end
|
||||
|
||||
set prefix (string sub -s 1 -l 1 -- (commandline -t))
|
||||
for i in (seq (count $result))
|
||||
set -l r $result[$i]
|
||||
switch $prefix
|
||||
case "'"
|
||||
commandline -t -- (string escape -- $r)
|
||||
case '"'
|
||||
if string match '*"*' -- $r >/dev/null
|
||||
commandline -t -- (string escape -- $r)
|
||||
else
|
||||
commandline -t -- '"'$r'"'
|
||||
end
|
||||
case '~'
|
||||
commandline -t -- (string sub -s 2 (string escape -n -- $r))
|
||||
case '*'
|
||||
commandline -t -- (string escape -n -- $r)
|
||||
end
|
||||
[ $i -lt (count $result) ]; and commandline -i ' '
|
||||
end
|
||||
|
||||
commandline -f repaint
|
||||
end
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# From https://github.com/junegunn/fzf/wiki/Examples-(fish)
|
||||
function fzf-select -d 'fzf commandline job and print unescaped selection back to commandline'
|
||||
set -l cmd (commandline -j)
|
||||
[ "$cmd" ]; or return
|
||||
eval $cmd | eval (__fzfcmd) -m --tiebreak=index --select-1 --exit-0 | string join ' ' | read -l result
|
||||
[ "$result" ]; and commandline -j -- $result
|
||||
commandline -f repaint
|
||||
end
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env fish
|
||||
if not set --query fzf_fish_custom_keybindings
|
||||
bind --erase --all \cf
|
||||
bind --erase --all \cr
|
||||
bind --erase --all \cv
|
||||
bind --erase --all \e\cl
|
||||
bind --erase --all \e\cs
|
||||
|
||||
set_color --italics cyan
|
||||
echo "fzf.fish key bindings removed"
|
||||
set_color normal
|
||||
end
|
||||
|
||||
# Not going to erase FZF_DEFAULT_OPTS because too hard to tell if it was set by the user or by this plugin
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
longtailfinancial
|
||||
holoviz
|
||||
longtailfinancial
|
||||
Loading…
Reference in New Issue