Skip to content

Commit

Permalink
feat!: more layout options for input and builtin select (#19)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
stevearc committed Mar 2, 2022
1 parent b6f5a92 commit 1e529b8
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 56 deletions.
50 changes: 39 additions & 11 deletions lua/dressing/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down Expand Up @@ -76,23 +84,30 @@ 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,
-- Change default highlight groups (see :help winhl)
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
Expand All @@ -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
Expand Down
104 changes: 70 additions & 34 deletions lua/dressing/input.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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,
Expand All @@ -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)
Expand Down
12 changes: 8 additions & 4 deletions lua/dressing/select/builtin.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
65 changes: 58 additions & 7 deletions lua/dressing/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit 1e529b8

Please sign in to comment.