Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: "spacers" #12

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
245 changes: 163 additions & 82 deletions lua/alpha.lua
Original file line number Diff line number Diff line change
Expand Up @@ -74,74 +74,81 @@ end
-- end


local layout_element = {}
local resolve_element = {}
local render_element = {}

layout_element.text = function(el, opts, state)
if type(el.val) == "table" then
local end_ln = state.line + #el.val
local val = el.val
if opts.opts and opts.opts.margin and el.opts and (el.opts.position ~= "center") then
val = pad_margin(val, state, opts.opts.margin, if_nil(el.opts.shrink_margin, true))
end
if el.opts then
if el.opts.position == "center" then
val, _ = center(val, state)
end
-- if el.opts.wrap == "overflow" then
-- val = trim(val, state)
-- end
end
vim.api.nvim_buf_set_lines(state.buffer, state.line, state.line, false, val)
if el.opts and el.opts.hl then
for i = state.line, end_ln do
vim.api.nvim_buf_add_highlight(state.buffer, -1, el.opts.hl, i, 0, -1)
end
end
state.line = end_ln
resolve_element.text = function(layout, el, opts, state)
local val
if type(el.val) == "function" then
val = el.val()
el.val = val
resolve_element.text(layout, el, opts, state)
return
end

if type(el.val) == "string" then
local val = {}

