feat!: more layout options for input and builtin select (#19)

Removed the row & col options for input and select.builtin. If you want
to tweak the values sent to nvim_open_win, use the `override` function.

Changes:
* min_width/height and max_width/height can be a list of values. Useful
  for mixing raw integers (e.g. for cols) and floats (e.g. for % of
  total)
* can specify an exact width or height for the windows
* when using relative = 'win' or 'editor', the row & col will by default
  be calculated to center the floating win
This commit is contained in:
Steven Arcangeli 2022-03-01 22:36:08 -08:00
parent b6f5a92ef4
commit 1e529b8cdb
4 changed files with 175 additions and 56 deletions

View File

@ -11,21 +11,29 @@ local default_config = {
-- These are passed to nvim_open_win
anchor = "SW",
relative = "cursor",
row = 0,
col = 0,
border = "rounded",
-- 'editor' and 'win' will default to being centered
relative = "cursor",
-- These can be integers or a float between 0 and 1 (e.g. 0.4 for 40%)
prefer_width = 40,
max_width = nil,
min_width = 20,
width = nil,
-- min_width and max_width can be a list of mixed types.
-- min_width = {20, 0.2} means "the greater of 20 columns or 20% of total"
max_width = { 140, 0.9 },
min_width = { 20, 0.2 },
-- Window transparency (0-100)
winblend = 10,
-- Change default highlight groups (see :help winhl)
winhighlight = "",
override = function(conf)
-- This is the config that will be passed to nvim_open_win.
-- Change values here to customize the layout
return conf
end,
-- see :help dressing_get_config
get_config = nil,
},
@ -76,10 +84,9 @@ local default_config = {
builtin = {
-- These are passed to nvim_open_win
anchor = "NW",
relative = "cursor",
row = 0,
col = 0,
border = "rounded",
-- 'editor' and 'win' will default to being centered
relative = "editor",
-- Window transparency (0-100)
winblend = 10,
@ -87,12 +94,20 @@ local default_config = {
winhighlight = "",
-- These can be integers or a float between 0 and 1 (e.g. 0.4 for 40%)
-- the min_ and max_ options can be a list of mixed types.
-- max_width = {140, 0.8} means "the lesser of 140 columns or 80% of total"
width = nil,
max_width = 0.8,
min_width = 40,
max_width = { 140, 0.8 },
min_width = { 40, 0.2 },
height = nil,
max_height = 0.9,
min_height = 10,
min_height = { 10, 0.2 },
override = function(conf)
-- This is the config that will be passed to nvim_open_win.
-- Change values here to customize the layout
return conf
end,
},
-- Used to override format_item. See :help dressing-format
@ -107,6 +122,19 @@ local M = vim.deepcopy(default_config)
M.update = function(opts)
local newconf = vim.tbl_deep_extend("force", default_config, opts or {})
if
newconf.input.row
or newconf.input.col
or newconf.select.builtin.row
or newconf.select.builtin.col
then
vim.notify(
"Deprecated: Dressing row and col are no longer used. Use the override to customize layout (:help dressing)",
vim.log.levels.WARN
)
end
for k, v in pairs(newconf) do
M[k] = v
end

View File

@ -162,6 +162,74 @@ M.trigger_completion = function()
end
end
local function create_or_update_win(config, prompt, opts)
local parent_win = 0
local winopt
local win_conf
-- If the previous window is still open and valid, we're going to update it
if context.winid and vim.api.nvim_win_is_valid(context.winid) then
win_conf = vim.api.nvim_win_get_config(context.winid)
parent_win = win_conf.win
winopt = {
relative = win_conf.relative,
win = win_conf.win,
}
else
winopt = {
relative = config.relative,
anchor = config.anchor,
border = config.border,
height = 1,
style = "minimal",
noautocmd = true,
}
end
-- First calculate the desired base width of the modal
local prefer_width = util.calculate_width(
config.relative,
config.prefer_width,
config,
parent_win
)
-- Then expand the width to fit the prompt and default value
prefer_width = math.max(prefer_width, 4 + vim.api.nvim_strwidth(prompt))
if opts.default then
prefer_width = math.max(prefer_width, 2 + vim.api.nvim_strwidth(opts.default))
end
-- Then recalculate to clamp final value to min/max
local width = util.calculate_width(config.relative, prefer_width, config, parent_win)
winopt.row = util.calculate_row(config.relative, 1, parent_win)
winopt.col = util.calculate_col(config.relative, width, parent_win)
winopt.width = width
if win_conf and config.relative == "cursor" then
-- If we're cursor-relative we should actually not adjust the row/col to
-- prevent jumping. Also remove related args.
if config.relative == "cursor" then
winopt.row = nil
winopt.col = nil
winopt.relative = nil
winopt.win = nil
end
end
winopt = config.override(winopt) or winopt
-- If the floating win was already open
if win_conf then
-- Make sure the previous on_confirm callback is called with nil
vim.schedule(context.on_confirm)
vim.api.nvim_win_set_config(context.winid, winopt)
local start_in_insert = context.start_in_insert
return context.winid, start_in_insert
else
local start_in_insert = string.sub(vim.api.nvim_get_mode().mode, 1, 1) == "i"
local bufnr = vim.api.nvim_create_buf(false, true)
local winid = vim.api.nvim_open_win(bufnr, true, winopt)
return winid, start_in_insert
end
end
setmetatable(M, {
-- use schedule_wrap to avoid a bug when vim opens
-- (see https://github.com/stevearc/dressing.nvim/issues/15)
@ -178,40 +246,7 @@ setmetatable(M, {
-- Create or update the window
local prompt = opts.prompt or config.default_prompt
-- First calculate the desired base width of the modal
local prefer_width = util.calculate_width(config.prefer_width, config)
-- Then expand the width to fit the prompt and default value
prefer_width = math.max(prefer_width, 4 + vim.api.nvim_strwidth(prompt))
if opts.default then
prefer_width = math.max(prefer_width, 2 + vim.api.nvim_strwidth(opts.default))
end
-- Then recalculate to clamp final value to min/max
local width = util.calculate_width(prefer_width, config)
local winopt = {
relative = config.relative,
anchor = config.anchor,
row = config.row,
col = config.col,
border = config.border,
width = width,
height = 1,
style = "minimal",
noautocmd = true,
}
local winid, bufnr, start_in_insert
-- If the input window is already open, hijack it
if context.winid and vim.api.nvim_win_is_valid(context.winid) then
winid = context.winid
-- Make sure the previous on_confirm callback is called with nil
vim.schedule(context.on_confirm)
vim.api.nvim_win_set_width(winid, width)
bufnr = vim.api.nvim_win_get_buf(winid)
start_in_insert = context.start_in_insert
else
start_in_insert = string.sub(vim.api.nvim_get_mode().mode, 1, 1) == "i"
bufnr = vim.api.nvim_create_buf(false, true)
winid = vim.api.nvim_open_win(bufnr, true, winopt)
end
local winid, start_in_insert = create_or_update_win(config, prompt, opts)
context = {
winid = winid,
on_confirm = on_confirm,
@ -221,6 +256,7 @@ setmetatable(M, {
}
vim.api.nvim_win_set_option(winid, "winblend", config.winblend)
vim.api.nvim_win_set_option(winid, "winhighlight", config.winhighlight)
local bufnr = vim.api.nvim_win_get_buf(winid)
-- Finish setting up the buffer
vim.api.nvim_buf_set_option(bufnr, "swapfile", false)

View File

@ -31,18 +31,22 @@ M.select = function(config, items, opts, on_choice)
for i = 0, #lines - 1, 1 do
vim.api.nvim_buf_add_highlight(bufnr, ns, "DressingSelectText", i, 0, -1)
end
local width = util.calculate_width(max_width, config)
local width = util.calculate_width(config.relative, max_width, config, 0)
local height = util.calculate_height(config.relative, #lines, config, 0)
local row = util.calculate_row(config.relative, height, 0)
local col = util.calculate_col(config.relative, width, 0)
local winopt = {
relative = config.relative,
anchor = config.anchor,
row = config.row,
col = config.col,
row = row,
col = col,
border = config.border,
width = width,
height = util.calculate_height(#lines, config),
height = height,
zindex = 150,
style = "minimal",
}
winopt = config.override(winopt) or winopt
local winnr = vim.api.nvim_open_win(bufnr, true, winopt)
vim.api.nvim_win_set_option(winnr, "winblend", config.winblend)
vim.api.nvim_win_set_option(winnr, "winhighlight", config.winhighlight)

View File

@ -13,33 +13,84 @@ local function calc_float(value, max_value)
end
end
local function calc_list(values, max_value, aggregator, limit)
local ret = limit
if type(values) == "table" then
for _, v in ipairs(values) do
ret = aggregator(ret, calc_float(v, max_value))
end
return ret
else
ret = aggregator(ret, calc_float(values, max_value))
end
return ret
end
local function calculate_dim(desired_size, size, min_size, max_size, total_size)
local ret = calc_float(size, total_size)
local min_val = calc_list(min_size, total_size, math.max, 1)
local max_val = calc_list(max_size, total_size, math.min, total_size)
if not ret then
ret = calc_float(desired_size, total_size)
if not desired_size then
ret = (min_val + max_val) / 2
else
ret = calc_float(desired_size, total_size)
end
end
ret = math.min(ret, calc_float(max_size, total_size) or total_size)
ret = math.max(ret, calc_float(min_size, total_size) or 1)
ret = math.min(ret, max_val)
ret = math.max(ret, min_val)
return math.floor(ret)
end
M.calculate_width = function(desired_width, config)
local function get_max_width(relative, winid)
if relative == "editor" then
return vim.o.columns
else
return vim.api.nvim_win_get_width(winid or 0)
end
end
local function get_max_height(relative, winid)
if relative == "editor" then
return vim.o.lines - vim.o.cmdheight
else
return vim.api.nvim_win_get_height(winid or 0)
end
end
M.calculate_col = function(relative, width, winid)
if relative == "cursor" then
return 1
else
return math.floor((get_max_width(relative, winid) - width) / 2)
end
end
M.calculate_row = function(relative, height, winid)
if relative == "cursor" then
return 1
else
return math.floor((get_max_height(relative, winid) - height) / 2)
end
end
M.calculate_width = function(relative, desired_width, config, winid)
return calculate_dim(
desired_width,
config.width,
config.min_width,
config.max_width,
vim.o.columns
get_max_width(relative, winid)
)
end
M.calculate_height = function(desired_height, config)
M.calculate_height = function(relative, desired_height, config, winid)
return calculate_dim(
desired_height,
config.height,
config.min_height,
config.max_height,
vim.o.lines - vim.o.cmdheight
get_max_height(relative, winid)
)
end