-
-
Notifications
You must be signed in to change notification settings - Fork 61
/
utils.lua
226 lines (185 loc) · 6.75 KB
/
utils.lua
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
local utils = require("nui.utils")
local _ = utils._
local defaults = utils.defaults
--luacheck: push no max line length
---@alias nui_layout_option_anchor "NW"|"NE"|"SW"|"SE"
---@alias nui_layout_option_relative_type "'cursor'"|"'editor'"|"'win'"|"'buf'"
---@alias nui_layout_option_relative { type: nui_layout_option_relative_type, winid?: number, position?: { row: number, col: number } }
---@alias nui_layout_option_position { row: number|string, col: number|string }
---@alias nui_layout_option_size { width: number|string, height: number|string }
---@alias nui_layout_internal_position { relative: "'cursor'"|"'editor'"|"'win'", win: number, bufpos?: number[], row: number, col: number }
---@alias nui_layout_container_info { relative: nui_layout_option_relative_type, size: { height: integer, width: integer }, type: "'editor'"|"'window'" }
--luacheck: pop
local mod_size = {}
local mod_position = {}
local mod = {
size = mod_size,
position = mod_position,
}
---@param position nui_layout_option_position
---@param size { width: number, height: number }
---@param container nui_layout_container_info
---@return { row: number, col: number }
function mod.calculate_window_position(position, size, container)
local row
local col
local is_percentage_allowed = not vim.tbl_contains({ "buf", "cursor" }, container.relative)
local percentage_error = string.format("position %% can not be used relative to %s", container.relative)
local r = utils.parse_number_input(position.row)
assert(r.value ~= nil, "invalid position.row")
if r.is_percentage then
assert(is_percentage_allowed, percentage_error)
row = math.floor((container.size.height - size.height) * r.value)
else
row = r.value
end
local c = utils.parse_number_input(position.col)
assert(c.value ~= nil, "invalid position.col")
if c.is_percentage then
assert(is_percentage_allowed, percentage_error)
col = math.floor((container.size.width - size.width) * c.value)
else
col = c.value
end
return {
row = row,
col = col,
}
end
---@param size { width: number|string, height: number|string }
---@param container_size { width: number, height: number }
---@return { width: number, height: number }
function mod.calculate_window_size(size, container_size)
local width = _.normalize_dimension(size.width, container_size.width)
assert(width, "invalid size.width")
local height = _.normalize_dimension(size.height, container_size.height)
assert(height, "invalid size.height")
return {
width = width,
height = height,
}
end
---@param position nui_layout_internal_position
---@return nui_layout_container_info
function mod.get_container_info(position)
local relative = position.relative
local winid = position.win == 0 and vim.api.nvim_get_current_win() or position.win
if relative == "editor" then
return {
relative = relative,
size = utils.get_editor_size(),
type = "editor",
winid = winid,
}
end
return {
relative = position.bufpos and "buf" or relative,
size = utils.get_window_size(position.win),
type = "window",
winid = winid,
}
end
---@param relative nui_layout_option_relative
---@param fallback_winid number
---@return nui_layout_internal_position
function mod.parse_relative(relative, fallback_winid)
local winid = defaults(relative.winid, fallback_winid)
if relative.type == "buf" then
return {
relative = "win",
win = winid,
bufpos = {
relative.position.row,
relative.position.col,
},
}
end
return {
relative = relative.type,
win = winid,
}
end
---@param component_internal table
---@param config nui_layout_options
function mod.update_layout_config(component_internal, config)
local internal = component_internal
local options = _.normalize_layout_options({
relative = config.relative,
size = config.size,
position = config.position,
})
local win_config = internal.win_config
if config.anchor then
win_config.anchor = config.anchor
end
if options.relative then
internal.layout.relative = options.relative
local fallback_winid = internal.position and internal.position.win
or internal.layout.relative.type == "cursor" and 0
or vim.api.nvim_get_current_win()
internal.position =
vim.tbl_extend("force", internal.position or {}, mod.parse_relative(internal.layout.relative, fallback_winid))
win_config.relative = internal.position.relative
win_config.win = internal.position.relative == "win" and internal.position.win or nil
win_config.bufpos = internal.position.bufpos
end
-- luacov: disable
if not win_config.relative then
return error("missing layout config: relative")
end
-- luacov: enable
local prev_container_size = internal.container_info and internal.container_info.size
internal.container_info = mod.get_container_info(internal.position)
local container_size_changed = not mod.size.are_same(internal.container_info.size, prev_container_size)
if
options.size
-- need_size_refresh
or (container_size_changed and internal.layout.size and mod.size.contains_percentage_string(internal.layout.size))
then
internal.layout.size = options.size or internal.layout.size
internal.size = mod.calculate_window_size(internal.layout.size, internal.container_info.size)
win_config.width = internal.size.width
win_config.height = internal.size.height
end
if not win_config.width or not win_config.height then
return error("missing layout config: size")
end
if
options.position
-- need_position_refresh
or (
container_size_changed
and internal.layout.position
and mod.position.contains_percentage_string(internal.layout.position)
)
then
internal.layout.position = options.position or internal.layout.position
internal.position = vim.tbl_extend(
"force",
internal.position,
mod.calculate_window_position(internal.layout.position, internal.size, internal.container_info)
)
win_config.row = internal.position.row
win_config.col = internal.position.col
end
if not win_config.row or not win_config.col then
return error("missing layout config: position")
end
end
---@param size_a nui_layout_option_size
---@param size_b? nui_layout_option_size
---@return boolean
function mod_size.are_same(size_a, size_b)
return size_b and size_a.width == size_b.width and size_a.height == size_b.height or false
end
---@param size nui_layout_option_size
---@return boolean
function mod_size.contains_percentage_string(size)
return type(size.width) == "string" or type(size.height) == "string"
end
---@param position nui_layout_option_position
---@return boolean
function mod_position.contains_percentage_string(position)
return type(position.row) == "string" or type(position.col) == "string"
end
return mod