aboutsummaryrefslogtreecommitdiff
path: root/dot_config/herdr/plugins/github/herdr-splits-d880c4924f2b/lua/herdr-splits/herdr.lua
blob: a5485ad8e3a1602c46b2e9811abd0452761c1e70 (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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
---Herdr CLI wrapper. All Herdr subprocess calls go through this module.
---@class HerdrSplitsHerdr
local M = {}

local config = require('herdr-splits.config')

---Resolve the herdr binary path.
---@return string
function M.herdr_bin()
  return config.herdr_bin or vim.env.HERDR_BIN_PATH or 'herdr'
end

---Check if Neovim is running inside a Herdr session.
---@return boolean
function M.is_in_session()
  return vim.env.HERDR_ENV == '1' and vim.env.HERDR_PANE_ID ~= nil and #vim.env.HERDR_PANE_ID > 0
end

---Get the current Herdr pane ID.
---@return string|nil
function M.current_pane_id()
  if not M.is_in_session() then
    return nil
  end
  return vim.env.HERDR_PANE_ID
end

---Run a herdr CLI command and return stdout, stderr, and exit code.
---@param args string[]
---@return string stdout, string stderr, number exit_code
local function herdr_exec(args)
  local cmd = vim.list_extend({ M.herdr_bin() }, args, 1, #args)
  local obj = vim.system(cmd, { text = true }):wait()
  return obj.stdout, obj.stderr, obj.code
end

---Check if the current Herdr pane is at the layout boundary in the given direction.
---Calls `herdr pane edges --current` and parses the JSON response.
---@param direction '"left"'|'"right"'|'"up"'|'"down"'
---@return boolean|nil true if at edge, false if neighbor exists, nil on error
function M.current_pane_at_edge(direction)
  if not M.is_in_session() then
    return nil
  end

  local edge_key = direction
  local stdout, _, code = herdr_exec({ 'pane', 'edges', '--current' })

  if code ~= 0 or not stdout or #stdout == 0 then
    return nil
  end

  local ok, data = pcall(vim.json.decode, stdout)
  if
    not ok
    or type(data) ~= 'table'
    or type(data.result) ~= 'table'
    or type(data.result.edges) ~= 'table'
  then
    return nil
  end

  local at_edge = data.result.edges[edge_key]
  if type(at_edge) ~= 'boolean' then
    return nil
  end
  return at_edge
end

---Check if the current Herdr pane is zoomed.
---Calls `herdr pane layout --current` and parses JSON.
---@return boolean|nil true if zoomed, false if not, nil on error
function M.current_pane_is_zoomed()
  if not M.is_in_session() then
    return nil
  end

  local stdout, _, code = herdr_exec({ 'pane', 'layout', '--current' })

  if code ~= 0 or not stdout or #stdout == 0 then
    return nil
  end

  local ok, data = pcall(vim.json.decode, stdout)
  if
    not ok
    or type(data) ~= 'table'
    or type(data.result) ~= 'table'
    or type(data.result.layout) ~= 'table'
  then
    return nil
  end

  local zoomed = data.result.layout.zoomed
  if type(zoomed) ~= 'boolean' then
    return nil
  end
  return zoomed
end

---Focus a Herdr pane in the given direction.
---Calls `herdr pane focus --direction <dir> --current`.
---@param direction '"left"'|'"right"'|'"up"'|'"down"'
---@return boolean true on success
function M.focus_pane(direction)
  if not M.is_in_session() then
    return false
  end

  local _, _, code = herdr_exec({ 'pane', 'focus', '--direction', direction, '--current' })
  return code == 0
end

---Unzoom the current Herdr pane (turn off zoom).
---Calls `herdr pane zoom --off --current`.
---@return boolean true on success
function M.unzoom()
  if not M.is_in_session() then
    return false
  end

  local _, _, code = herdr_exec({ 'pane', 'zoom', '--off', '--current' })
  return code == 0
end

---Check whether auto-unzoom is enabled.
---Reads `config.unzoom_on_nav` (resolved from setup(); default: enabled).
---@return boolean
function M.unzoom_enabled()
  return config.unzoom_on_nav ~= false
end

---Edge-wrap behavior across the Herdr pane boundary, read from the resolved
---in-memory config (the same value written to the shared conf that the
---Herdr-side scripts read). `stop` suppresses wrap-across-boundary on both
---the plain-pane side (handled by the Herdr script) and the Neovim
---edge-wrap side (handled here). Anything else defaults to `wrap`.
---@return '"wrap"'|'"stop"'
function M.nav_at_edge()
  return config.nav_at_edge == 'stop' and 'stop' or 'wrap'
end

---Resize the current Herdr pane in the given direction.
---Calls `herdr pane resize --direction <dir> --amount <float> --current`.
---@param direction '"left"'|'"right"'|'"up"'|'"down"'
---@param amount number Float ratio (e.g., 0.03 = 3% of terminal dimension)
---@return boolean true on success
function M.resize_pane(direction, amount)
  if not M.is_in_session() then
    return false
  end

  local amount_str = string.format('%.4f', amount)
  local _, _, code = herdr_exec({
    'pane', 'resize', '--direction', direction, '--amount', amount_str, '--current',
  })
  return code == 0
end

return M