1
argonaut.nvim/lua/argonaut/types.lua

375 lines
8.8 KiB
Lua
Raw Normal View History

2024-04-22 01:11:56 +00:00
--
-- Cursor
--
2024-04-22 00:20:57 +00:00
2024-04-22 01:11:56 +00:00
Cursor = {}
2024-04-22 00:20:57 +00:00
2024-04-22 01:11:56 +00:00
function Cursor.new(row, col)
local cursor = {row = row, col = col}
2024-04-22 00:20:57 +00:00
return setmetatable(cursor, {__index = Cursor})
end
2024-04-22 04:57:41 +00:00
function Cursor:is_valid()
return self.row > 0 and self.col > 0
end
function Cursor:is_string()
2024-04-25 02:58:01 +00:00
assert(self:is_valid())
2024-04-22 01:11:56 +00:00
local syn_id = vim.fn.synID(self.row, self.col, false)
local syn_attr = vim.fn.synIDattr(syn_id, 'name')
return syn_attr:find('String$')
end
2024-04-25 02:58:01 +00:00
function Cursor.get_current()
local _, row, col, _ = unpack(vim.fn.getpos('.'))
return Cursor.new(row, col)
end
function Cursor:set_current()
assert(self:is_valid())
vim.fn.secursorcharpos({self.row, self.col})
end
2024-04-22 01:11:56 +00:00
--
-- BracePair
--
2024-04-22 00:20:57 +00:00
BracePair = {}
2024-04-25 02:58:01 +00:00
function BracePair.new(open, close)
local brace_pair = {open = open, close = close}
2024-04-22 01:11:56 +00:00
return setmetatable(brace_pair, {__index = BracePair})
end
function BracePair.from_brace(brace)
2024-04-25 02:58:01 +00:00
local brace_pairs = {
{'(', ')'},
{'[', ']'},
{'{', '}'},
{'<', '>'},
}
for _, brace_pair in ipairs(brace_pairs) do
2024-04-23 05:36:25 +00:00
if brace_pair[1] == brace or brace_pair[2] == brace then
return BracePair.new(brace_pair[1], brace_pair[2])
2024-04-22 01:11:56 +00:00
end
end
end
function BracePair:escaped()
2024-04-25 02:58:01 +00:00
local escape_func = function(brace_raw)
2024-04-22 00:20:57 +00:00
if brace_raw == '[' or brace_raw == ']' then
return '\\' .. brace_raw
else
return brace_raw
end
end
2024-04-25 02:58:01 +00:00
return BracePair.new(
escape_func(self.open),
escape_func(self.close)
)
2024-04-22 01:11:56 +00:00
end
2024-04-25 02:58:01 +00:00
function BracePair:find_closest(backward, cursor)
if not cursor then
cursor = Cursor.get_current()
end
2024-04-22 01:11:56 +00:00
-- See flags: https://neovim.io/doc/user/builtin.html#search()
local flags = 'Wcn'
if backward then
flags = 'Wbn'
end
2024-04-25 02:58:01 +00:00
local ignore_func = function()
return cursor:is_string()
end
2024-04-22 01:11:56 +00:00
local escaped_pair = self:escaped()
local position = vim.fn.searchpairpos(
escaped_pair.open,
'',
escaped_pair.close,
flags,
2024-04-25 02:58:01 +00:00
ignore_func
2024-04-22 01:11:56 +00:00
)
2024-04-25 02:58:01 +00:00
local brace_cursor = Cursor.new(unpack(position))
if brace_cursor:is_valid() then
return brace_cursor
2024-04-22 00:20:57 +00:00
end
end
2024-04-23 05:36:25 +00:00
--
-- BraceStack
--
BraceStack = {}
function BraceStack.new()
local stack = {stack = {}}
return setmetatable(stack, {__index = BraceStack})
end
function BraceStack:update(brace)
2024-04-25 02:39:09 +00:00
local brace_pair = BracePair.from_brace(brace)
2024-04-23 05:36:25 +00:00
if brace_pair then
if brace == brace_pair.close and self:top() == brace_pair.open then
self:pop()
else
self:push(brace)
end
end
end
function BraceStack:push(brace)
table.insert(self.stack, brace)
end
function BraceStack:pop()
assert(not self:empty())
return table.remove(self.stack, #self.stack)
end
function BraceStack:empty()
return #self.stack == 0
end
function BraceStack:top()
return #self.stack[#self.stack]
end
2024-04-22 01:11:56 +00:00
--
-- BraceRange
--
2024-04-22 00:20:57 +00:00
BraceRange = {}
function BraceRange.new(start_cursor, stop_cursor, brace_pair, brace_params)
local brace_range = {
start_cursor = start_cursor,
stop_cursor = stop_cursor,
brace_pair = brace_pair,
2024-04-22 01:11:56 +00:00
brace_params = brace_params,
2024-04-22 00:20:57 +00:00
}
return setmetatable(brace_range, {__index = BraceRange})
end
2024-04-22 01:11:56 +00:00
2024-04-25 02:58:01 +00:00
function BraceRange.find_closest(brace_pair)
local stop_cursor = brace_pair:find_closest(false)
2024-04-25 02:39:09 +00:00
if stop_cursor then
2024-04-25 02:58:01 +00:00
local start_cursor = brace_pair:find_closest(true)
2024-04-25 02:39:09 +00:00
if start_cursor then
2024-04-22 01:11:56 +00:00
return BraceRange.new(start_cursor, stop_cursor, brace_pair, {})
end
end
end
2024-04-22 04:57:41 +00:00
2024-04-25 02:58:01 +00:00
function BraceRange.find_closest_any()
2024-04-22 04:57:41 +00:00
local brace_range_compare = function(brace_range_1, brace_range_2)
2024-04-25 02:58:01 +00:00
local cursor = Cursor:get_current()
2024-04-22 04:57:41 +00:00
local row_diff1 = cursor.row - brace_range_1.start_cursor.row
local row_diff2 = cursor.row - brace_range_2.start_cursor.row
if row_diff1 < row_diff2 then
return -1
elseif row_diff1 > row_diff2 then
return 1
end
local col_diff1 = cursor.col - brace_range_1.start_cursor.col
local col_diff2 = cursor.col - brace_range_2.start_cursor.col
if col_diff1 < col_diff2 then
return -1
elseif col_diff1 > col_diff2 then
return 1
end
return 0
end
local brace_ranges = {}
for _, brace in ipairs({'(', '[', '{', '<'}) do
local brace_pair = BracePair.from_brace(brace)
2024-04-25 02:58:01 +00:00
local brace_range = BraceRange.find_closest(brace_pair)
2024-04-22 04:57:41 +00:00
if brace_range then
table.insert(brace_ranges, brace_range)
end
end
2024-04-22 05:07:24 +00:00
if #brace_ranges > 0 then
2024-04-25 02:39:09 +00:00
vim.fn.sort(brace_ranges, brace_range_compare)
return brace_ranges[1]
2024-04-22 05:07:24 +00:00
end
2024-04-22 04:57:41 +00:00
end
2024-04-24 02:04:14 +00:00
2024-04-24 04:43:26 +00:00
function BraceRange:is_wrapped()
2024-04-25 02:39:09 +00:00
return self.start_cursor.row < self.stop_cursor.row
2024-04-24 04:43:26 +00:00
end
2024-04-24 02:04:14 +00:00
--
-- Arg
--
Arg = {}
function Arg.new(text, brace_pair)
local arg = {
text = text,
brace_pair = brace_pair,
}
return setmetatable(arg, {__index = Arg})
end
function Arg:append(char)
self.text = self.text .. char
end
--
-- ArgList
--
ArgList = {}
function ArgList.new()
local arg_list = {
indent = '',
prefix = '',
suffix = '',
arg = nil,
args = {},
}
return setmetatable(arg_list, {__index = ArgList})
end
function ArgList:flush()
if self.arg then
table.insert(self.args, self.arg)
self.arg = nil
end
end
function ArgList:update(char, brace_stack, brace_range, cursor)
if not cursor:is_string() then
brace_stack:update(char)
if brace_stack:empty() and char == ',' then
self:flush()
return
end
end
if self.arg then
2024-04-25 02:39:09 +00:00
self.arg:append(char)
2024-04-24 02:04:14 +00:00
else
self.arg = Arg.new(char, brace_range)
end
end
function ArgList:parse(brace_range)
local brace_stack = BraceStack:new()
local first_line = vim.fn.getline(brace_range.start_cursor.row)
self.indent = first_line:match('^(%s*)')
2024-04-25 02:39:09 +00:00
self.prefix = first_line:sub(#self.indent + 1, brace_range.start_cursor.col)
2024-04-24 02:04:14 +00:00
local last_line = vim.fn.getline(brace_range.stop_cursor.row)
self.suffix = last_line:sub(brace_range.stop_cursor.col)
for row = brace_range.start_cursor.row, brace_range.stop_cursor.row do
local line = vim.fn.getline(row)
local start_col = 1
if row == brace_range.start_cursor.row then
start_col = brace_range.start_cursor.col + 1
end
local stop_col = #line
if row == brace_range.stop_cursor.row then
stop_col = brace_range.stop_cursor.col - 1
end
for col = start_col, stop_col do
local char = line:sub(col, col)
self:update(char, brace_stack, brace_range, Cursor.new(row, col))
end
end
2024-04-24 03:38:56 +00:00
self:flush()
2024-04-24 02:04:14 +00:00
end
2024-04-24 04:43:26 +00:00
local function wrap_brace_range(brace_range, arg_list)
vim.fn.setline(brace_range.start_cursor.row, arg_list.indent .. arg_list.prefix)
-- local cursor_pos = nil
local row = brace_range.start_cursor.row
for _, arg in ipairs(arg_list.args) do
-- local on_last_row = i == #brace_range.params
2024-04-25 02:39:09 +00:00
local line = arg_list.indent .. arg.text
2024-04-24 04:43:26 +00:00
-- if opts.tail_comma or not on_last_row then
2024-04-25 02:39:09 +00:00
line = line .. ','
2024-04-24 04:43:26 +00:00
-- end
-- if on_last_row and not opts.wrap_closing_brace then
-- line = line .. brace_range.suffix
-- end
vim.fn.append(row, line)
vim.fn.execute(string.format('%d>', row + 1))
-- if param.offset then
-- cursor_pos = get_cursor_pos()
-- cursor_pos.col = cursor_pos.col + param.offset
-- cursor_pos.row = row + 1
-- end
row = row + 1
end
vim.fn.append(row, arg_list.indent .. arg_list.suffix)
-- if opts.wrap_closing_brace then
-- vim.fn.append(row, brace_range.indent .. brace_range.suffix)
-- end
--
-- if cursor_pos then
-- vim.fn.setcursorcharpos({cursor_pos.row, cursor_pos.col})
-- end
end
local function unwrap_brace_range(brace_range, arg_list)
2024-04-25 02:39:09 +00:00
local line = arg_list.indent .. arg_list.prefix
for i, arg in ipairs(arg_list.args) do
line = line .. arg.text
if i < #arg_list.args then
2024-04-24 04:43:26 +00:00
line = line .. ', '
end
end
2024-04-25 02:39:09 +00:00
line = line .. arg_list.suffix
2024-04-24 04:43:26 +00:00
vim.fn.setline(brace_range.start_cursor.row, line)
2024-04-25 02:39:09 +00:00
vim.fn.execute(string.format('%d,%dd_', brace_range.start_cursor.row + 1, brace_range.stop_cursor.row))
2024-04-24 04:43:26 +00:00
end
local function reflow()
2024-04-25 02:58:01 +00:00
local brace_range = BraceRange.find_closest_any()
2024-04-24 04:43:26 +00:00
if brace_range then
local arg_list = ArgList.new()
2024-04-25 02:39:09 +00:00
arg_list:parse(brace_range)
2024-04-24 04:43:26 +00:00
2024-04-25 02:39:09 +00:00
if brace_range:is_wrapped() then
2024-04-24 04:43:26 +00:00
unwrap_brace_range(brace_range, arg_list)
else
wrap_brace_range(brace_range, arg_list)
end
end
end
2024-04-25 02:39:09 +00:00
return {
reflow = reflow
}