Skip to content

Commit 577f08c

Browse files
authored
Merge pull request #24 from mhiro2/refactor/split-stack-view-pipeline
refactor(ui): Split stack view state and modularize the render pipeline
2 parents 90af8bd + 2f5e1ab commit 577f08c

11 files changed

Lines changed: 2281 additions & 2218 deletions

File tree

lua/peekstack/types.lua

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,32 @@
161161
---@field help_augroup integer?
162162
---@field autoclose_group integer?
163163
---@field autoclose_suspended integer
164+
---@field preview_ts_cache table<string, PeekstackStackViewPreviewTsHighlight[]|false>
165+
166+
---@class PeekstackStackViewHighlight
167+
---@field col_start integer
168+
---@field col_end integer
169+
---@field hl_group string
170+
171+
---@class PeekstackStackViewPreviewLine
172+
---@field line string
173+
---@field source_bufnr integer
174+
---@field source_line integer
175+
---@field source_col_start integer
176+
---@field source_col_end integer
177+
---@field preview_col_start integer
178+
179+
---@class PeekstackStackViewPreviewTsHighlight
180+
---@field start_offset integer
181+
---@field end_offset integer
182+
---@field hl_group string
183+
184+
---@class PeekstackStackViewRenderModel
185+
---@field lines string[]
186+
---@field highlights PeekstackStackViewHighlight[][]
187+
---@field preview_lines table<integer, PeekstackStackViewPreviewLine>
188+
---@field line_to_id table<integer, integer>
189+
---@field header_lines integer
164190

165191
-- Config type definitions
166192

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
local preview = require("peekstack.ui.stack_view.preview")
2+
3+
local M = {}
4+
5+
local NS = vim.api.nvim_create_namespace("PeekstackStackView")
6+
local PREVIEW_BASE_HL_PRIORITY = 10
7+
8+
---@param line string
9+
---@param line_hls PeekstackStackViewHighlight[]
10+
---@param preview_line PeekstackStackViewPreviewLine?
11+
---@return string
12+
local function line_render_key(line, line_hls, preview_line)
13+
local parts = { line }
14+
for _, hl in ipairs(line_hls) do
15+
parts[#parts + 1] = string.format("%d:%d:%s", hl.col_start, hl.col_end, hl.hl_group)
16+
end
17+
if preview_line then
18+
parts[#parts + 1] = string.format(
19+
"preview:%d:%d:%d:%d:%d",
20+
preview_line.source_bufnr,
21+
preview_line.source_line,
22+
preview_line.source_col_start,
23+
preview_line.source_col_end,
24+
preview_line.preview_col_start
25+
)
26+
end
27+
return table.concat(parts, "|")
28+
end
29+
30+
---@param items string[]
31+
---@param start_idx integer
32+
---@param end_idx integer
33+
---@return string[]
34+
local function slice_lines(items, start_idx, end_idx)
35+
if end_idx < start_idx then
36+
return {}
37+
end
38+
39+
---@type string[]
40+
local slice = {}
41+
for idx = start_idx, end_idx do
42+
slice[#slice + 1] = items[idx]
43+
end
44+
return slice
45+
end
46+
47+
---@param old_keys string[]
48+
---@param new_keys string[]
49+
---@return integer?, integer?, integer?
50+
local function diff_range(old_keys, new_keys)
51+
local old_count = #old_keys
52+
local new_count = #new_keys
53+
local start_idx = 1
54+
55+
while start_idx <= old_count and start_idx <= new_count and old_keys[start_idx] == new_keys[start_idx] do
56+
start_idx = start_idx + 1
57+
end
58+
59+
if start_idx > old_count and start_idx > new_count then
60+
return nil, nil, nil
61+
end
62+
63+
local old_end = old_count
64+
local new_end = new_count
65+
while old_end >= start_idx and new_end >= start_idx and old_keys[old_end] == new_keys[new_end] do
66+
old_end = old_end - 1
67+
new_end = new_end - 1
68+
end
69+
70+
return start_idx, old_end, new_end
71+
end
72+
73+
---@param bufnr integer
74+
---@param highlights PeekstackStackViewHighlight[][]
75+
---@param preview_lines table<integer, PeekstackStackViewPreviewLine>
76+
---@param start_idx integer
77+
---@param end_idx integer
78+
---@param preview_cache table<string, PeekstackStackViewPreviewTsHighlight[]|false>
79+
local function apply_highlights_in_range(bufnr, highlights, preview_lines, start_idx, end_idx, preview_cache)
80+
if end_idx < start_idx then
81+
return
82+
end
83+
84+
for line_idx = start_idx, end_idx do
85+
for _, hl in ipairs(highlights[line_idx] or {}) do
86+
local opts = {
87+
end_col = hl.col_end,
88+
hl_group = hl.hl_group,
89+
}
90+
if hl.hl_group == "PeekstackStackViewPreview" then
91+
opts.priority = PREVIEW_BASE_HL_PRIORITY
92+
end
93+
vim.api.nvim_buf_set_extmark(bufnr, NS, line_idx - 1, hl.col_start, {
94+
end_col = opts.end_col,
95+
hl_group = opts.hl_group,
96+
priority = opts.priority,
97+
})
98+
end
99+
end
100+
101+
---@type table<integer, PeekstackStackViewPreviewLine>
102+
local changed_previews = {}
103+
for line_idx = start_idx, end_idx do
104+
if preview_lines[line_idx] then
105+
changed_previews[line_idx] = preview_lines[line_idx]
106+
end
107+
end
108+
if next(changed_previews) then
109+
preview.apply_treesitter_highlights(bufnr, changed_previews, preview_cache)
110+
end
111+
end
112+
113+
---@param model PeekstackStackViewRenderModel
114+
---@return string[]
115+
local function line_keys(model)
116+
---@type string[]
117+
local keys = {}
118+
for line_idx, line in ipairs(model.lines) do
119+
keys[line_idx] = line_render_key(line, model.highlights[line_idx] or {}, model.preview_lines[line_idx])
120+
end
121+
return keys
122+
end
123+
124+
---@param bufnr integer
125+
---@param old_keys string[]
126+
---@param model PeekstackStackViewRenderModel
127+
---@param preview_cache table<string, PeekstackStackViewPreviewTsHighlight[]|false>
128+
---@return string[]
129+
function M.apply(bufnr, old_keys, model, preview_cache)
130+
local new_keys = line_keys(model)
131+
local start_idx, old_end, new_end = diff_range(old_keys, new_keys)
132+
if start_idx then
133+
local replace = slice_lines(model.lines, start_idx, new_end)
134+
135+
vim.bo[bufnr].modifiable = true
136+
vim.api.nvim_buf_set_lines(bufnr, start_idx - 1, old_end, false, replace)
137+
vim.bo[bufnr].modifiable = false
138+
139+
vim.api.nvim_buf_clear_namespace(bufnr, NS, start_idx - 1, old_end)
140+
apply_highlights_in_range(bufnr, model.highlights, model.preview_lines, start_idx, new_end, preview_cache)
141+
end
142+
143+
return new_keys
144+
end
145+
146+
return M

0 commit comments

Comments
 (0)