Files
input-form.nvim/lua/input-form/utils.lua

175 lines
5.6 KiB
Lua

local M = {}
--- Deep-merge two tables, with `t2` taking precedence over `t1`.
---@param t1 table
---@param t2 table
---@return table
function M.merge(t1, t2)
return vim.tbl_deep_extend("force", t1 or {}, t2 or {})
end
--- Resolve a width value: if a float 0<v<=1, treat as a ratio of `vim.o.columns`.
---@param value number
---@return integer
function M.resolve_width(value)
if value > 0 and value <= 1 then
return math.floor(vim.o.columns * value)
end
return math.floor(value)
end
--- Resolve a height value similarly against `vim.o.lines`.
---@param value number
---@return integer
function M.resolve_height(value)
if value > 0 and value <= 1 then
return math.floor(vim.o.lines * value)
end
return math.floor(value)
end
--- Clamp an integer into [lo, hi].
function M.clamp(v, lo, hi)
if v < lo then
return lo
end
if v > hi then
return hi
end
return v
end
--- The filetype set on every buffer the plugin owns. Users can add this to
--- their UI plugins' exclusion lists as a fallback.
M.FORM_FILETYPE = "input-form"
--- Character sets for the built-in border styles accepted by `nvim_open_win`.
--- Order is clockwise from top-left: TL, T, TR, R, BR, B, BL, L.
local BORDER_CHARS = {
rounded = { "", "", "", "", "", "", "", "" },
single = { "", "", "", "", "", "", "", "" },
double = { "", "", "", "", "", "", "", "" },
solid = { " ", " ", " ", " ", " ", " ", " ", " " },
}
-- T-junction connectors used to replace the top corners when stitching two
-- boxes together (the bottom of box A and the top of box B share a row).
local MERGE_CONNECTORS = {
rounded = { left = "", right = "" },
single = { left = "", right = "" },
double = { left = "", right = "" },
solid = { left = " ", right = " " },
}
--- Build an 8-element border array whose top row is a T-junction stitching
--- into the bottom of a parent box above it. Accepts either one of the
--- built-in border style names or an existing 8-element border array.
---
--- Returns `nil` for unrecognised / non-mergeable borders (e.g. `"none"`,
--- `"shadow"`) so the caller can fall back to an unmerged layout.
---@param border string|table
---@return table|nil
function M.merged_top_border(border)
local chars, connectors
if type(border) == "string" then
chars = BORDER_CHARS[border]
connectors = MERGE_CONNECTORS[border]
elseif type(border) == "table" and #border == 8 then
chars = vim.deepcopy(border)
-- Best-effort fallback for custom arrays: use the straight T's.
connectors = { left = "", right = "" }
end
if not chars or not connectors then
return nil
end
return {
connectors.left,
chars[2],
connectors.right,
chars[4],
chars[5],
chars[6],
chars[7],
chars[8],
}
end
local _excluded_registered = false
-- Append `ft` to a list-shaped config field if missing.
local function ensure_excluded(cfg, key, ft)
if type(cfg) ~= "table" then
return
end
cfg[key] = cfg[key] or {}
if not vim.tbl_contains(cfg[key], ft) then
table.insert(cfg[key], ft)
end
end
--- Tell known UI plugins (scrollbars, indent guides, etc.) to skip buffers
--- with filetype `input-form`. Idempotent. Called lazily on the first
--- `form:show()` so it works whether or not the user called `setup()`.
function M.register_ui_exclusions()
if _excluded_registered then
return
end
_excluded_registered = true
-- nvim-scrollbar (petertriho/nvim-scrollbar).
-- Its real config lives at `require("scrollbar.config")` — the module stores
-- the active table under `.config` and exposes it via `.get()`. We patch
-- both paths defensively in case the module layout differs across versions.
local ok, sbar_cfg = pcall(require, "scrollbar.config")
if ok and sbar_cfg then
if type(sbar_cfg.get) == "function" then
local cfg = sbar_cfg.get()
ensure_excluded(cfg, "excluded_filetypes", M.FORM_FILETYPE)
ensure_excluded(cfg, "excluded_buftypes", "nofile")
end
if type(sbar_cfg.config) == "table" then
ensure_excluded(sbar_cfg.config, "excluded_filetypes", M.FORM_FILETYPE)
end
end
-- Some older layouts exposed the config directly on the main module.
local ok_top, sbar = pcall(require, "scrollbar")
if ok_top and type(sbar) == "table" and type(sbar.config) == "table" then
ensure_excluded(sbar.config, "excluded_filetypes", M.FORM_FILETYPE)
end
-- satellite.nvim (lewis6991/satellite.nvim).
local ok2, sat_cfg = pcall(require, "satellite.config")
if ok2 and sat_cfg then
if type(sat_cfg.user_config) == "table" then
ensure_excluded(sat_cfg.user_config, "excluded_filetypes", M.FORM_FILETYPE)
end
if type(sat_cfg.config) == "table" then
ensure_excluded(sat_cfg.config, "excluded_filetypes", M.FORM_FILETYPE)
end
end
end
--- Mark a buffer as an internal form buffer so third-party UI plugins
--- (scrollbars, indent guides, git signs, etc.) skip it.
---
--- Sets `filetype = "input-form"` plus the opt-out buffer variables
--- recognized by common plugins. Users whose plugins don't honour these can
--- add `input-form` to their plugin's exclusion list.
function M.mark_form_buffer(buf)
if not (buf and vim.api.nvim_buf_is_valid(buf)) then
return
end
vim.bo[buf].filetype = M.FORM_FILETYPE
-- nvim-scrollbar (petertriho/nvim-scrollbar)
vim.b[buf].scrollbar_disabled = true
-- satellite.nvim (lewis6991/satellite.nvim)
vim.b[buf].satellite_disable = true
-- mini.indentscope / mini.map
vim.b[buf].miniindentscope_disable = true
vim.b[buf].minimap_disable = true
-- gitsigns (defensive; unlikely on a nofile buf but cheap)
vim.b[buf].gitsigns_disable = true
end
return M