diff options
Diffstat (limited to 'dot_config/hypr/scripts/executable_herdr-smart-nav')
| -rw-r--r-- | dot_config/hypr/scripts/executable_herdr-smart-nav | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/dot_config/hypr/scripts/executable_herdr-smart-nav b/dot_config/hypr/scripts/executable_herdr-smart-nav new file mode 100644 index 0000000..36d27aa --- /dev/null +++ b/dot_config/hypr/scripts/executable_herdr-smart-nav @@ -0,0 +1,143 @@ +#!/usr/bin/env bash + +set -u + +direction="${1:-}" +terminal_pid="${2:-}" + +case "$direction" in + left|right|up|down) + ;; + *) + printf 'usage: %s <left|right|up|down> <terminal-pid>\n' "$0" >&2 + exit 2 + ;; +esac + +# Window titles can change with the workspace. Find a foreground Herdr +# process belonging to this terminal instead of relying on its title. +is_in_terminal() { + local pid="$1" + local parent + + while [[ "$pid" =~ ^[0-9]+$ && "$pid" -gt 1 ]]; do + [[ "$pid" == "$terminal_pid" ]] && return 0 + parent="$(ps -o ppid= -p "$pid" 2>/dev/null | tr -d '[:space:]')" + [[ -n "$parent" && "$parent" != "$pid" ]] || return 1 + pid="$parent" + done + + return 1 +} + +herdr_found=false +if [[ "$terminal_pid" =~ ^[0-9]+$ ]]; then + while read -r pid process_group foreground_group; do + # The server has no foreground terminal; a suspended client is not active. + if [[ "$foreground_group" -gt 0 && "$process_group" == "$foreground_group" ]] && + is_in_terminal "$pid"; then + herdr_found=true + break + fi + done < <(ps -C herdr -o pid=,pgid=,tpgid= 2>/dev/null) +fi + +if [[ "$herdr_found" != true ]]; then + printf 'not-herdr\n' + exit +fi + +herdr_bin="${HERDR_BIN_PATH:-$HOME/.local/share/mise/installs/herdr/latest/herdr}" + +if [[ ! -x "$herdr_bin" ]]; then + printf 'hyprland\n' + exit +fi + +handled() { + printf 'handled\n' + exit +} + +fall_through() { + printf 'hyprland\n' + exit +} + +current="$("$herdr_bin" pane current --current 2>/dev/null)" || { + fall_through +} + +pane_id="$(jq -r '.result.pane.pane_id // empty' <<<"$current")" + +if [[ -z "$pane_id" ]]; then + fall_through +fi + +process_info="$( + "$herdr_bin" pane process-info --pane "$pane_id" 2>/dev/null || true +)" + +nvim_pid="$( + jq -r ' + [ + (.result.process_info.foreground_processes // [])[] + | select( + ((.name // "") | ascii_downcase) == "nvim" + or ((.name // "") | ascii_downcase) == "vim" + ) + | .pid + ][0] // empty + ' <<<"$process_info" 2>/dev/null +)" + +if [[ -n "$nvim_pid" ]]; then + safe_pane_id="${pane_id//[^a-zA-Z0-9_.-]/_}" + runtime_dir="${XDG_RUNTIME_DIR:-/tmp}" + + socket_path="$runtime_dir/nvim-hypr-nav-pane-$safe_pane_id.sock" + + response="$( + printf 'nav:%s\n' "$direction" | + socat -T 0.25 -,ignoreeof "UNIX-CONNECT:$socket_path" 2>/dev/null || + true + )" + + if [[ "$response" == *success* ]]; then + handled + fi +fi + +neighbor="$( + "$herdr_bin" pane neighbor \ + --pane "$pane_id" \ + --direction "$direction" \ + 2>/dev/null +)" || { + fall_through +} + +# A zoomed pane deliberately falls through to Hyprland. Do not expose or +# navigate to another Herdr pane. +if jq -e '.result.neighbor.layout.zoomed == true' \ + >/dev/null 2>&1 <<<"$neighbor"; then + fall_through +fi + +neighbor_id="$( + jq -r '.result.neighbor.neighbor_pane_id // empty' <<<"$neighbor" +)" + +# Herdr 0.7.5 returns the current pane itself when there is no neighbour. +if [[ -z "$neighbor_id" || "$neighbor_id" == "$pane_id" ]]; then + fall_through +fi + +if "$herdr_bin" pane focus \ + --pane "$pane_id" \ + --direction "$direction" \ + >/dev/null 2>&1; then + handled +else + fall_through +fi |
