diff --git a/lua/oil/config.lua b/lua/oil/config.lua index f25fdfb7..86d978ea 100644 --- a/lua/oil/config.lua +++ b/lua/oil/config.lua @@ -74,6 +74,29 @@ local default_config = { winblend = 10, }, }, + -- Configuration for the actions floating preview window + preview = { + -- Width dimensions can be integers or a float between 0 and 1 (e.g. 0.4 for 40%) + -- min_width and max_width can be a single value or a list of mixed integer/float types. + -- max_width = {100, 0.8} means "the lesser of 100 columns or 80% of total" + max_width = 0.9, + -- min_width = {40, 0.4} means "the greater of 40 columns or 40% of total" + min_width = { 40, 0.4 }, + -- optionally define an integer/float for the exact width of the preview window + width = nil, + -- Height dimensions can be integers or a float between 0 and 1 (e.g. 0.4 for 40%) + -- min_height and max_height can be a single value or a list of mixed integer/float types. + -- max_height = {80, 0.9} means "the lesser of 80 columns or 90% of total" + max_height = 0.9, + -- min_height = {5, 0.1} means "the greater of 5 columns or 10% of total" + min_height = { 5, 0.1 }, + -- optionally define an integer/float for the exact height of the preview window + height = nil, + border = "rounded", + win_options = { + winblend = 0, + }, + }, } -- The adapter API hasn't really stabilized yet. We're not ready to advertise or encourage people to diff --git a/lua/oil/layout.lua b/lua/oil/layout.lua new file mode 100644 index 00000000..6c094aae --- /dev/null +++ b/lua/oil/layout.lua @@ -0,0 +1,100 @@ +local M = {} + +local function is_float(value) + local _, p = math.modf(value) + return p ~= 0 +end + +local function calc_float(value, max_value) + if value and is_float(value) then + return math.min(max_value, value * max_value) + else + return value + end +end + +M.get_editor_width = function() + return vim.o.columns +end + +M.get_editor_height = function() + local editor_height = vim.o.lines - vim.o.cmdheight + -- Subtract 1 if tabline is visible + if vim.o.showtabline == 2 or (vim.o.showtabline == 1 and #vim.api.nvim_list_tabpages() > 1) then + editor_height = editor_height - 1 + end + -- Subtract 1 if statusline is visible + if + vim.o.laststatus >= 2 or (vim.o.laststatus == 1 and #vim.api.nvim_tabpage_list_wins(0) > 1) + then + editor_height = editor_height - 1 + end + return editor_height +end + +local function calc_list(values, max_value, aggregator, limit) + local ret = limit + if not max_value or not values then + return nil + elseif 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, exact_size, min_size, max_size, total_size) + local ret = calc_float(exact_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 + if not desired_size then + if min_val and max_val then + ret = (min_val + max_val) / 2 + else + ret = 80 + end + else + ret = calc_float(desired_size, total_size) + end + end + if max_val then + ret = math.min(ret, max_val) + end + if min_val then + ret = math.max(ret, min_val) + end + return math.floor(ret) +end + +M.calculate_width = function(desired_width, opts) + return calculate_dim( + desired_width, + opts.width, + opts.min_width, + opts.max_width, + M.get_editor_width() + ) +end + +M.calculate_height = function(desired_height, opts) + return calculate_dim( + desired_height, + opts.height, + opts.min_height, + opts.max_height, + M.get_editor_height() + ) +end + +M.calculate_dims = function(desired_width, desired_height, opts) + local width = M.calculate_width(desired_width, opts) + local height = M.calculate_height(desired_height, opts) + return width, height +end + +return M diff --git a/lua/oil/mutator/preview.lua b/lua/oil/mutator/preview.lua index 48893b20..bb1fb320 100644 --- a/lua/oil/mutator/preview.lua +++ b/lua/oil/mutator/preview.lua @@ -1,5 +1,6 @@ local columns = require("oil.columns") local config = require("oil.config") +local layout = require("oil.layout") local util = require("oil.util") local M = {} @@ -44,6 +45,8 @@ end ---@param should_confirm nil|boolean ---@param cb fun(proceed: boolean) M.show = vim.schedule_wrap(function(actions, should_confirm, cb) + -- The schedule wrap ensures that we actually enter the floating window. + -- Not sure why it doesn't work without that if should_confirm == false or #actions == 0 then cb(true) return @@ -52,36 +55,52 @@ M.show = vim.schedule_wrap(function(actions, should_confirm, cb) cb(true) return end - -- The schedule wrap ensures that we actually enter the floating window. - -- Not sure why it doesn't work without that + + -- Create the buffer local bufnr = vim.api.nvim_create_buf(false, true) vim.bo[bufnr].bufhidden = "wipe" - local width = 120 - local height = 40 + local lines = {} + local max_line_width = 0 + for _, action in ipairs(actions) do + local adapter = util.get_adapter_for_action(action) + local line + if action.type == "change" then + line = columns.render_change_action(adapter, action) + else + line = adapter.render_action(action) + end + table.insert(lines, line) + local line_width = vim.api.nvim_strwidth(line) + if line_width > max_line_width then + max_line_width = line_width + end + end + table.insert(lines, "") + + -- Create the floating window + local width, height = layout.calculate_dims(max_line_width, #lines + 1, config.preview) local winid = vim.api.nvim_open_win(bufnr, true, { relative = "editor", width = width, height = height, - row = math.floor((vim.o.lines - vim.o.cmdheight - height) / 2), - col = math.floor((vim.o.columns - width) / 2), + row = math.floor((layout.get_editor_height() - height) / 2), + col = math.floor((layout.get_editor_width() - width) / 2), zindex = 152, -- render on top of the floating window title style = "minimal", - border = "rounded", + border = config.preview.border, }) vim.bo[bufnr].filetype = "oil_preview" vim.bo[bufnr].syntax = "oil_preview" - - local lines = {} - for _, action in ipairs(actions) do - local adapter = util.get_adapter_for_action(action) - if action.type == "change" then - table.insert(lines, columns.render_change_action(adapter, action)) - else - table.insert(lines, adapter.render_action(action)) - end + for k, v in pairs(config.preview.win_options) do + vim.api.nvim_win_set_option(winid, k, v) end - table.insert(lines, "") + + -- Finish setting the last line and highlights on the buffer width = vim.api.nvim_win_get_width(0) + height = vim.api.nvim_win_get_height(0) + while #lines < height - 1 do + table.insert(lines, "") + end local last_line = "[O]k [C]ancel" local highlights = {} local padding = string.rep(" ", math.floor((width - last_line:len()) / 2)) @@ -97,6 +116,7 @@ M.show = vim.schedule_wrap(function(actions, should_confirm, cb) vim.api.nvim_buf_add_highlight(bufnr, ns, unpack(hl)) end + -- Attach autocmds and keymaps local cancel local confirm local function make_callback(value)