Skip to content

Commit 3d4db4b

Browse files
committed
feat(layout)!: use size instead of height and width (#116)
1 parent c3dcedd commit 3d4db4b

File tree

10 files changed

+205
-33
lines changed

10 files changed

+205
-33
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,10 @@ return {
8484
},
8585
},
8686
windows = {
87-
height = 0.25,
87+
size = 0.25,
8888
position = "below",
8989
terminal = {
90-
width = 0.5,
90+
size = 0.5,
9191
position = "left",
9292
-- List of debug adapters for which the terminal should be ALWAYS hidden
9393
hide = {},

docs/src/routes/dynamic-layout/+page.md

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Dynamic Layout
33
category: Recipes
44
---
55

6-
You can assign different positions to `nvim-dap-view`'s windows using functions.
6+
You can assign different positions and sizes to `nvim-dap-view`'s windows using functions.
77

88
## Example
99

@@ -12,20 +12,38 @@ When there's a single window in the current tab, you can use a _vertical_ layout
1212
```lua
1313
return {
1414
windows = {
15-
position = function()
16-
return vim.tbl_count(vim.iter(vim.api.nvim_tabpage_list_wins(0))
15+
-- `prev` is the last used position, might be nil
16+
position = function(prev)
17+
local wins = api.nvim_tabpage_list_wins(0)
18+
19+
-- Restores previous position if terminal is visible
20+
if
21+
vim.iter(wins):find(function(win)
22+
return vim.w[win].dapview_win_term
23+
end)
24+
then
25+
return prev
26+
end
27+
28+
return vim.tbl_count(vim.iter(wins)
1729
:filter(function(win)
18-
local buf = vim.api.nvim_win_get_buf(win)
19-
-- extui has some funky windows
20-
return vim.bo[buf].buftype == ""
30+
local buf = api.nvim_win_get_buf(win)
31+
local valid_buftype =
32+
vim.tbl_contains({ "", "help", "prompt", "quickfix", "terminal" }, vim.bo[buf].buftype)
33+
local dapview_win = vim.w[win].dapview_win or vim.w[win].dapview_win_term
34+
return valid_buftype and not dapview_win
2135
end)
2236
:totable()) > 1 and "below" or "right"
2337
end,
38+
size = function(pos)
39+
return pos == "below" and 0.25 or 0.5
40+
end,
2441
terminal = {
2542
-- `pos` is the position for the regular window
2643
position = function(pos)
2744
return pos == "below" and "right" or "below"
2845
end,
46+
size = 0.5,
2947
},
3048
},
3149
}

lua/dap-view/actions.lua

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ local traversal = require("dap-view.tree.traversal")
1313
local M = {}
1414

1515
local api = vim.api
16+
local go = vim.go
1617

1718
---@param hide_terminal? boolean
1819
M.toggle = function(hide_terminal)
@@ -88,30 +89,82 @@ M.open = function(hide_terminal)
8889
local term_config = windows_config.terminal
8990

9091
local position = windows_config.position
91-
local win_pos = (type(position) == "function" and position()) or (type(position) == "string" and position)
92+
local win_pos = (type(position) == "function" and position(state.win_pos))
93+
or (type(position) == "string" and position)
9294

9395
---@cast win_pos dapview.Position
9496

97+
state.win_pos = win_pos
98+
9599
local term_position_ = term_config.position
96100
local term_win_pos = (type(term_position_) == "function" and term_position_(win_pos))
97101
or (type(term_position_) == "string" and term_position_)
98102

99103
---@cast term_win_pos dapview.Position
100104

101-
local term_position = util.inverted_directions[term_win_pos]
105+
local inv_term_position = util.inverted_directions[term_win_pos]
102106

103107
local anchor_win = windows_config.anchor and windows_config.anchor()
104108
local is_anchor_win_valid = util.is_win_valid(anchor_win)
105109

110+
local size_ = windows_config.size
111+
local size__ = (type(size_) == "function" and size_(win_pos)) or size_
112+
113+
---@cast size__ number
114+
115+
local is_vertical = win_pos == "above" or win_pos == "below"
116+
117+
local term_size_ = term_config.size
118+
local term_size__ = (type(term_size_) == "function" and term_size_(inv_term_position)) or term_size_
119+
120+
---@cast term_size__ number
121+
122+
local term_is_vertical = term_win_pos == "above" or term_win_pos == "below"
123+
124+
local is_win_valid = is_anchor_win_valid or is_term_win_valid
125+
126+
local size = size__ < 1 and math.floor((is_vertical and go.lines or go.columns) * size__) or size__
127+
128+
local shared_split = term_is_vertical == is_vertical
129+
130+
local winfix_setting = is_vertical and "winfixheight" or "winfixwidth"
131+
132+
-- Temporarily disable fixed size
133+
-- If the window exists, it's using the space of both
134+
-- Do not touch anchor because we don't own it
135+
if is_term_win_valid and shared_split then
136+
vim.wo[state.term_winnr][winfix_setting] = false
137+
138+
-- `size` is already an integer at this point
139+
if term_size__ < 1 then
140+
term_size__ = term_size__ * size
141+
end
142+
end
143+
144+
local go_max = term_is_vertical and go.lines or go.columns
145+
146+
local term_size = term_size__ < 1 and math.floor(go_max * term_size__) or math.floor(term_size__)
147+
106148
local winnr = api.nvim_open_win(bufnr, false, {
107-
split = (is_anchor_win_valid or is_term_win_valid) and term_position or win_pos,
149+
split = is_win_valid and inv_term_position or win_pos,
108150
win = is_anchor_win_valid and anchor_win or is_term_win_valid and term_winnr or -1,
109-
height = windows_config.height < 1 and math.floor(vim.go.lines * windows_config.height)
110-
or windows_config.height,
111-
width = term_config.width < 1 and math.floor(vim.go.columns * (1 - term_config.width))
112-
or math.floor(vim.go.columns - term_config.width),
151+
-- Oh lord
152+
height = (
153+
is_win_valid and (term_is_vertical and ((is_vertical and size or go.lines) - term_size) or size)
154+
or (is_vertical and size or nil)
155+
),
156+
width = (
157+
is_win_valid
158+
and (not term_is_vertical and ((not is_vertical and size or go.columns) - term_size) or size)
159+
or (not is_vertical and size or nil)
160+
),
113161
})
114162

163+
-- Restore fixed size
164+
if is_term_win_valid and shared_split then
165+
vim.wo[state.term_winnr][winfix_setting] = true
166+
end
167+
115168
assert(winnr ~= 0, "Failed to create nvim-dap-view window")
116169

117170
state.winnr = winnr

lua/dap-view/autocmds.lua

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,26 @@ local winbar = require("dap-view.options.winbar")
99

1010
local api = vim.api
1111

12+
api.nvim_create_autocmd("WinClosed", {
13+
callback = function(args)
14+
local w = vim.w[tonumber(args.file)]
15+
16+
-- Resize main window on term close to
17+
if w.dapview_win_term and util.is_win_valid(state.winnr) then
18+
require("dap-view.util.window").resize(state.winnr)
19+
end
20+
21+
-- Similarly for closing main
22+
if w.dapview_win and util.is_win_valid(state.term_winnr) then
23+
require("dap-view.util.window").resize(state.term_winnr)
24+
end
25+
end,
26+
})
27+
1228
api.nvim_create_autocmd({ "WinClosed", "WinNew" }, {
1329
callback = function()
1430
vim.schedule(function()
15-
if state.winnr ~= nil then
31+
if util.is_win_valid(state.winnr) then
1632
winbar.refresh_winbar()
1733
end
1834
end)

lua/dap-view/config.lua

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ local M = {}
1212

1313
---@class dapview.TerminalConfig
1414
---@field hide string[] List of adapters for which the terminal should be hidden
15-
---@field position dapview.Position|fun(position: dapview.Position): dapview.Position
16-
---@field width number If > 1 number of columns, else percentage the terminal window should use
15+
---@field position dapview.Position|fun(position: dapview.Position): dapview.Position Can be a function, receiving the main window's position as its argument
16+
---@field size number|fun(position: dapview.Position): number Size of the terminal window's split. Either a number, where if > 1 it's the absolute size, or else the percentage. Can also be a function, receiving the position and returning a number, that then follows the same logic.
1717
---@field start_hidden boolean Don't show the terminal window when starting a new session
1818

1919
---@class dapview.WindowsConfig
20-
---@field height number If > 1 number of lines, else percentage the windows should use
21-
---@field position dapview.Position|fun(): dapview.Position
22-
---@field anchor? fun(): integer? Function that returns a window number for the main nvim-dap-view window to follow
20+
---@field size number|fun(position: dapview.Position): number Size of the main window's split. Either a number, where if > 1 it's the absolute size, or else the percentage. Can also be a function, receiving the position and returning a number, that then follows the same logic.
21+
---@field position dapview.Position|fun(prev_position?: dapview.Position): dapview.Position Position of the top level split. Can also be a function, receiving its previous position (if any) as its argument.
22+
---@field anchor? fun(): integer? Function that returns a window number for the main nvim-dap-view window to treat as the terminal window
2323
---@field terminal dapview.TerminalConfig
2424

2525
---@alias dapview.DefaultIcons dapview.DefaultButton | "pause"
@@ -148,10 +148,10 @@ M.config = {
148148
},
149149
},
150150
windows = {
151-
height = 0.25,
151+
size = 0.25,
152152
position = "below",
153153
terminal = {
154-
width = 0.5,
154+
size = 0.5,
155155
position = "left",
156156
hide = {},
157157
start_hidden = true,

lua/dap-view/console/view.lua

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ local scroll = require("dap-view.console.scroll")
1111
local M = {}
1212

1313
local api = vim.api
14+
local go = vim.go
1415

1516
---Workaround to fetch the term_buf for sessions created via `startDebugging` from js-debug-adapter
1617
---Only the top-level session owns the buf, children need to traverse parents to get it
@@ -112,24 +113,73 @@ M.open_term_buf_win = function()
112113
local is_win_valid = util.is_win_valid(state.winnr)
113114

114115
local position = windows_config.position
115-
local win_pos = (type(position) == "function" and position()) or (type(position) == "string" and position)
116+
local win_pos = (type(position) == "function" and position(state.win_pos))
117+
or (type(position) == "string" and position)
116118

117119
---@cast win_pos dapview.Position
118120

121+
state.win_pos = win_pos
122+
119123
local term_position = term_config.position
120124
local term_win_pos = (type(term_position) == "function" and term_position(win_pos))
121125
or (type(term_position) == "string" and term_position)
122126

123127
---@cast term_win_pos dapview.Position
124128

129+
local size_ = windows_config.size
130+
local size__ = (type(size_) == "function" and size_(win_pos)) or size_
131+
132+
---@cast size__ number
133+
134+
local is_vertical = win_pos == "above" or win_pos == "below"
135+
136+
local go_max = is_vertical and go.lines or go.columns
137+
138+
local size = size__ < 1 and math.floor(go_max * size__) or size__
139+
140+
local term_size_ = term_config.size
141+
local term_size__ = (type(term_size_) == "function" and term_size_(term_win_pos)) or term_size_
142+
143+
---@cast term_size__ number
144+
145+
local term_is_vertical = term_win_pos == "above" or term_win_pos == "below"
146+
147+
local shared_split = term_is_vertical == is_vertical
148+
149+
local winfix_setting = is_vertical and "winfixheight" or "winfixwidth"
150+
151+
-- Temporarily disable fixed size
152+
-- If the window exists, it's using the space of both
153+
if is_win_valid and shared_split then
154+
vim.wo[state.winnr][winfix_setting] = false
155+
156+
-- `size` is already an integer at this point
157+
if term_size__ < 1 then
158+
term_size__ = term_size__ * size
159+
end
160+
end
161+
162+
local term_go_max = term_is_vertical and go.lines or go.columns
163+
164+
local term_size = math.floor(term_size__ < 1 and term_go_max * term_size__ or term_size__)
165+
125166
local term_winnr = api.nvim_open_win(term_bufnr, false, {
126167
split = is_win_valid and term_win_pos or win_pos,
127168
win = is_win_valid and state.winnr or -1,
128-
height = windows_config.height < 1 and math.floor(vim.go.lines * windows_config.height)
129-
or windows_config.height,
130-
width = term_config.width < 1 and math.floor(vim.go.columns * term_config.width) or term_config.width,
169+
-- If there is a valid main window, we just need to apply whatever the term config says
170+
-- Which means that we'd only set the height for vertical terms
171+
-- Else, we need to create the window as if it occupied the space of both windows
172+
-- Which means that, we'd only set the height if the main window is a vertical split
173+
height = (is_win_valid and (term_is_vertical and term_size or nil)) or (is_vertical and size or nil),
174+
-- Symmetric for width
175+
width = (is_win_valid and (not term_is_vertical and term_size or nil) or (not is_vertical and size or nil)),
131176
})
132177

178+
-- Restore fixed size
179+
if is_win_valid and shared_split then
180+
vim.wo[state.winnr][winfix_setting] = true
181+
end
182+
133183
-- Track the state of the last term win, so it can be closed later if becomes a leftover window
134184
if state.term_winnr and term_winnr ~= state.term_winnr then
135185
state.last_term_winnr = state.term_winnr

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ local validate = require("dap-view.setup.validate.util").validate
55
---@param config dapview.WindowsConfig
66
function M.validate(config)
77
validate("windows", {
8-
height = { config.height, "number" },
8+
size = { config.size, { "number", "function" } },
99
position = { config.position, { "string", "function" } },
1010
terminal = { config.terminal, "table" },
1111
anchor = { config.anchor, { "function", "nil" } },
@@ -14,7 +14,7 @@ function M.validate(config)
1414
validate("windows.terminal", {
1515
position = { config.terminal.position, { "string", "function" } },
1616
hide = { config.terminal.hide, "table" },
17-
width = { config.terminal.width, "number" },
17+
size = { config.terminal.size, { "number", "function" } },
1818
start_hidden = { config.terminal.start_hidden, "boolean" },
1919
}, config.terminal)
2020
end

lua/dap-view/state.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
---@field watched_expressions table<string, dapview.ExpressionView>
4646
---@field expr_count integer
4747
---@field cur_pos table<dapview.DefaultSection,integer?>
48+
---@field win_pos? dapview.Position
4849
local M = {
4950
expr_count = 0,
5051
threads_filter = "",

lua/dap-view/util/init.lua

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ local M = {}
33
local api = vim.api
44

55
M.inverted_directions = {
6-
["above"] = "below",
7-
["below"] = "above",
8-
["right"] = "left",
9-
["left"] = "right",
6+
above = "below",
7+
below = "above",
8+
right = "left",
9+
left = "right",
1010
}
1111

1212
---@param bufnr? integer

lua/dap-view/util/window.lua

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
local state = require("dap-view.state")
2+
local setup = require("dap-view.setup")
3+
14
local api = vim.api
25

36
local M = {}
@@ -26,4 +29,35 @@ M.fetch_window = function(opts)
2629
end
2730
end
2831

32+
---Restores the configured size for a given window
33+
---@param winnr integer
34+
M.resize = function(winnr)
35+
local wo = vim.wo[winnr]
36+
37+
local size_ = setup.config.windows.size
38+
local size__ = ((type(size_) == "function" and size_(state.win_pos)) or size_)
39+
40+
---@cast size__ number
41+
42+
if wo.winfixheight then
43+
wo.winfixheight = false
44+
45+
local size = math.floor(size__ < 1 and size__ * vim.go.lines or size__)
46+
47+
api.nvim_win_set_height(state.term_winnr, size)
48+
49+
wo.winfixheight = true
50+
end
51+
52+
if wo.winfixwidth then
53+
wo.winfixwidth = false
54+
55+
local size = math.floor(size__ < 1 and size__ * vim.go.columns or size__)
56+
57+
api.nvim_win_set_width(state.term_winnr, size)
58+
59+
wo.winfixwidth = true
60+
end
61+
end
62+
2963
return M

0 commit comments

Comments
 (0)