Meh I'll figure out submodules later
This commit is contained in:
parent
4ca9d44a90
commit
8cb281f436
352 changed files with 66107 additions and 0 deletions
|
|
@ -0,0 +1,296 @@
|
|||
local strings = require "plenary.strings"
|
||||
|
||||
local Border = {}
|
||||
|
||||
Border.__index = Border
|
||||
|
||||
Border._default_thickness = {
|
||||
top = 1,
|
||||
right = 1,
|
||||
bot = 1,
|
||||
left = 1,
|
||||
}
|
||||
|
||||
local calc_left_start = function(title_pos, title_len, total_width)
|
||||
if string.find(title_pos, "W") then
|
||||
return 0
|
||||
elseif string.find(title_pos, "E") then
|
||||
return total_width - title_len
|
||||
else
|
||||
return math.floor((total_width - title_len) / 2)
|
||||
end
|
||||
end
|
||||
|
||||
local create_horizontal_line = function(title, pos, width, left_char, mid_char, right_char)
|
||||
local title_len
|
||||
if title == "" then
|
||||
title_len = 0
|
||||
else
|
||||
local len = strings.strdisplaywidth(title)
|
||||
local max_title_width = width - 2
|
||||
if len > max_title_width then
|
||||
title = strings.truncate(title, max_title_width)
|
||||
len = strings.strdisplaywidth(title)
|
||||
end
|
||||
title = string.format(" %s ", title)
|
||||
title_len = len + 2
|
||||
end
|
||||
|
||||
local left_start = calc_left_start(pos, title_len, width)
|
||||
|
||||
local horizontal_line = string.format(
|
||||
"%s%s%s%s%s",
|
||||
left_char,
|
||||
string.rep(mid_char, left_start),
|
||||
title,
|
||||
string.rep(mid_char, width - title_len - left_start),
|
||||
right_char
|
||||
)
|
||||
local ranges = {}
|
||||
if title_len ~= 0 then
|
||||
-- Need to calculate again due to multi-byte characters
|
||||
local r_start = string.len(left_char) + math.max(left_start, 0) * string.len(mid_char)
|
||||
ranges = { { r_start, r_start + string.len(title) } }
|
||||
end
|
||||
return horizontal_line, ranges
|
||||
end
|
||||
|
||||
function Border._create_lines(content_win_id, content_win_options, border_win_options)
|
||||
local content_pos = vim.api.nvim_win_get_position(content_win_id)
|
||||
local content_height = vim.api.nvim_win_get_height(content_win_id)
|
||||
local content_width = vim.api.nvim_win_get_width(content_win_id)
|
||||
|
||||
-- TODO: Handle border width, which I haven't right here.
|
||||
local thickness = border_win_options.border_thickness
|
||||
|
||||
local top_enabled = thickness.top == 1
|
||||
local right_enabled = thickness.right == 1 and content_pos[2] + content_width < vim.o.columns
|
||||
local bot_enabled = thickness.bot == 1
|
||||
local left_enabled = thickness.left == 1 and content_pos[2] > 0
|
||||
|
||||
border_win_options.border_thickness.left = left_enabled and 1 or 0
|
||||
border_win_options.border_thickness.right = right_enabled and 1 or 0
|
||||
|
||||
local border_lines = {}
|
||||
local ranges = {}
|
||||
|
||||
-- border_win_options.title should have be a list with entries of the
|
||||
-- form: { pos = foo, text = bar }.
|
||||
-- pos can take values in { "NW", "N", "NE", "SW", "S", "SE" }
|
||||
local titles = type(border_win_options.title) == "string" and { { pos = "N", text = border_win_options.title } }
|
||||
or border_win_options.title
|
||||
or {}
|
||||
|
||||
local topline = nil
|
||||
local topleft = (left_enabled and border_win_options.topleft) or ""
|
||||
local topright = (right_enabled and border_win_options.topright) or ""
|
||||
-- Only calculate the topline if there is space above the first content row (relative to the editor)
|
||||
if content_pos[1] > 0 then
|
||||
for _, title in ipairs(titles) do
|
||||
if string.find(title.pos, "N") then
|
||||
local top_ranges
|
||||
topline, top_ranges = create_horizontal_line(
|
||||
title.text,
|
||||
title.pos,
|
||||
content_win_options.width,
|
||||
topleft,
|
||||
border_win_options.top or "",
|
||||
topright
|
||||
)
|
||||
for _, r in pairs(top_ranges) do
|
||||
table.insert(ranges, { 0, r[1], r[2] })
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
if topline == nil then
|
||||
if top_enabled then
|
||||
topline = topleft .. string.rep(border_win_options.top, content_win_options.width) .. topright
|
||||
end
|
||||
end
|
||||
else
|
||||
border_win_options.border_thickness.top = 0
|
||||
end
|
||||
|
||||
if topline then
|
||||
table.insert(border_lines, topline)
|
||||
end
|
||||
|
||||
local middle_line = string.format(
|
||||
"%s%s%s",
|
||||
(left_enabled and border_win_options.left) or "",
|
||||
string.rep(" ", content_win_options.width),
|
||||
(right_enabled and border_win_options.right) or ""
|
||||
)
|
||||
|
||||
for _ = 1, content_win_options.height do
|
||||
table.insert(border_lines, middle_line)
|
||||
end
|
||||
|
||||
local botline = nil
|
||||
local botleft = (left_enabled and border_win_options.botleft) or ""
|
||||
local botright = (right_enabled and border_win_options.botright) or ""
|
||||
if content_pos[1] + content_height < vim.o.lines then
|
||||
for _, title in ipairs(titles) do
|
||||
if string.find(title.pos, "S") then
|
||||
local bot_ranges
|
||||
botline, bot_ranges = create_horizontal_line(
|
||||
title.text,
|
||||
title.pos,
|
||||
content_win_options.width,
|
||||
botleft,
|
||||
border_win_options.bot or "",
|
||||
botright
|
||||
)
|
||||
for _, r in pairs(bot_ranges) do
|
||||
table.insert(ranges, { content_win_options.height + thickness.top, r[1], r[2] })
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
if botline == nil then
|
||||
if bot_enabled then
|
||||
botline = botleft .. string.rep(border_win_options.bot, content_win_options.width) .. botright
|
||||
end
|
||||
end
|
||||
else
|
||||
border_win_options.border_thickness.bot = 0
|
||||
end
|
||||
|
||||
if botline then
|
||||
table.insert(border_lines, botline)
|
||||
end
|
||||
|
||||
return border_lines, ranges
|
||||
end
|
||||
|
||||
local set_title_highlights = function(bufnr, ranges, hl)
|
||||
-- Check if both `hl` and `ranges` are provided, and `ranges` is not the empty table.
|
||||
if hl and ranges and next(ranges) then
|
||||
for _, r in pairs(ranges) do
|
||||
vim.api.nvim_buf_add_highlight(bufnr, -1, hl, r[1], r[2], r[3])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Border:change_title(new_title, pos)
|
||||
if self._border_win_options.title == new_title then
|
||||
return
|
||||
end
|
||||
|
||||
pos = pos
|
||||
or (self._border_win_options.title and self._border_win_options.title[1] and self._border_win_options.title[1].pos)
|
||||
if pos == nil then
|
||||
self._border_win_options.title = new_title
|
||||
else
|
||||
self._border_win_options.title = { { text = new_title, pos = pos } }
|
||||
end
|
||||
|
||||
self.contents, self.title_ranges =
|
||||
Border._create_lines(self.content_win_id, self.content_win_options, self._border_win_options)
|
||||
vim.api.nvim_buf_set_lines(self.bufnr, 0, -1, false, self.contents)
|
||||
|
||||
set_title_highlights(self.bufnr, self.title_ranges, self._border_win_options.titlehighlight)
|
||||
end
|
||||
|
||||
-- Updates characters for border lines, and returns nvim_win_config
|
||||
-- (generally used in conjunction with `move` or `new`)
|
||||
function Border:__align_calc_config(content_win_options, border_win_options)
|
||||
border_win_options = vim.tbl_deep_extend("keep", border_win_options, {
|
||||
border_thickness = Border._default_thickness,
|
||||
|
||||
-- Border options, could be passed as a list?
|
||||
topleft = "╔",
|
||||
topright = "╗",
|
||||
top = "═",
|
||||
left = "║",
|
||||
right = "║",
|
||||
botleft = "╚",
|
||||
botright = "╝",
|
||||
bot = "═",
|
||||
})
|
||||
|
||||
-- Ensure the relevant contents and border win_options are set
|
||||
self._border_win_options = border_win_options
|
||||
self.content_win_options = content_win_options
|
||||
-- Update border characters and title_ranges
|
||||
self.contents, self.title_ranges = Border._create_lines(self.content_win_id, content_win_options, border_win_options)
|
||||
|
||||
vim.api.nvim_buf_set_option(self.bufnr, "modifiable", true)
|
||||
vim.api.nvim_buf_set_lines(self.bufnr, 0, -1, false, self.contents)
|
||||
|
||||
local thickness = border_win_options.border_thickness
|
||||
local nvim_win_config = {
|
||||
anchor = content_win_options.anchor,
|
||||
relative = content_win_options.relative,
|
||||
style = "minimal",
|
||||
row = content_win_options.row - thickness.top,
|
||||
col = content_win_options.col - thickness.left,
|
||||
width = content_win_options.width + thickness.left + thickness.right,
|
||||
height = content_win_options.height + thickness.top + thickness.bot,
|
||||
zindex = content_win_options.zindex or 50,
|
||||
noautocmd = content_win_options.noautocmd,
|
||||
focusable = vim.F.if_nil(border_win_options.focusable, false),
|
||||
border = "none",
|
||||
}
|
||||
|
||||
return nvim_win_config
|
||||
end
|
||||
|
||||
-- Sets the size and position of the given Border.
|
||||
-- Can be used to create a new window (with `create_window = true`)
|
||||
-- or change an existing one
|
||||
function Border:move(content_win_options, border_win_options)
|
||||
-- Update lines in border buffer, and get config for border window
|
||||
local nvim_win_config = self:__align_calc_config(content_win_options, border_win_options)
|
||||
|
||||
-- Set config for border window
|
||||
vim.api.nvim_win_set_config(self.win_id, nvim_win_config)
|
||||
|
||||
set_title_highlights(self.bufnr, self.title_ranges, self._border_win_options.titlehighlight)
|
||||
end
|
||||
|
||||
function Border:new(content_bufnr, content_win_id, content_win_options, border_win_options)
|
||||
assert(type(content_win_id) == "number", "Must supply a valid win_id. It's possible you forgot to call with ':'")
|
||||
|
||||
local obj = {}
|
||||
|
||||
obj.content_win_id = content_win_id
|
||||
|
||||
obj.bufnr = vim.api.nvim_create_buf(false, true)
|
||||
assert(obj.bufnr, "Failed to create border buffer")
|
||||
vim.api.nvim_buf_set_option(obj.bufnr, "bufhidden", "wipe")
|
||||
|
||||
-- Create a border window and buffer, with border characters around the edge
|
||||
local nvim_win_config = Border.__align_calc_config(obj, content_win_options, border_win_options)
|
||||
obj.win_id = vim.api.nvim_open_win(obj.bufnr, false, nvim_win_config)
|
||||
|
||||
if border_win_options.highlight then
|
||||
vim.api.nvim_win_set_option(obj.win_id, "winhl", border_win_options.highlight)
|
||||
end
|
||||
|
||||
set_title_highlights(obj.bufnr, obj.title_ranges, obj._border_win_options.titlehighlight)
|
||||
|
||||
vim.cmd(
|
||||
string.format(
|
||||
"autocmd BufDelete <buffer=%s> ++nested ++once :lua require('plenary.window').close_related_win(%s, %s)",
|
||||
content_bufnr,
|
||||
content_win_id,
|
||||
obj.win_id
|
||||
)
|
||||
)
|
||||
|
||||
vim.cmd(
|
||||
string.format(
|
||||
"autocmd WinClosed <buffer=%s> ++nested ++once :lua require('plenary.window').try_close(%s, true)",
|
||||
content_bufnr,
|
||||
obj.win_id
|
||||
)
|
||||
)
|
||||
|
||||
setmetatable(obj, Border)
|
||||
|
||||
return obj
|
||||
end
|
||||
|
||||
return Border
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
local Border = require "plenary.window.border"
|
||||
local tbl = require "plenary.tbl"
|
||||
|
||||
_AssociatedBufs = {}
|
||||
|
||||
local clear_buf_on_leave = function(bufnr)
|
||||
vim.cmd(
|
||||
string.format(
|
||||
"autocmd WinLeave,BufLeave,BufDelete <buffer=%s> ++once ++nested lua require('plenary.window.float').clear(%s)",
|
||||
bufnr,
|
||||
bufnr
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
local win_float = {}
|
||||
|
||||
win_float.default_options = {
|
||||
winblend = 15,
|
||||
percentage = 0.9,
|
||||
}
|
||||
|
||||
function win_float.default_opts(options)
|
||||
options = tbl.apply_defaults(options, win_float.default_options)
|
||||
|
||||
local width = math.floor(vim.o.columns * options.percentage)
|
||||
local height = math.floor(vim.o.lines * options.percentage)
|
||||
|
||||
local top = math.floor(((vim.o.lines - height) / 2) - 1)
|
||||
local left = math.floor((vim.o.columns - width) / 2)
|
||||
|
||||
local opts = {
|
||||
relative = "editor",
|
||||
row = top,
|
||||
col = left,
|
||||
width = width,
|
||||
height = height,
|
||||
style = "minimal",
|
||||
}
|
||||
|
||||
return opts
|
||||
end
|
||||
|
||||
function win_float.centered(options)
|
||||
options = tbl.apply_defaults(options, win_float.default_options)
|
||||
|
||||
local win_opts = win_float.default_opts(options)
|
||||
|
||||
local bufnr = options.bufnr or vim.api.nvim_create_buf(false, true)
|
||||
local win_id = vim.api.nvim_open_win(bufnr, true, win_opts)
|
||||
|
||||
vim.cmd "setlocal nocursorcolumn"
|
||||
vim.api.nvim_win_set_option(win_id, "winblend", options.winblend)
|
||||
|
||||
vim.cmd(string.format("autocmd WinLeave <buffer> silent! execute 'bdelete! %s'", bufnr))
|
||||
|
||||
return {
|
||||
bufnr = bufnr,
|
||||
win_id = win_id,
|
||||
}
|
||||
end
|
||||
|
||||
function win_float.centered_with_top_win(top_text, options)
|
||||
options = tbl.apply_defaults(options, win_float.default_options)
|
||||
|
||||
table.insert(top_text, 1, string.rep("=", 80))
|
||||
table.insert(top_text, string.rep("=", 80))
|
||||
|
||||
local primary_win_opts = win_float.default_opts(nil, nil, options)
|
||||
local minor_win_opts = vim.deepcopy(primary_win_opts)
|
||||
|
||||
primary_win_opts.height = primary_win_opts.height - #top_text - 1
|
||||
primary_win_opts.row = primary_win_opts.row + #top_text + 1
|
||||
|
||||
minor_win_opts.height = #top_text
|
||||
|
||||
local minor_bufnr = vim.api.nvim_create_buf(false, true)
|
||||
local minor_win_id = vim.api.nvim_open_win(minor_bufnr, true, minor_win_opts)
|
||||
|
||||
vim.cmd "setlocal nocursorcolumn"
|
||||
vim.api.nvim_win_set_option(minor_win_id, "winblend", options.winblend)
|
||||
|
||||
vim.api.nvim_buf_set_lines(minor_bufnr, 0, -1, false, top_text)
|
||||
|
||||
local primary_bufnr = vim.api.nvim_create_buf(false, true)
|
||||
local primary_win_id = vim.api.nvim_open_win(primary_bufnr, true, primary_win_opts)
|
||||
|
||||
vim.cmd "setlocal nocursorcolumn"
|
||||
vim.api.nvim_win_set_option(primary_win_id, "winblend", options.winblend)
|
||||
|
||||
-- vim.cmd(
|
||||
-- string.format(
|
||||
-- "autocmd WinLeave,BufDelete,BufLeave <buffer=%s> ++once ++nested silent! execute 'bdelete! %s'",
|
||||
-- primary_buf,
|
||||
-- minor_buf
|
||||
-- )
|
||||
-- )
|
||||
|
||||
-- vim.cmd(
|
||||
-- string.format(
|
||||
-- "autocmd WinLeave,BufDelete,BufLeave <buffer> ++once ++nested silent! execute 'bdelete! %s'",
|
||||
-- primary_buf
|
||||
-- )
|
||||
-- )
|
||||
|
||||
local primary_border = Border:new(primary_bufnr, primary_win_id, primary_win_opts, {})
|
||||
local minor_border = Border:new(minor_bufnr, minor_win_id, minor_win_opts, {})
|
||||
|
||||
_AssociatedBufs[primary_bufnr] = {
|
||||
primary_win_id,
|
||||
minor_win_id,
|
||||
primary_border.win_id,
|
||||
minor_border.win_id,
|
||||
}
|
||||
|
||||
clear_buf_on_leave(primary_bufnr)
|
||||
|
||||
return {
|
||||
bufnr = primary_bufnr,
|
||||
win_id = primary_win_id,
|
||||
|
||||
minor_bufnr = minor_bufnr,
|
||||
minor_win_id = minor_win_id,
|
||||
}
|
||||
end
|
||||
|
||||
--- Create window that takes up certain percentags of the current screen.
|
||||
---
|
||||
--- Works regardless of current buffers, tabs, splits, etc.
|
||||
--@param col_range number | Table:
|
||||
-- If number, then center the window taking up this percentage of the screen.
|
||||
-- If table, first index should be start, second_index should be end
|
||||
--@param row_range number | Table:
|
||||
-- If number, then center the window taking up this percentage of the screen.
|
||||
-- If table, first index should be start, second_index should be end
|
||||
--@param win_opts Table
|
||||
--@param border_opts Table
|
||||
function win_float.percentage_range_window(col_range, row_range, win_opts, border_opts)
|
||||
win_opts = tbl.apply_defaults(win_opts, win_float.default_options)
|
||||
|
||||
local default_win_opts = win_float.default_opts(win_opts)
|
||||
default_win_opts.relative = "editor"
|
||||
|
||||
local height_percentage, row_start_percentage
|
||||
if type(row_range) == "number" then
|
||||
assert(row_range <= 1)
|
||||
assert(row_range > 0)
|
||||
height_percentage = row_range
|
||||
row_start_percentage = (1 - height_percentage) / 2
|
||||
elseif type(row_range) == "table" then
|
||||
height_percentage = row_range[2] - row_range[1]
|
||||
row_start_percentage = row_range[1]
|
||||
else
|
||||
error(string.format("Invalid type for 'row_range': %p", row_range))
|
||||
end
|
||||
|
||||
default_win_opts.height = math.ceil(vim.o.lines * height_percentage)
|
||||
default_win_opts.row = math.ceil(vim.o.lines * row_start_percentage)
|
||||
|
||||
local width_percentage, col_start_percentage
|
||||
if type(col_range) == "number" then
|
||||
assert(col_range <= 1)
|
||||
assert(col_range > 0)
|
||||
width_percentage = col_range
|
||||
col_start_percentage = (1 - width_percentage) / 2
|
||||
elseif type(col_range) == "table" then
|
||||
width_percentage = col_range[2] - col_range[1]
|
||||
col_start_percentage = col_range[1]
|
||||
else
|
||||
error(string.format("Invalid type for 'col_range': %p", col_range))
|
||||
end
|
||||
|
||||
default_win_opts.col = math.floor(vim.o.columns * col_start_percentage)
|
||||
default_win_opts.width = math.floor(vim.o.columns * width_percentage)
|
||||
|
||||
local bufnr = win_opts.bufnr or vim.api.nvim_create_buf(false, true)
|
||||
local win_id = vim.api.nvim_open_win(bufnr, true, default_win_opts)
|
||||
vim.api.nvim_win_set_buf(win_id, bufnr)
|
||||
|
||||
vim.cmd "setlocal nocursorcolumn"
|
||||
vim.api.nvim_win_set_option(win_id, "winblend", win_opts.winblend)
|
||||
|
||||
local border = Border:new(bufnr, win_id, default_win_opts, border_opts or {})
|
||||
|
||||
_AssociatedBufs[bufnr] = { win_id, border.win_id }
|
||||
|
||||
clear_buf_on_leave(bufnr)
|
||||
|
||||
return {
|
||||
bufnr = bufnr,
|
||||
win_id = win_id,
|
||||
|
||||
border_bufnr = border.bufnr,
|
||||
border_win_id = border.win_id,
|
||||
}
|
||||
end
|
||||
|
||||
function win_float.clear(bufnr)
|
||||
if _AssociatedBufs[bufnr] == nil then
|
||||
return
|
||||
end
|
||||
|
||||
for _, win_id in ipairs(_AssociatedBufs[bufnr]) do
|
||||
if vim.api.nvim_win_is_valid(win_id) then
|
||||
vim.api.nvim_win_close(win_id, true)
|
||||
end
|
||||
end
|
||||
|
||||
_AssociatedBufs[bufnr] = nil
|
||||
end
|
||||
|
||||
return win_float
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
local window = {}
|
||||
|
||||
window.try_close = function(win_id, force)
|
||||
if force == nil then
|
||||
force = true
|
||||
end
|
||||
|
||||
pcall(vim.api.nvim_win_close, win_id, force)
|
||||
end
|
||||
|
||||
window.close_related_win = function(parent_win_id, child_win_id)
|
||||
window.try_close(parent_win_id, true)
|
||||
window.try_close(child_win_id, true)
|
||||
end
|
||||
|
||||
return window
|
||||
Loading…
Add table
Add a link
Reference in a new issue