From 3368c8149ffdd2b84be4adef2d35aa6301209748 Mon Sep 17 00:00:00 2001 From: Peder Bergebakken Sundt Date: Fri, 21 Mar 2025 20:06:27 +0100 Subject: [PATCH] lru --- .../home/profiles/bashrc.d/zeditor-remote.sh | 174 +++++++++++------- 1 file changed, 110 insertions(+), 64 deletions(-) diff --git a/users/pbsds/home/profiles/bashrc.d/zeditor-remote.sh b/users/pbsds/home/profiles/bashrc.d/zeditor-remote.sh index a407aaa..3e0eda7 100644 --- a/users/pbsds/home/profiles/bashrc.d/zeditor-remote.sh +++ b/users/pbsds/home/profiles/bashrc.d/zeditor-remote.sh @@ -1,109 +1,150 @@ zeditor-remote() { - ensure() { - local error - for name in "$@"; do - if ! command -v "${name}" >/dev/null; then - printf >&2 '%s\n' "ERROR: '$name' not found in PATH" - error=1 - fi - done - if [[ -n "$error" ]]; then - return 1 - fi - } - ensure xe fd rg fzf gum || return 1 + local prefix="${1:-repos}" + local statedir="${XDG_STATE_HOME:-"$HOME/.local/state/"}/zeditor-remote-sh" - local spin="" - # doesn't forward stdin - if command -v gum >/dev/null; then - if printf '%s\n' "$(gum --version)" "gum version 0.15.1" | sort -C -V; then - : # older version don't forward stdin - else - spin="gum spin --show-output --show-error --" - fi + local -a missing=() + command -v xe >/dev/null || missing+=('xe') + command -v fd >/dev/null || missing+=('fd') + command -v rg >/dev/null || missing+=('rg') + command -v fzf >/dev/null || missing+=('fzf') + command -v gum >/dev/null || missing+=('gum') + if [[ "${#missing[@]}" -gt 0 ]]; then + printf >&2 "%s\n" "ERROR: Not found in PATH:$(printf " %s" "${missing[@]}")" + return 1 fi - - local host=$( - { - printf '%s\n' localhost - { - # ask tailscale - if command -v tailscale >/dev/null; then - tailscale status --json | jq .Peer[].HostName -r; - fi - # search .ssh/known_hosts, strip DNS search domains to deduplicate - if [[ -r /etc/resolv.conf && -r "$HOME"/.ssh/known_hosts ]]; then - local -a hosts - readarray -d $'\n' -t hosts < <( - cut <"$HOME"/.ssh/known_hosts -d' ' -f1 | sort -u \ - | grep -vE '^\[' \ - | grep -v '[,@]' \ - | grep -Ev '^([0-9]{0,3}\.){3}[0-9]{0,3}$' \ - | grep -Ev '^([0-9a-fA-F]{0,4}:){0,7}:?([0-9a-fA-F]{0,4}:){0,6}[0-9a-fA-F]{0,4}$' - ) - local -a domains - readarray -d ' ' -t domains < <( - grep &2 "%s\n" "ERROR: $HOME is not set" return 1 fi + # make $prefix relative to $HOME + if [[ "$prefix" =~ ^/ ]]; then + prefix=${prefix//"$HOME/"/} + fi + if [[ "$prefix" =~ ^/ ]]; then + printf >&2 "%s\n" "ERROR: prefix not in \$HOME ($prefix)" + return 1 + fi + + uniq-stable() ( + command cat -n | sort -b --key=2.1 -u | sort -n | cut -f2- + ) + + local hosts=$( + { + printf '%s\n' localhost + { + if [[ -s "$statedir"/ssh-host-history ]]; then + cat "$statedir"/ssh-host-history + fi + + # ask tailscale + if command -v tailscale >/dev/null; then + tailscale status --json | jq .Peer[].HostName -r + fi + + # search .ssh/known_hosts, strip DNS search domains to deduplicate + if [[ -r /etc/resolv.conf && -r "$HOME"/.ssh/known_hosts ]]; then + local -a hosts + readarray -d $'\n' -t hosts < <( + cut <"$HOME"/.ssh/known_hosts -d' ' -f1 | sort -u \ + | grep -vE '^\[' \ + | grep -v '[,@]' \ + | grep -Ev '^([0-9]{0,3}\.){3}[0-9]{0,3}$' \ + | grep -Ev '^([0-9a-fA-F]{0,4}:){0,7}:?([0-9a-fA-F]{0,4}:){0,6}[0-9a-fA-F]{0,4}$' + ) + local -a domains + readarray -d ' ' -t domains < <( + grep /dev/null "$host" \ - fd "^\.git$" repos/ "${fdargs[@]}" -L -x printf "'ssh://%s/~/%q\n'" "'$host'" "'{//}'" + fd "^\.git$" "'$prefix/'" "${fdargs[@]}" -L -x printf "'ssh://%s/~/%q\n'" "'$host'" "'{//}'" fi } - $spin xe <<<"$host" -j"$(wc -l <<<"$host")" -s "$(declare -f worker); worker \"\$@\"" 2>/dev/null ||: + "${spin[@]}" xe -f <(printf "%s\n" "$hosts") -j"$(wc -l <<<"$hosts")" -s "$(declare -f worker); worker \"\$@\"" ||: ) if [[ -z "${repos}" ]]; then printf >&2 '%s\n' "ERROR: no repos found!" return 1 fi - local choice=$(sort <<<"$repos" | fzf --reverse --cycle --height=25) + local choice=$( + { + if [[ -s "$statedir"/ssh-repo-history ]]; then + grep <"$statedir"/ssh-repo-history "$(printf "^ssh://%s/\n" "${hosts[@]}")" + fi + sort <<<"$repos" + } | uniq-stable | fzf -sync --reverse --cycle --height=25) if [[ -z "${choice}" ]]; then return fi local zed_cmd="zeditor ${choice}" + local host=$( + rg <<<"$choice" '^ssh://([^/]*)/(~/.*)$' -r '$1' || printf "%s\n" localhost + ) local ssh_cd_cmd=$( rg <<<"$choice" '^ssh://([^/]*)/(~/.*)$' -r 'ssh -t "$1" "cd $2; \\$$SHELL -l"' ||: ) + # TODO: evict + lru_push() { + local line="$1" + local fname="$2" + mkdir -p "$(dirname "$fname")" + local new_lru=$( + printf "%s\n" "$line" + [[ -s "$fname" ]] && cat "$fname" + ) + uniq-stable <<<"$new_lru" >"$fname" + } + if [[ -n "$ssh_cd_cmd" ]]; then - local do_ssh="" + local do_ssh=false if gum confirm "SSH in there now?" --default=yes; then - do_ssh=1 + do_ssh=true + elif [[ "$?" -eq 130 ]]; then + return 1 fi - # history -s "${zed_cmd}" + + lru_push "$host" "$statedir"/ssh-host-history + lru_push "$choice" "$statedir"/ssh-repo-history + + # history -s "${zed_cmd}" # TODO: this too? eval ${zed_cmd} # TODO: find a way to not push the rest of the unpushed history history -s "${ssh_cd_cmd}" history -a - if [[ -n "$do_ssh" ]]; then + if $do_ssh; then printf >&2 "%s\n" "+ ${ssh_cd_cmd}" eval ${ssh_cd_cmd} fi @@ -116,8 +157,13 @@ zeditor-remote() { # https://github.com/zed-industries/zed/issues/4977#issuecomment-2536680602 zed_cmd="direnv exec ${choice} $zed_cmd" fi + + # lru_push "$host" "$statedir"/ssh-host-history + lru_push "$choice" "$statedir"/ssh-repo-history + # history -s "${zed_cmd}" eval ${zed_cmd} + printf >&2 "%s\n" "+$(printf " %q" cd "${choice}")" eval cd "${choice}" # 'eval' to expand the tilde fi