aboutsummaryrefslogtreecommitdiff
path: root/dot_config/hypr/scripts/executable_herdr-smart-nav
blob: 36d27aae6b0e7ff5789c6aa6290bc95a0b9a717e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
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