Skip to content

Commit

Permalink
feat: support more floating window layouts (#60)
Browse files Browse the repository at this point in the history
  • Loading branch information
stevearc committed Feb 21, 2022
1 parent f41a715 commit 0ae1984
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 77 deletions.
2 changes: 1 addition & 1 deletion lua/aerial/autocommands.lua
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ M.on_enter_buffer = util.throttle(function()
vim.cmd("quit")
else
-- Hack to ignore winwidth
util.set_win_width(0, util.get_width(0))
util.restore_width(0)
end
elseif window.is_open() then
close_orphans()
Expand Down
49 changes: 32 additions & 17 deletions lua/aerial/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,12 @@ local default_options = {
-- 'auto' will manage folds if your previous foldmethod was 'manual'
manage_folds = false,

-- The maximum width of the aerial window
max_width = 40,

-- The minimum width of the aerial window.
-- To disable dynamic resizing, set this to be equal to max_width
-- These control the width of the aerial window.
-- They can be integers or a float between 0 and 1 (e.g. 0.4 for 40%)
-- min_width and max_width can be a list of mixed types.
-- max_value = {40, 0.2} means "the lesser of 40 columns or 20% of total"
max_width = { 40, 0.2 },
width = nil,
min_width = 10,

-- Set default symbol icons to use patched font icons (see https://www.nerdfonts.com/)
Expand Down Expand Up @@ -165,18 +166,25 @@ local default_options = {
-- Controls border appearance. Passed to nvim_open_win
border = "rounded",

-- Controls row offset from cursor. Passed to nvim_open_win
row = 1,

-- Controls col offset from cursor. Passed to nvim_open_win
col = 0,

-- The maximum height of the floating aerial window
max_height = 100,

-- The minimum height of the floating aerial window
-- To disable dynamic resizing, set this to be equal to max_height
min_height = 4,
-- Enum: cursor, editor, win
-- cursor - Opens float on top of the cursor
-- editor - Opens float centered in the editor
-- win - Opens float centered in the window
relative = "cursor",

-- These control the height of the floating window.
-- They can be integers or a float between 0 and 1 (e.g. 0.4 for 40%)
-- min_height and max_height can be a list of mixed types.
-- min_value = {8, 0.1} means "the greater of 8 rows or 10% of total"
max_height = 0.9,
height = nil,
min_height = { 8, 0.1 },

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,
},

lsp = {
Expand Down Expand Up @@ -345,6 +353,13 @@ M.setup = function(opts)
end
end

if newconf.float.row or newconf.float.col then
vim.notify(
"Deprecated: Aerial float.row and float.col are no longer used. Use float.override to customize layout",
vim.log.levels.WARN
)
end

for k, v in pairs(newconf) do
M[k] = v
end
Expand Down
97 changes: 97 additions & 0 deletions lua/aerial/layout.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
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

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
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, max_val)
ret = math.max(ret, min_val)
return math.floor(ret)
end

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,
get_max_width(relative, winid)
)
end

M.calculate_height = function(relative, desired_height, config, winid)
return calculate_dim(
desired_height,
config.height,
config.min_height,
config.max_height,
get_max_height(relative, winid)
)
end

return M
58 changes: 55 additions & 3 deletions lua/aerial/render.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
local config = require("aerial.config")
local data = require("aerial.data")
local layout = require("aerial.layout")
local loading = require("aerial.loading")
local util = require("aerial.util")
local M = {}
Expand All @@ -10,17 +11,68 @@ M.clear_buffer = function(bufnr)
vim.api.nvim_buf_set_option(bufnr, "modifiable", false)
end

-- Resize all windows displaying this aerial buffer
local function resize_all_wins(aer_bufnr, preferred_width, preferred_height)
local max_width = 0
for _, winid in ipairs(vim.api.nvim_list_wins()) do
if vim.api.nvim_win_get_buf(winid) == aer_bufnr then
local relative = "editor"
local parent_win = 0
if util.is_floating_win(winid) then
local win_conf = vim.api.nvim_win_get_config(winid)
relative = win_conf.relative
parent_win = win_conf.win
end

-- preferred width can be nil if symbols are loading
local pw = preferred_width
local gutter = util.win_get_gutter_width(winid)
if pw then
pw = pw + gutter
end
local width = layout.calculate_width(relative, pw, config, parent_win)
-- Subtract the gutter here because it is passed back to be used for
-- padding out whitespace. The gutter needs to adjust the total window
-- size, but it doesn't take space away from the content.
max_width = math.max(max_width, width - gutter)
vim.api.nvim_win_set_width(winid, width)
util.save_width(winid, width)

-- Reposition floating windows
if util.is_floating_win(winid) then
local height = layout.calculate_height(relative, preferred_height, config.float, parent_win)
vim.api.nvim_win_set_height(winid, height)
if relative ~= "cursor" then
local row = layout.calculate_row(relative, height, parent_win)
local col = layout.calculate_col(relative, width, parent_win)
local win_conf = {
row = row,
col = col,
relative = relative,
win = parent_win,
}
local new_conf = config.float.override(win_conf)
vim.api.nvim_win_set_config(winid, new_conf or win_conf)
end
end
end
end
return max_width
end

-- Update the aerial buffer from cached symbols
M.update_aerial_buffer = function(buf)
local bufnr, aer_bufnr = util.get_buffers(buf)
if aer_bufnr == -1 or loading.is_loading(aer_bufnr) then
resize_all_wins(aer_bufnr)
return
end
if not data:has_symbols(bufnr) then
local lines = { "No symbols" }
if config.lsp.filter_kind ~= false then
table.insert(lines, ":help aerial-filter")
end
resize_all_wins(aer_bufnr)
util.render_centered_text(aer_bufnr, lines)
return
end
Expand Down Expand Up @@ -88,8 +140,7 @@ M.update_aerial_buffer = function(buf)
row = row + 1
end)

local width = util.set_width(aer_bufnr, max_len)
util.set_height(aer_bufnr, #lines)
local width = resize_all_wins(aer_bufnr, max_len, #lines)

-- Insert lines into buffer
for i, line in ipairs(lines) do
Expand Down Expand Up @@ -128,7 +179,8 @@ M.update_highlights = function(buf)
if vim.tbl_isempty(winids) then
return
end
local hl_width = math.floor(util.get_width(aer_bufnr) / #winids)
local line = vim.api.nvim_buf_get_lines(aer_bufnr, 0, 1, true)[1]
local hl_width = math.floor(vim.api.nvim_strwidth(line) / #winids)

if hl_mode == "last" then
local pos = bufdata.positions[bufdata.last_win]
Expand Down
58 changes: 11 additions & 47 deletions lua/aerial/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,62 +17,26 @@ M.lpad = function(str, length, padchar)
return str
end

M.get_width = function(bufnr)
local ok, width = pcall(vim.api.nvim_buf_get_var, bufnr or 0, "aerial_width")
M.restore_width = function(winid)
local ok, width = pcall(vim.api.nvim_win_get_var, winid or 0, "aerial_width")
if ok then
return width
vim.api.nvim_win_set_width(winid or 0, width)
end
return math.floor((config.min_width + config.max_width) / 2)
end

M.set_width = function(bufnr, width)
if bufnr == nil or bufnr == 0 then
bufnr = vim.api.nvim_get_current_buf()
end
width = math.min(config.max_width, math.max(config.min_width, width))
if M.get_width(bufnr) == width then
return width
end
vim.api.nvim_buf_set_var(bufnr, "aerial_width", width)

for _, winid in ipairs(vim.api.nvim_list_wins()) do
if vim.api.nvim_win_get_buf(winid) == bufnr then
M.set_win_width(winid, width)
end
end
return width
M.save_width = function(winid, width)
vim.api.nvim_win_set_var(winid or 0, "aerial_width", width)
end

M.set_win_width = function(winid, width)
-- adjust width if buffer has line numbers
M.win_get_gutter_width = function(winid)
winid = winid or 0
if
vim.api.nvim_win_get_option(winid, "number")
or vim.api.nvim_win_get_option(winid, "relativenumber")
then
width = math.min(config.max_width, width + vim.api.nvim_win_get_option(winid, "numberwidth"))
end
vim.api.nvim_win_set_width(winid, width)
end

M.get_height = function(bufnr)
local ok, height = pcall(vim.api.nvim_buf_get_var, bufnr or 0, "aerial_height")
if ok then
return height
end
local max_height = math.min(config.float.max_height, vim.o.lines - vim.o.cmdheight)
return math.max(config.float.min_height, math.floor(max_height / 2))
end

M.set_height = function(bufnr, height)
if bufnr == nil or bufnr == 0 then
bufnr = vim.api.nvim_get_current_buf()
end
height = math.min(config.float.max_height, math.max(config.float.min_height, height))
vim.api.nvim_buf_set_var(bufnr, "aerial_height", height)
for _, winid in ipairs(vim.api.nvim_list_wins()) do
if vim.api.nvim_win_get_buf(winid) == bufnr and M.is_floating_win(winid) then
vim.api.nvim_win_set_height(winid, height)
end
return vim.api.nvim_win_get_option(winid, "numberwidth")
else
return 0
end
end

Expand Down Expand Up @@ -305,7 +269,7 @@ M.render_centered_text = function(bufnr, text)
end
end
local height = 40
local width = M.get_width(bufnr)
local width = 30
if winid then
height = vim.api.nvim_win_get_height(winid)
width = vim.api.nvim_win_get_width(winid)
Expand Down

0 comments on commit 0ae1984

Please sign in to comment.