Skip to content

Commit b1eadfa

Browse files
committed
feat: allow aligning content inside breakpoints and threads (#129)
1 parent 663f985 commit b1eadfa

File tree

8 files changed

+169
-4
lines changed

8 files changed

+169
-4
lines changed

docs/src/routes/configuration/+page.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ The default configuration below is applied when the plugin is _loaded_ (**no `se
1111
These are the default options for `nvim-dap-view`.
1212

1313
:::note
14-
You don't have to copy and paste these options. Use them as a reference.
14+
**You don't have to copy and paste these options.** Use them as a reference.
1515
:::
1616

1717
```lua
@@ -115,6 +115,32 @@ return {
115115
-- Optionally a function that takes two `dap.Variable`'s as arguments
116116
-- and is forwarded to a `table.sort` when rendering variables in the scopes view
117117
sort_variables = nil,
118+
-- Full control of how frames are rendered, see the "Custom Formatting" page
119+
threads = {
120+
-- Choose which items to display and how
121+
format = function(name, lnum, path)
122+
return {
123+
{ part = name, separator = " " },
124+
{ part = path, hl = "FileName", separator = ":" },
125+
{ part = lnum, hl = "LineNumber" },
126+
}
127+
end,
128+
-- Align columns
129+
align = false,
130+
},
131+
-- Full control of how breakpoints are rendered, see the "Custom Formatting" page
132+
breakpoints = {
133+
-- Choose which items to display and how
134+
format = function(line, lnum, path)
135+
return {
136+
{ part = path, hl = "FileName" },
137+
{ part = lnum, hl = "LineNumber" },
138+
{ part = line, hl = true },
139+
}
140+
end,
141+
-- Align columns
142+
align = false,
143+
},
118144
},
119145
-- Controls how to jump when selecting a breakpoint or navigating the stack
120146
-- Comma separated list, like the built-in 'switchbuf'. See :help 'switchbuf'

docs/src/routes/custom-formatting/+page.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ category: Recipes
55

66
You can control how the text is displayed for some views, using special `format` functions. Each customizable view has its own parameters, but the expected return type is the same: an array of `{part: string, hl?: string, separator?: string}`. The `part` is the "content" itself, `hl` is one of `nvim-dap-view`'s [highlight groups](./highlight-groups) (without the `NvimDapView` prefix) and the separator can be used to customize the divider between the current part and the next one.
77

8+
The separator must be a single character. A pipe (`|`) is used if not specified.
9+
10+
Additionally, both `threads` and `breakpoints` support a flag for aligning their columns.
11+
812
## Threads
913

1014
You can manipulate the frame _name_ (usually the function name), its line number and path.
@@ -65,6 +69,8 @@ return {
6569
{ part = line, hl = true },
6670
}
6771
end,
72+
-- Alignment enjoyer
73+
align = true,
6874
},
6975
},
7076
}

lua/dap-view/breakpoints/view.lua

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ M.show = function()
3535
state.breakpoint_lines_by_line[i] = nil
3636
end
3737

38+
---@type integer[][]
39+
local lengths = {}
40+
41+
local num_parts = 0
42+
3843
for buf, buf_entries in pairs(breakpoints) do
3944
local filename = api.nvim_buf_get_name(buf)
4045
local relative_path = vim.fn.fnamemodify(filename, ":.")
@@ -45,6 +50,21 @@ M.show = function()
4550

4651
local parts = setup.config.render.breakpoints.format(text, tostring(entry.lnum), relative_path)
4752

