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

289 lines
7.4 KiB
Lua
Vendored

-- internal utils
local _ = {
feature = {
lua_keymap = type(vim.keymap) ~= "nil",
lua_autocmd = type(vim.api.nvim_create_autocmd) ~= "nil",
},
}
local utils = {
_ = _,
}
function utils.get_editor_size()
return {
width = vim.o.columns,
height = vim.o.lines,
}
end
function utils.get_window_size(winid)
winid = winid or 0
return {
width = vim.api.nvim_win_get_width(winid),
height = vim.api.nvim_win_get_height(winid),
}
end
function utils.defaults(v, default_value)
return type(v) == "nil" and default_value or v
end
-- luacheck: push no max comment line length
---@param type_name "'nil'" | "'number'" | "'string'" | "'boolean'" | "'table'" | "'function'" | "'thread'" | "'userdata'" | "'list'" | '"map"'
function utils.is_type(type_name, v)
if type_name == "list" then
return vim.tbl_islist(v)
end
if type_name == "map" then
return type(v) == "table" and not vim.tbl_islist(v)
end
return type(v) == type_name
end
-- luacheck: pop
---@param v string | number
function utils.parse_number_input(v)
local parsed = {}
parsed.is_percentage = type(v) == "string" and string.sub(v, -1) == "%"
if parsed.is_percentage then
parsed.value = tonumber(string.sub(v, 1, #v - 1)) / 100
else
parsed.value = tonumber(v)
end
return parsed
end
---@param prefix? string
---@return (fun(): string) get_next_id
local function get_id_generator(prefix)
prefix = prefix or ""
local id = 0
return function()
id = id + 1
return prefix .. id
end
end
_.get_next_id = get_id_generator("nui_")
---@private
---@param bufnr number
---@param linenr number line number (1-indexed)
---@param char_start number start character position (0-indexed)
---@param char_end number end character position (0-indexed)
---@return number[] byte_range
function _.char_to_byte_range(bufnr, linenr, char_start, char_end)
local line = vim.api.nvim_buf_get_lines(bufnr, linenr - 1, linenr, false)[1]
local skipped_part = vim.fn.strcharpart(line, 0, char_start)
local target_part = vim.fn.strcharpart(line, char_start, char_end - char_start)
local byte_start = vim.fn.strlen(skipped_part)
local byte_end = math.min(byte_start + vim.fn.strlen(target_part), vim.fn.strlen(line))
return { byte_start, byte_end }
end
---@type integer
local fallback_namespace_id = vim.api.nvim_create_namespace("nui.nvim")
---@private
---@param ns_id integer
---@return integer
function _.ensure_namespace_id(ns_id)
return ns_id == -1 and fallback_namespace_id or ns_id
end
---@private
---@param ns_id? integer|string
---@return integer ns_id namespace id
function _.normalize_namespace_id(ns_id)
if utils.is_type("string", ns_id) then
---@cast ns_id string
return vim.api.nvim_create_namespace(ns_id)
end
---@cast ns_id integer
return ns_id or fallback_namespace_id
end
---@private
---@param bufnr integer
---@param ns_id integer
---@param linenr_start? integer (1-indexed)
---@param linenr_end? integer (1-indexed,inclusive)
function _.clear_namespace(bufnr, ns_id, linenr_start, linenr_end)
linenr_start = linenr_start or 1
linenr_end = linenr_end and linenr_end + 1 or 0
vim.api.nvim_buf_clear_namespace(bufnr, ns_id, linenr_start - 1, linenr_end - 1)
end
---@private
---@param bufnr number
---@param buf_options table<string, any>
function _.set_buf_options(bufnr, buf_options)
for name, value in pairs(buf_options) do
vim.api.nvim_buf_set_option(bufnr, name, value)
end
end
---@private
---@param winid number
---@param win_options table<string, any>
function _.set_win_options(winid, win_options)
for name, value in pairs(win_options) do
vim.api.nvim_win_set_option(winid, name, value)
end
end
---@private
---@param dimension number | string
---@param container_dimension number
---@return nil | number
function _.normalize_dimension(dimension, container_dimension)
local number = utils.parse_number_input(dimension)
if not number.value then
return nil
end
if number.is_percentage then
return math.floor(container_dimension * number.value)
end
return number.value
end
---@param text string
---@param max_length number
---@return string
function _.truncate_text(text, max_length)
if vim.api.nvim_strwidth(text) > max_length then
return string.sub(text, 1, max_length - 1) .. ""
end
return text
end
---@param text NuiText
---@param max_width number
function _.truncate_nui_text(text, max_width)
text:set(_.truncate_text(text:content(), max_width))
end
---@param line NuiLine
---@param max_width number
function _.truncate_nui_line(line, max_width)
local width = line:width()
local last_part_idx = #line._texts
while width > max_width do
local extra_width = width - max_width
local last_part = line._texts[last_part_idx]
if last_part:width() <= extra_width then
width = width - last_part:width()
line._texts[last_part_idx] = nil
last_part_idx = last_part_idx - 1
-- need to add truncate indicator in previous part
if last_part:width() == extra_width then
last_part = line._texts[last_part_idx]
last_part:set(_.truncate_text(last_part:content() .. " ", last_part:width()))
end
else
last_part:set(_.truncate_text(last_part:content(), last_part:width() - extra_width))
width = width - extra_width
end
end
end
---@param align "'left'" | "'center'" | "'right'"
---@param total_width number
---@param text_width number
---@return number left_gap_width, number right_gap_width
function _.calculate_gap_width(align, total_width, text_width)
local gap_width = total_width - text_width
if align == "left" then
return 0, gap_width
elseif align == "center" then
return math.floor(gap_width / 2), math.ceil(gap_width / 2)
elseif align == "right" then
return gap_width, 0
end
error("invalid value align=" .. align)
end
---@param lines table[] NuiLine[]
---@param bufnr number
---@param ns_id number
---@param linenr_start number
---@param linenr_end number
function _.render_lines(lines, bufnr, ns_id, linenr_start, linenr_end)
vim.api.nvim_buf_set_lines(
bufnr,
linenr_start - 1,
linenr_end - 1,
false,
vim.tbl_map(function(line)
return line:content()
end, lines)
)
for linenr, line in ipairs(lines) do
line:highlight(bufnr, ns_id, linenr)
end
end
function _.normalize_layout_options(options)
if utils.is_type("string", options.relative) then
options.relative = {
type = options.relative,
}
end
if options.position and not utils.is_type("table", options.position) then
options.position = {
row = options.position,
col = options.position,
}
end
if options.size and not utils.is_type("table", options.size) then
options.size = {
width = options.size,
height = options.size,
}
end
return options
end
---@param winhighlight string
---@return table<string, string> highlight_map
function _.parse_winhighlight(winhighlight)
local highlight = {}
local parts = vim.split(winhighlight, ",", { plain = true, trimempty = true })
for _, part in ipairs(parts) do
local key, value = part:match("(.+):(.+)")
highlight[key] = value
end
return highlight
end
---@param highlight_map table<string, string>
---@return string winhighlight
function _.serialize_winhighlight(highlight_map)
local parts = vim.tbl_map(function(key)
return key .. ":" .. highlight_map[key]
end, vim.tbl_keys(highlight_map))
table.sort(parts)
return table.concat(parts, ",")
end
return utils