if type(el.val) == "table" then
val = el.val
elseif type(el.val) == "string" then
val = {}
for s in el.val:gmatch("[^\r\n]+") do
val[#val+1] = s
end
if opts.opts and opts.opts.margin and el.opts and (el.opts.position ~= "center") then
val = pad_margin(val, state, opts.opts.margin, if_nil(el.opts.shrink_margin, true))
end
if el.opts then
if el.opts.position == "center" then
val, _ = center(val, state)
end
end
vim.api.nvim_buf_set_lines(state.buffer, state.line, state.line, false, val)
local end_ln = state.line + #val
if el.opts and el.opts.hl then
for i = state.line, end_ln do
vim.api.nvim_buf_add_highlight(state.buffer, -1, el.opts.hl, i, 0, -1)
end
end

if opts.opts and opts.opts.margin and el.opts and (el.opts.position ~= "center") then
val = pad_margin(val, state, opts.opts.margin, if_nil(el.opts.shrink_margin, true))
end
if el.opts then
if el.opts.position == "center" then
val, _ = center(val, state)
end
state.line = end_ln
-- if el.opts.wrap == "overflow" then
-- val = trim(val, state)
-- end
end

if type(el.val) == "function" then
local val = el.val()
el.val = val
layout_element.text(el, opts, state)
el = vim.deepcopy(el)
el.val = val
local end_ln = state.line + #val
el.start_ln = state.line
el.end_ln = end_ln
state.line = end_ln

table.insert(layout, el)
end

render_element.text = function(el, _, state)
vim.api.nvim_buf_set_lines(state.buffer, el.start_ln, el.end_ln, false, el.val)
if el.opts and el.opts.hl then
for i = el.start_ln, el.end_ln do
vim.api.nvim_buf_add_highlight(state.buffer, -1, el.opts.hl, i, 0, -1)
end
end
end

layout_element.padding = function(el, opts, state)
resolve_element.padding = function(layout, el, _, state)
local end_ln = state.line + el.val
local val = {}
for i = 1, el.val + 1 do
val[i] = ""
end
vim.api.nvim_buf_set_lines(state.buffer, state.line, end_ln, false, val)
local p = { type = "text", start_ln = state.line, end_ln = end_ln, val = val }
table.insert(layout, p)
state.line = end_ln
end

layout_element.button = function(el, opts, state)
-- render_element.padding is not needed (uses text renderer)

resolve_element.spacer = function (layout, el, _, state)
local s = { type = "spacer", start_ln = state.line, end_ln = state.line, val = el.val }
table.insert(layout, s)
table.insert(state.spacers, s)
end

-- render_element.spacer is not needed (uses text renderer, see layout_spacers)

resolve_element.button = function(layout, el, opts, state)
local val
local padding = {
left = 0,
Expand All @@ -152,14 +159,16 @@ layout_element.button = function(el, opts, state)
-- this min lets the padding resize when the window gets smaller
if el.opts.width then
local max_width = math.min(el.opts.width, state.win_width)
if el.opts.align_shortcut == "right"
then padding.center = max_width - (#el.val + #el.opts.shortcut)
else padding.right = max_width - (#el.val + #el.opts.shortcut)
if el.opts.align_shortcut == "right" then
padding.center = max_width - (#el.val + #el.opts.shortcut)
else
padding.right = max_width - (#el.val + #el.opts.shortcut)
end
end
if el.opts.align_shortcut == "right"
then val = {el.val .. spaces(padding.center) .. el.opts.shortcut}
else val = {el.opts.shortcut .. " " .. el.val .. spaces(padding.right)}
if el.opts.align_shortcut == "right" then
val = {el.val .. spaces(padding.center) .. el.opts.shortcut}
else
val = {el.opts.shortcut .. " " .. el.val .. spaces(padding.right)}
end
else
val = {el.val}
Expand All @@ -168,9 +177,10 @@ layout_element.button = function(el, opts, state)
-- margin
if opts.opts and opts.opts.margin and el.opts and (el.opts.position ~= "center") then
val = pad_margin(val, state, opts.opts.margin, if_nil(el.opts.shrink_margin, true))
if el.opts.align_shortcut == "right"
then padding.center = padding.center + opts.opts.margin
else padding.left = padding.left + opts.opts.margin
if el.opts.align_shortcut == "right" then
padding.center = padding.center + opts.opts.margin
else
padding.left = padding.left + opts.opts.margin
end
end

Expand All @@ -179,23 +189,39 @@ layout_element.button = function(el, opts, state)
if el.opts.position == "center" then
local left
val, left = center(val, state)
if el.opts.align_shortcut == "right"
then padding.center = padding.center + left
else padding.left = padding.left + left
if el.opts.align_shortcut == "right" then
padding.center = padding.center + left
else
padding.left = padding.left + left
end
end
end

local row = state.line + 1
local _, count_spaces = string.find(val[1], "%s*")
el = vim.deepcopy(el)
el.padding = padding
el.text = val
local end_ln = state.line + #val
el.start_ln = state.line
el.end_ln = end_ln
state.line = end_ln

table.insert(layout, el)
end

render_element.button = function(el, _, state)

local row = el.start_ln + 1
local _, count_spaces = string.find(el.text[1], "%s*")
local col = ((el.opts and el.opts.cursor) or 0) + count_spaces
cursor_jumps[#cursor_jumps+1] = {row, col}
cursor_jumps_press[#cursor_jumps_press+1] = el.on_press
vim.api.nvim_buf_set_lines(state.buffer, state.line, state.line, false, val)
vim.api.nvim_buf_set_lines(state.buffer, el.start_ln, el.end_ln, false, el.text)
local padding = el.padding
if el.opts and el.opts.hl_shortcut then
if el.opts.align_shortcut == "right"
then vim.api.nvim_buf_add_highlight(state.buffer, -1, el.opts.hl_shortcut, state.line, #el.val + padding.center, -1)
else vim.api.nvim_buf_add_highlight(state.buffer, -1, el.opts.hl_shortcut, state.line, padding.left, padding.left + #el.opts.shortcut)
if el.opts.align_shortcut == "right" then
vim.api.nvim_buf_add_highlight(state.buffer, -1, el.opts.hl_shortcut, el.start_ln, #el.val + padding.center, -1)
else
vim.api.nvim_buf_add_highlight(state.buffer, -1, el.opts.hl_shortcut, el.start_ln, padding.left, padding.left + #el.opts.shortcut)
end
end

Expand All @@ -207,7 +233,7 @@ layout_element.button = function(el, opts, state)
state.buffer,
-1,
hl[1],
state.line,
el.start_ln,
left + hl[2],
left + hl[3]
)
Expand All @@ -216,28 +242,80 @@ layout_element.button = function(el, opts, state)
state.line = state.line + 1
end

layout_element.group = function(el, opts, state)
resolve_element.group = function(layout, el, opts, state)
for _, v in pairs(el.val) do
layout_element[v.type](v, opts, state)
resolve_element[v.type](layout, v, opts, state)
if el.opts and el.opts.spacing then
local padding_el = {type = "padding", val = el.opts.spacing}
layout_element[padding_el.type](padding_el, opts, state)
resolve_element[padding_el.type](layout, padding_el, opts, state)
end
end
end

local function layout(opts, state)
-- render_element.group is not needed

local function layout_spacers(layout, opts, state)
local space = vim.api.nvim_win_get_height(state.window) - layout[#layout].end_ln
space = math.max(space, 0)

local space_lines = {}
for _ = 1, space do
table.insert(space_lines, "")
end

local tot_val = 0
for _, s in ipairs(state.spacers) do
tot_val = tot_val + s.val
end

local cur_shift = 0
for _, el in ipairs(layout) do
if el.type == "spacer" then
local shift = math.floor(el.val / tot_val * space)
el.type = "text"
el.val = {unpack(space_lines, 1, shift)}
el.start_ln = el.start_ln + cur_shift
el.end_ln = el.end_ln + cur_shift + shift

-- Adjust current total shift
cur_shift = cur_shift + shift
else
el.start_ln = el.start_ln + cur_shift
el.end_ln = el.end_ln + cur_shift
end
end

-- -- Correct cursor_jumps list for additional spacing
-- for k, v in ipairs(cursor_jumps) do
-- local shift = (v[3] == 0) and 0 or state.spacers[v[3]].shift
-- cursor_jumps[k][1] = v[1] + shift
-- cursor_jumps[k][3] = nil
-- end
end

local function resolve_layout(opts, state)
local layout = {}
-- this is my way of hacking pattern matching
-- you index the table by its "type"
for _, el in pairs(opts.layout) do
layout_element[el.type](el, opts, state)
resolve_element[el.type](layout, el, opts, state)
end

layout_spacers(layout, opts, state)
return layout
end

local function render_layout(layout, opts, state)
for _, el in pairs(layout) do
render_element[el.type](el, _, state)
end
end

local keymaps_element = {}

keymaps_element.text = function () end
keymaps_element.padding = function () end
keymaps_element.spacer = function () end

keymaps_element.button = function (el, opts, state)
if el.opts and el.opts.keymap then
Expand Down Expand Up @@ -280,16 +358,16 @@ local function closest_cursor_jump(cursor, cursors, prev_cursor)
if min[1] > res[1] then min = res end
end
end
if not min -- top or bottom
then
if direction
then return 1, cursors[1]
else return #cursors, cursors[#cursors]
end
if not min then -- top or bottom
if direction then
return 1, cursors[1]
else
-- returns the key (stored in a jank way so we can sort the table)
-- and the {row, col} tuple
return min[2], cursors[min[2]]
return #cursors, cursors[#cursors]
end
else
-- returns the key (stored in a jank way so we can sort the table)
-- and the {row, col} tuple
return min[2], cursors[min[2]]
end
end

Expand Down Expand Up @@ -340,20 +418,23 @@ local function start(on_vimenter, opts)
line = 0,
buffer = buffer,
window = window,
win_width = 0
win_width = 0,
spacers = {},
}
local function draw()
for k in pairs(cursor_jumps) do cursor_jumps[k] = nil end
for k in pairs(cursor_jumps_press) do cursor_jumps_press[k] = nil end
state.win_width = vim.api.nvim_win_get_width(state.window)
state.line = 0
state.spacers = {}
-- this is for redraws. i guess the cursor 'moves'
-- when the screen is cleared and then redrawn
-- so we save the index before that happens
local ix = cursor_ix
vim.api.nvim_buf_set_option(state.buffer, "modifiable", true)
vim.api.nvim_buf_set_lines(state.buffer, 0, -1, false, {})
layout(opts, state)
local layout = resolve_layout(opts, state)
render_layout(layout, opts, state)
vim.api.nvim_buf_set_option(state.buffer, "modifiable", false)
vim.api.nvim_buf_set_keymap(
state.buffer,
Expand Down