53+
for _, p in ipairs(parts) do
54+
assert(not p.separator or #p.separator == 1, "Separator length must not exceeed 1 character")
55+
end
56+
57+
num_parts = #parts - 1
58+
59+
lengths[#lengths + 1] = vim.iter(parts)
60+
:map(
61+
---@param part dapview.Content
62+
function(part)
63+
return #part.part
64+
end
65+
)
66+
:totable()
67+
4868
table.insert(state.breakpoint_paths_by_line, relative_path)
4969
table.insert(state.breakpoint_lines_by_line, entry.lnum)
5070

@@ -81,6 +101,10 @@ M.show = function()
81101
end
82102
end
83103

104+
if setup.config.render.breakpoints.align then
105+
require("dap-view.util.align").align(num_parts, lengths)
106+
end
107+
84108
-- Clear previous content
85109
util.set_lines(state.bufnr, line, -1, true, {})
86110
end

lua/dap-view/config.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,11 @@ local M = {}
7575

7676
---@class dapview.RenderBreakpointsConfig
7777
---@field format fun(line: string, lnum: string, path: string): dapview.Content[]
78+
---@field align boolean
7879

7980
---@class dapview.RenderThreadsConfig
8081
---@field format fun(name: string, lnum: string, path: string): dapview.Content[]
82+
---@field align boolean
8183

8284
---@class dapview.RenderConfig
8385
---@field sort_variables? fun(lhs: dap.Variable, rhs: dap.Variable): boolean Override order of variables
@@ -189,6 +191,7 @@ M.config = {
189191
render = {
190192
sort_variables = nil,
191193
threads = {
194+
align = false,
192195
format = function(name, lnum, path)
193196
return {
194197
{ part = name, separator = " " },
@@ -198,6 +201,7 @@ M.config = {
198201
end,
199202
},
200203
breakpoints = {
204+
align = false,
201205
format = function(line, lnum, path)
202206
return {
203207
{ part = path, hl = "FileName" },

lua/dap-view/setup/validate/render.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ function M.validate(config)
1313
local threads = config.threads
1414
validate("render.threads", {
1515
format = { threads.format, { "function" } },
16+
align = { threads.align, { "boolean" } },
1617
}, threads)
1718

1819
local breakpoints = config.breakpoints
1920
validate("render.breakpoints", {
2021
format = { breakpoints.format, { "function" } },
22+
align = { breakpoints.align, { "boolean" } },
2123
}, breakpoints)
2224
end
2325

lua/dap-view/threads/view.lua

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,19 @@ M.show = function()
8383

8484
local has_no_filter = state.threads_filter == ""
8585

86+
---@type integer[][]
87+
local lengths = {}
88+
89+
local num_parts = 0
90+
91+
---@type integer[]
92+
local row_offsets = {}
93+
94+
local row_offset = 0
95+
8696
for k, thread in pairs(session.threads) do
97+
row_offset = row_offset + 1
98+
8799
local is_stopped_thread = session.stopped_thread_id == thread.id
88100
local thread_name = is_stopped_thread and thread.name .. " " .. config.icons["pause"] or thread.name
89101
util.set_lines(state.bufnr, line, line, true, { thread_name })
@@ -137,16 +149,37 @@ M.show = function()
137149
end
138150
)
139151

140-
local formated_frames = vim.iter(frames):map(
152+
local formatted_frames = vim.iter(frames):map(
141153
---@param frame dapview.Frame
142154
function(frame)
143155
local format = config.render.threads.format(frame.name, frame.lnum, frame.path)
156+
157+
for _, p in ipairs(format) do
158+
assert(
159+
not p.separator or #p.separator == 1,
160+
"Separator length must not exceeed 1 character"
161+
)
162+
end
163+
164+
num_parts = #format - 1
165+
166+
lengths[#lengths + 1] = vim.iter(format)
167+
:map(
168+
---@param part dapview.Content
169+
function(part)
170+
return #part.part
171+
end
172+
)
173+
:totable()
174+
175+
row_offsets[#row_offsets + 1] = row_offset
176+
144177
return { content = format, id = frame.id }
145178
end
146179
)
147180

148181
---@type dapview.FrameContent[]
149-
local filtered_frames = formated_frames
182+
local filtered_frames = formatted_frames
150183
:filter(
151184
---@param f dapview.FrameContent
152185
function(f)
@@ -217,6 +250,10 @@ M.show = function()
217250
end
218251
end
219252

253+
if setup.config.render.breakpoints.align then
254+
require("dap-view.util.align").align(num_parts, lengths, 1, row_offsets)
255+
end
256+
220257
-- Clear previous content
221258
util.set_lines(state.bufnr, line, -1, true, {})
222259
end

lua/dap-view/util/align.lua

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
local globals = require("dap-view.globals")
2+
local state = require("dap-view.state")
3+
4+
local M = {}
5+
6+
local api = vim.api
7+
8+
---@param num_parts integer
9+
---@param lengths integer[][]
10+
---@param col_offset? integer
11+
---@param row_offsets? integer[]
12+
M.align = function(num_parts, lengths, col_offset, row_offsets)
13+
col_offset = col_offset or 0
14+
row_offsets = row_offsets or {}
15+
16+
---@type integer[]
17+
local maxes = {}
18+
---@type integer[][]
19+
local diffs = {}
20+
21+
for _ = 1, num_parts do
22+
maxes[#maxes + 1] = 0
23+
end
24+
25+
for i = 1, num_parts do
26+
for j = 1, #lengths do
27+
diffs[#diffs + 1] = {}
28+
if lengths[j][i] > maxes[i] then
29+
maxes[i] = lengths[j][i]
30+
end
31+
end
32+
end
33+
34+
for i = 1, num_parts do
35+
for j = 1, #lengths do
36+
diffs[j][i] = maxes[i] - lengths[j][i]
37+
end
38+
end
39+
40+
for i, k in ipairs(diffs) do
41+
local cur_col = col_offset
42+
43+
for j, v in ipairs(k) do
44+
local row = i - 1
45+
if row_offsets[i] then
46+
row = row + row_offsets[i]
47+
end
48+
49+
local col = lengths[i][j] + cur_col
50+
51+
-- Account for the separator
52+
cur_col = col + 1
53+
54+
if v > 0 then
55+
api.nvim_buf_set_extmark(state.bufnr, globals.NAMESPACE, row, col, {
56+
virt_text = { { string.rep(" ", v) } },
57+
virt_text_pos = "inline",
58+
})
59+
end
60+
end
61+
end
62+
end
63+
64+
return M

lua/dap-view/watches/view.lua

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ local function show_variables(children, reference, line, depth)
5757
end
5858

5959
M.show = function()
60-
-- Since variables aren't ordered, lines may change unexpectedly
60+
-- New expressions may prepended
61+
-- And lines may be no longer valid if a variable changes (e.g., array's size changes)
62+
-- Hence, lines may change unexpectedly
6163
-- To handle that, always clear the storage table
6264
for k, _ in pairs(state.expression_views_by_line) do
6365
state.expression_views_by_line[k] = nil

0 commit comments

Comments
 (0)