|
1 | | -local config = require("peekstack.config") |
2 | 1 | local keymaps = require("peekstack.ui.stack_view.keymaps") |
3 | 2 | local renderer = require("peekstack.ui.stack_view.render") |
| 3 | +local state = require("peekstack.ui.stack_view.state") |
| 4 | +local window = require("peekstack.ui.stack_view.window") |
4 | 5 |
|
5 | 6 | local M = {} |
6 | 7 |
|
7 | | ----@type table<integer, PeekstackStackViewState> |
8 | | -local states = {} |
9 | | ----@type integer? |
10 | | -local tab_cleanup_group = nil |
11 | | - |
12 | | ----@param s PeekstackStackViewState |
13 | | -local function cleanup_state(s) |
14 | | - if s.help_winid and vim.api.nvim_win_is_valid(s.help_winid) then |
15 | | - pcall(vim.api.nvim_win_close, s.help_winid, true) |
16 | | - end |
17 | | - s.help_winid = nil |
18 | | - s.help_bufnr = nil |
19 | | - |
20 | | - if s.help_augroup then |
21 | | - pcall(vim.api.nvim_del_augroup_by_id, s.help_augroup) |
22 | | - s.help_augroup = nil |
23 | | - end |
24 | | - |
25 | | - if s.autoclose_group then |
26 | | - pcall(vim.api.nvim_del_augroup_by_id, s.autoclose_group) |
27 | | - s.autoclose_group = nil |
28 | | - end |
29 | | -end |
30 | | - |
31 | | -local function cleanup_invalid_states() |
32 | | - for tabpage, s in pairs(states) do |
33 | | - if not vim.api.nvim_tabpage_is_valid(tabpage) then |
34 | | - cleanup_state(s) |
35 | | - states[tabpage] = nil |
36 | | - end |
37 | | - end |
38 | | -end |
39 | | - |
40 | | ----Setup stack view autocmds. |
41 | | -function M.setup() |
42 | | - if tab_cleanup_group then |
43 | | - return |
44 | | - end |
45 | | - |
46 | | - tab_cleanup_group = vim.api.nvim_create_augroup("PeekstackStackViewTabCleanup", { clear = true }) |
47 | | - vim.api.nvim_create_autocmd("TabClosed", { |
48 | | - group = tab_cleanup_group, |
49 | | - callback = function() |
50 | | - cleanup_invalid_states() |
51 | | - end, |
52 | | - }) |
53 | | -end |
54 | | - |
55 | | ----@return PeekstackStackViewState |
56 | | -local function get_state() |
57 | | - local tabpage = vim.api.nvim_get_current_tabpage() |
58 | | - if not states[tabpage] then |
59 | | - states[tabpage] = { |
60 | | - bufnr = nil, |
61 | | - winid = nil, |
62 | | - root_winid = nil, |
63 | | - line_to_id = {}, |
64 | | - render_keys = {}, |
65 | | - filter = nil, |
66 | | - header_lines = 0, |
67 | | - help_bufnr = nil, |
68 | | - help_winid = nil, |
69 | | - help_augroup = nil, |
70 | | - autoclose_group = nil, |
71 | | - autoclose_suspended = 0, |
72 | | - preview_ts_cache = {}, |
73 | | - } |
74 | | - end |
75 | | - return states[tabpage] |
76 | | -end |
77 | | - |
78 | | ----@param s PeekstackStackViewState |
79 | | ----@return boolean |
80 | | -local function is_open(s) |
81 | | - return s.winid ~= nil and vim.api.nvim_win_is_valid(s.winid) |
82 | | -end |
83 | | - |
84 | | ----@param s PeekstackStackViewState |
85 | | ----@return boolean |
86 | | -local function is_ready(s) |
87 | | - return s.bufnr ~= nil and s.winid ~= nil and vim.api.nvim_buf_is_valid(s.bufnr) and vim.api.nvim_win_is_valid(s.winid) |
88 | | -end |
89 | | - |
90 | | ----@param s PeekstackStackViewState |
91 | | -local function focus_stack_view(s) |
92 | | - if s.winid and vim.api.nvim_win_is_valid(s.winid) then |
93 | | - vim.api.nvim_set_current_win(s.winid) |
94 | | - end |
95 | | -end |
96 | | - |
97 | | ----@param s PeekstackStackViewState |
98 | | ----@return boolean |
99 | | -local function should_autoclose(s) |
100 | | - if s.autoclose_suspended and s.autoclose_suspended > 0 then |
101 | | - return false |
102 | | - end |
103 | | - if not is_open(s) then |
104 | | - return false |
105 | | - end |
106 | | - |
107 | | - local current = vim.api.nvim_get_current_win() |
108 | | - if s.winid and current == s.winid then |
109 | | - return false |
110 | | - end |
111 | | - if s.help_winid and vim.api.nvim_win_is_valid(s.help_winid) and current == s.help_winid then |
112 | | - return false |
113 | | - end |
114 | | - |
115 | | - return true |
116 | | -end |
117 | | - |
118 | 8 | ---@param s PeekstackStackViewState |
119 | 9 | local function render_state(s) |
120 | | - renderer.render(s, is_ready) |
| 10 | + renderer.render(s, window.is_ready) |
121 | 11 | end |
122 | 12 |
|
123 | 13 | ---@return PeekstackStackViewKeymapDeps |
124 | 14 | local function keymap_deps() |
125 | 15 | return { |
126 | | - render = function(s) |
127 | | - render_state(s) |
128 | | - end, |
| 16 | + render = render_state, |
129 | 17 | toggle = function() |
130 | 18 | M.toggle() |
131 | 19 | end, |
132 | 20 | is_open = function(s) |
133 | | - return is_open(s) |
| 21 | + return window.is_open(s) |
134 | 22 | end, |
135 | 23 | focus_stack_view = function(s) |
136 | | - focus_stack_view(s) |
| 24 | + window.focus(s) |
137 | 25 | end, |
138 | 26 | } |
139 | 27 | end |
140 | 28 |
|
141 | 29 | ---@param s PeekstackStackViewState |
142 | | -local function reset_open_state(s) |
143 | | - if s.autoclose_group then |
144 | | - pcall(vim.api.nvim_del_augroup_by_id, s.autoclose_group) |
145 | | - end |
146 | | - s.autoclose_group = nil |
147 | | - |
148 | | - if s.winid and vim.api.nvim_win_is_valid(s.winid) then |
149 | | - pcall(vim.api.nvim_win_close, s.winid, true) |
150 | | - end |
151 | | - |
152 | | - s.winid = nil |
153 | | - s.bufnr = nil |
154 | | - s.root_winid = nil |
155 | | - s.render_keys = {} |
156 | | - s.autoclose_suspended = 0 |
157 | | - s.help_augroup = nil |
158 | | - s.preview_ts_cache = {} |
| 30 | +---@param opts? { refocus: boolean } |
| 31 | +local function close_stack_view(s, opts) |
| 32 | + keymaps.close_help(s, opts, keymap_deps()) |
| 33 | + window.close(s) |
159 | 34 | end |
160 | 35 |
|
161 | | ----Find a non-floating window to use as root. |
162 | | ----@return integer |
163 | | -local function find_root_winid() |
164 | | - local winid = vim.api.nvim_get_current_win() |
165 | | - local cfg = vim.api.nvim_win_get_config(winid) |
166 | | - if cfg.relative == "" then |
167 | | - return winid |
168 | | - end |
169 | | - for _, w in ipairs(vim.api.nvim_tabpage_list_wins(0)) do |
170 | | - local c = vim.api.nvim_win_get_config(w) |
171 | | - if c.relative == "" then |
172 | | - return w |
173 | | - end |
174 | | - end |
175 | | - return winid |
176 | | -end |
177 | | - |
178 | | ----@return integer |
179 | | -local function editor_lines() |
180 | | - return math.max(vim.o.lines - vim.o.cmdheight, 1) |
181 | | -end |
182 | | - |
183 | | ----@return PeekstackRenderWinOpts |
184 | | -local function stack_view_win_config() |
185 | | - local columns = vim.o.columns |
186 | | - local lines = editor_lines() |
187 | | - local position = config.get().ui.stack_view.position or "right" |
188 | | - |
189 | | - local width = math.max(30, math.floor(columns * 0.3)) |
190 | | - width = math.min(width, columns) |
191 | | - |
192 | | - local height = math.max(6, lines - 2) |
193 | | - height = math.min(height, lines) |
194 | | - |
195 | | - local row = 0 |
196 | | - local col = math.max(columns - width, 0) |
197 | | - |
198 | | - if position == "left" then |
199 | | - col = 0 |
200 | | - elseif position == "bottom" then |
201 | | - width = columns |
202 | | - height = math.max(6, math.floor(lines * 0.3)) |
203 | | - height = math.min(height, lines) |
204 | | - row = math.max(lines - height, 0) |
205 | | - col = 0 |
206 | | - end |
207 | | - |
208 | | - return { |
209 | | - relative = "editor", |
210 | | - row = row, |
211 | | - col = col, |
212 | | - width = width, |
213 | | - height = height, |
214 | | - style = "minimal", |
215 | | - border = "rounded", |
216 | | - focusable = true, |
217 | | - zindex = 100, |
218 | | - title = "Stack View", |
219 | | - title_pos = "center", |
220 | | - } |
| 36 | +---Setup stack view autocmds. |
| 37 | +function M.setup() |
| 38 | + state.setup() |
221 | 39 | end |
222 | 40 |
|
223 | 41 | ---Open the stack view. |
224 | 42 | function M.open() |
225 | 43 | M.setup() |
226 | 44 |
|
227 | | - local s = get_state() |
228 | | - if is_open(s) then |
229 | | - vim.api.nvim_set_current_win(s.winid) |
| 45 | + local s = state.current() |
| 46 | + if window.is_open(s) then |
| 47 | + window.focus(s) |
230 | 48 | render_state(s) |
231 | 49 | return |
232 | 50 | end |
233 | 51 |
|
234 | | - s.autoclose_suspended = 0 |
235 | | - s.root_winid = find_root_winid() |
236 | | - s.bufnr = vim.api.nvim_create_buf(false, true) |
237 | | - s.winid = vim.api.nvim_open_win(s.bufnr, true, stack_view_win_config()) |
238 | | - s.render_keys = {} |
239 | | - |
240 | | - vim.wo[s.winid].cursorline = true |
241 | | - vim.wo[s.winid].winhighlight = "CursorLine:PeekstackStackViewCursorLine" |
242 | | - vim.api.nvim_win_set_var(s.winid, "peekstack_root_winid", s.root_winid) |
243 | | - require("peekstack.core.stack")._register_stack_view_win(s.winid) |
244 | | - |
245 | | - local fs = require("peekstack.util.fs") |
246 | | - fs.configure_buffer(s.bufnr) |
247 | | - vim.bo[s.bufnr].modifiable = false |
248 | | - vim.bo[s.bufnr].filetype = "peekstack-stack" |
249 | | - |
250 | | - local group_name = string.format("PeekstackStackViewAutoClose:%d", s.bufnr) |
251 | | - local au_group = vim.api.nvim_create_augroup(group_name, { clear = true }) |
252 | | - s.autoclose_group = au_group |
253 | | - |
254 | | - vim.api.nvim_create_autocmd("WinLeave", { |
255 | | - group = au_group, |
256 | | - buffer = s.bufnr, |
257 | | - callback = function() |
258 | | - vim.schedule(function() |
259 | | - if not should_autoclose(s) then |
260 | | - return |
261 | | - end |
262 | | - keymaps.close_help(s, nil, keymap_deps()) |
263 | | - reset_open_state(s) |
264 | | - end) |
| 52 | + window.open(s, { |
| 53 | + before_close = function(opts) |
| 54 | + keymaps.close_help(s, opts, keymap_deps()) |
265 | 55 | end, |
266 | | - }) |
267 | | - |
268 | | - vim.api.nvim_create_autocmd("CursorMoved", { |
269 | | - group = au_group, |
270 | | - buffer = s.bufnr, |
271 | | - callback = function() |
| 56 | + ensure_non_header_cursor = function() |
272 | 57 | keymaps.ensure_non_header_cursor(s) |
273 | 58 | end, |
274 | 59 | }) |
|
279 | 64 |
|
280 | 65 | ---Toggle the stack view (open if closed, close if open). |
281 | 66 | function M.toggle() |
282 | | - local s = get_state() |
283 | | - if is_open(s) then |
284 | | - keymaps.close_help(s, nil, keymap_deps()) |
285 | | - reset_open_state(s) |
| 67 | + local s = state.current() |
| 68 | + if window.is_open(s) then |
| 69 | + close_stack_view(s) |
286 | 70 | return |
287 | 71 | end |
288 | 72 | M.open() |
289 | 73 | end |
290 | 74 |
|
291 | 75 | ---Re-render all open stack views (called on push/close events). |
292 | 76 | function M.refresh_all() |
293 | | - for _, s in pairs(states) do |
294 | | - if is_open(s) and s.bufnr and vim.api.nvim_buf_is_valid(s.bufnr) then |
| 77 | + for _, s in pairs(state.all()) do |
| 78 | + if window.is_ready(s) then |
295 | 79 | render_state(s) |
296 | 80 | end |
297 | 81 | end |
298 | 82 | end |
299 | 83 |
|
300 | 84 | ---Resize and re-render all open stack views (called on VimResized/WinResized). |
301 | 85 | function M.resize_all() |
302 | | - for _, s in pairs(states) do |
303 | | - if is_open(s) and s.winid and vim.api.nvim_win_is_valid(s.winid) then |
304 | | - pcall(vim.api.nvim_win_set_config, s.winid, stack_view_win_config()) |
| 86 | + for _, s in pairs(state.all()) do |
| 87 | + if window.is_open(s) then |
| 88 | + window.resize(s) |
305 | 89 | render_state(s) |
306 | 90 | end |
307 | 91 | end |
308 | 92 | end |
309 | 93 |
|
310 | | ----Get stack view state (for testing). |
311 | | ----@return PeekstackStackViewState |
312 | | -function M._get_state() |
313 | | - return get_state() |
314 | | -end |
315 | | - |
316 | | ----Get stack view state count (for testing). |
317 | | ----@return integer |
318 | | -function M._state_count() |
319 | | - local count = 0 |
320 | | - for _ in pairs(states) do |
321 | | - count = count + 1 |
322 | | - end |
323 | | - return count |
324 | | -end |
325 | | - |
326 | | ----Render stack view state (for testing). |
327 | | ----@param s PeekstackStackViewState |
328 | | -function M._render(s) |
329 | | - render_state(s) |
330 | | -end |
331 | | - |
332 | 94 | return M |
0 commit comments