1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-01-24 05:30:07 +08:00
SpaceVim/bundle/nui.nvim/lua/nui/layout/utils.lua
2023-05-30 21:09:18 +08:00

217 lines
6.6 KiB
Lua
Vendored

local utils = require("nui.utils")
local _ = utils._
local defaults = utils.defaults
--luacheck: push no max line length
---@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_config { relative?: nui_layout_option_relative, size?: nui_layout_option_size, position?: nui_layout_option_position }
---@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: nui_layout_option_size, 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
if relative == "editor" then
return {
relative = relative,
size = utils.get_editor_size(),
type = "editor",
}
end
if relative == "cursor" or relative == "win" then
return {
relative = position.bufpos and "buf" or relative,
size = utils.get_window_size(position.win),
type = "window",
}
end
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_config
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 options.relative then
internal.layout.relative = options.relative
local fallback_winid = internal.position and internal.position.win 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
if not win_config.relative then
return error("missing layout config: relative")
end
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)
local need_size_refresh = container_size_changed
and internal.layout.size
and mod.size.contains_percentage_string(internal.layout.size)
if options.size or need_size_refresh 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
local need_position_refresh = container_size_changed
and internal.layout.position
and mod.position.contains_percentage_string(internal.layout.position)
if options.position or need_position_refresh 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
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