1

Rework for deep parsing

This commit is contained in:
Alex Yatskov 2024-12-30 20:31:50 -08:00
parent 1d46f84163
commit 5fa76a6522
10 changed files with 652 additions and 681 deletions

56
lua/argonaut/builder.lua Normal file
View File

@ -0,0 +1,56 @@
local Builder = {}
Builder.__index = Builder
function Builder.new(indent_level, indent_block)
local builder = {
line = '',
buffer = {},
indent_level = indent_level,
indent_block = indent_block,
}
return setmetatable(builder, Builder)
end
function Builder:indent()
self:write(self.indent_block)
end
function Builder:push_indent()
self.indent_level = self.indent_level + 1
end
function Builder:pop_indent()
assert(self.indent_level > 0)
self.indent_level = self.indent_level - 1
end
function Builder:write(text)
self.line = self.line .. text
end
function Builder:endline()
local indent = string.rep(self.indent_block, self.indent_level)
table.insert(self.buffer, indent .. self.line)
self.line = ''
end
function Builder:output(row_begin, row_end)
local row = row_begin
local row_count = row_end - row_begin + 1
for i, line in ipairs(self.buffer) do
if i <= row_count then
vim.fn.setline(row, line)
else
vim.fn.append(row - 1, line)
end
row = row + 1
end
if row <= row_end then
vim.fn.execute(string.format('%d,%dd_', row, row_end))
end
end
return Builder

View File

@ -1,53 +0,0 @@
local opt_curr = {
default = {
brace_last_indent = false,
brace_last_wrap = true,
brace_pad = false,
comma_last = false,
comma_prefix = false,
comma_prefix_indent = false,
line_max = 32,
},
go = {
comma_last = true,
},
vim = {
line_prefix = '\\',
}
}
local function setup(opt)
for file_type, file_opt in pairs(opt) do
local file_opt_curr = opt_curr[file_type]
if not file_opt_curr then
file_opt_curr = {}
opt_curr[file_type] = file_opt_curr
end
for param_name, param_value in pairs(file_opt) do
file_opt_curr[param_name] = param_value
end
end
end
local function get()
local file_opt_curr = opt_curr[vim.bo.filetype]
local file_opt = {}
for param_name, param_value in pairs(opt_curr.default) do
file_opt[param_name] = param_value
if file_opt_curr then
local param_value_curr = file_opt_curr[param_name]
if param_value_curr ~= nil then
file_opt[param_name] = param_value_curr
end
end
end
return file_opt
end
return {
setup = setup,
get = get,
}

94
lua/argonaut/cursor.lua Normal file
View File

@ -0,0 +1,94 @@
local Cursor = {}
Cursor.__index = Cursor
function Cursor.new(row, col)
local cursor = {
row = row,
col = col,
}
return setmetatable(cursor, Cursor)
end
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.setcursorcharpos({self.row, self.col})
end
function Cursor:get_value()
assert(self:is_valid())
local line = vim.fn.getline(self.row)
return line:sub(self.col, self.col)
end
function Cursor:is_valid()
return self.row > 0 and self.col > 0
end
function Cursor:is_literal()
assert(self:is_valid())
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$') ~= nil or syn_attr:find('Comment$') ~= nil
end
function Cursor:next()
assert(self:is_valid())
local current_line = vim.fn.getline(self.row)
local next_col = self.col + 1
local next_row = self.row
if next_col > #current_line then
next_row = next_row + 1
next_col = 1
end
return Cursor.new(next_row, next_col)
end
function Cursor.__lt(cursor_1, cursor_2)
if cursor_1 == cursor_2 then
return false
end
if cursor_1.row > cursor_2.row then
return false
end
if cursor_1.row == cursor_2.row and cursor_1.col > cursor_2.col then
return false
end
return true
end
function Cursor.__gt(cursor_1, cursor_2)
if cursor_1 == cursor_2 then
return false
end
if cursor_1.row < cursor_2.row then
return false
end
if cursor_1.row == cursor_2.row and cursor_1.col < cursor_2.col then
return false
end
return true
end
function Cursor.__eq(cursor_1, cursor_2)
return cursor_1.row == cursor_2.row and cursor_1.col == cursor_2.col
end
return Cursor

View File

@ -1,14 +1,44 @@
local config = require('argonaut.config') local Builder = require('argonaut.builder')
local types = require('argonaut.types') local Cursor = require('argonaut.cursor')
local Options = require('argonaut.options')
local Range = require('argonaut.range')
local function reflow() local function reflow()
local wrap_context = types.WrapContext.new(config.get()) local range = Range.find_closest()
if wrap_context:parse() then if not range then
wrap_context:toggle() return false
end end
local trace = range:hit_test(Cursor.get_current())
assert(trace)
local line_first = vim.fn.getline(range.start_cursor.row)
local line_last = vim.fn.getline(range.stop_cursor.row)
local indent_level = #line_first:match('^(%s*)')
local indent_block = '\t'
if vim.o.expandtab then
indent_level = math.floor(indent_level / vim.o.shiftwidth)
indent_block = string.rep(' ', vim.o.shiftwidth)
end
local range_prefix = line_first:sub(indent_level * #indent_block + 1, range.start_cursor.col - 1)
local range_suffix = line_last:sub(range.stop_cursor.col + 1)
local builder = Builder.new(indent_level, indent_block)
builder:write(range_prefix)
range:write(builder, not range:is_wrapped())
builder:write(range_suffix)
builder:endline()
builder:output(range.start_cursor.row, range.stop_cursor.row)
local new_range = Range.find_at_cursor(range.start_cursor)
new_range:hit_search(trace, 1):set_current()
return true
end end
return { return {
reflow = reflow, reflow = reflow,
setup = config.setup, setup = Options.setup,
} }

61
lua/argonaut/options.lua Normal file
View File

@ -0,0 +1,61 @@
local options_current = {
default = {
brace_last_indent = false,
brace_last_wrap = true,
brace_pad = false,
comma_last = false,
comma_prefix = false,
comma_prefix_indent = false,
line_max = 32,
},
go = {
comma_last = true,
},
}
local function setup(opt)
for file_type, file_options in pairs(opt) do
local file_options_current = options_current[file_type]
if not file_options_current then
file_options_current = {}
options_current[file_type] = file_options_current
end
for param_name, param_value in pairs(file_options) do
file_options_current[param_name] = param_value
end
end
end
local function query(param_name, pairing)
assert(pairing)
local default_param_value = options_current.default[param_name]
local param_value = default_param_value
local file_options_current = options_current[vim.bo.filetype]
if file_options_current then
param_value = file_options_current[param_name]
if param_value == nil then
param_value = default_param_value
end
end
if type(param_value) == 'table' then
param_value = param_value[pairing.open]
if param_value == nil then
param_value = default_param_value
end
end
if type(param_value) == 'table' then
param_value = param_value[pairing.open]
end
return param_value
end
return {
setup = setup,
query = query,
}

71
lua/argonaut/pairing.lua Normal file
View File

@ -0,0 +1,71 @@
local Cursor = require('argonaut.cursor')
local Pairing = {}
Pairing.__index = Pairing
function Pairing.new(open, close)
local pair = {
open = open,
close = close
}
return setmetatable(pair, Pairing)
end
function Pairing.from_brace(brace)
local all_pairs = {
{'(', ')'},
{'[', ']'},
{'{', '}'},
{'<', '>'},
}
for _, pair in ipairs(all_pairs) do
if pair[1] == brace or pair[2] == brace then
return Pairing.new(pair[1], pair[2])
end
end
end
function Pairing:get_escaped()
local escape_func = function(brace_raw)
if brace_raw == '[' or brace_raw == ']' then
return '\\' .. brace_raw
else
return brace_raw
end
end
return Pairing.new(
escape_func(self.open),
escape_func(self.close)
)
end
function Pairing:find_closest(forward)
-- See flags: https://neovim.io/doc/user/builtin.html#search()
local flags = 'Wnbc'
if forward then
flags = 'Wn'
end
local ignore_func = function()
local cursor = Cursor.get_current()
return cursor:is_literal()
end
local escaped_pair = self:get_escaped()
local cursor = Cursor.new(unpack(vim.fn.searchpairpos(
escaped_pair.open,
'',
escaped_pair.close,
flags,
ignore_func
)))
if cursor:is_valid() then
return cursor
end
end
return Pairing

95
lua/argonaut/param.lua Normal file
View File

@ -0,0 +1,95 @@
local Param = {}
Param.__index = Param
local function is_empty_cell(cell)
return cell.type == 'cursor' and not cell.value:get_value():match('%S')
end
function Param.new(range, index, cells)
local param = {
range = range,
index = index,
cells = cells or {},
}
return setmetatable(param, Param)
end
function Param:append(value, type)
table.insert(self.cells, {
value = value,
type = type,
})
end
function Param:stripped()
local cells = {}
for i = self:find_index_begin(), self:find_index_end() do
table.insert(cells, self.cells[i])
end
return Param.new(self.range, self.index, cells)
end
function Param:hit_test(cursor)
for i, cell in ipairs(self.cells) do
local trace = nil
if cell.type == 'range' then
local trace_param = cell.value:hit_test(cursor)
if trace_param then
trace = {i}
for _, trace_param_frame in ipairs(trace_param) do
table.insert(trace, trace_param_frame)
end
end
elseif cell.type == 'cursor' and cell.value == cursor then
trace = {i}
end
if trace then
return trace
end
end
end
function Param:hit_search(trace, depth)
local index = trace[depth]
local cell = self.cells[index]
if cell.type == 'range' then
return cell.value:hit_search(trace, depth + 1)
elseif cell.type == 'cursor' then
return cell.value
end
end
function Param:write(builder, wrapped)
for i = self:find_index_begin(), self:find_index_end() do
local cell = self.cells[i]
if cell.type == 'range' then
cell.value:write(builder, wrapped)
elseif cell.type == 'cursor' then
builder:write(cell.value:get_value())
end
end
end
function Param:find_index_begin()
for i = 1, #self.cells do
if not is_empty_cell(self.cells[i]) then
return i
end
end
end
function Param:find_index_end()
for i = #self.cells, 1, -1 do
if not is_empty_cell(self.cells[i]) then
return i
end
end
end
function Param:is_empty()
return self:find_index_begin() == nil
end
return Param

236
lua/argonaut/range.lua Normal file
View File

@ -0,0 +1,236 @@
local Cursor = require('argonaut.cursor')
local Options = require('argonaut.options')
local Pairing = require('argonaut.pairing')
local Param = require('argonaut.param')
local Range = {}
Range.__index = Range
local function find_closest_pair(pairing)
local stop_cursor = pairing:find_closest(true)
if stop_cursor then
local start_cursor = pairing:find_closest(false)
if start_cursor then
return Range.new(start_cursor, stop_cursor, pairing)
end
end
end
local function find_closest()
local ranges = {}
for _, brace in pairs({'(', '[', '{', '<'}) do
local pairing = Pairing.from_brace(brace)
local range = find_closest_pair(pairing)
if range then
table.insert(ranges, range)
end
end
if #ranges > 0 then
table.sort(ranges)
local range = ranges[1]
range:parse()
return range
end
end
local function find_at_cursor(start_cursor)
local pairing = Pairing.from_brace(start_cursor:get_value())
if pairing then
local cursor_current = Cursor:get_current()
start_cursor:set_current()
local stop_cursor = pairing:find_closest(true)
cursor_current:set_current()
if stop_cursor then
local range = Range.new(start_cursor, stop_cursor, pairing)
range:parse()
return range
end
end
end
function Range.new(start_cursor, stop_cursor, pairing)
local range = {
start_cursor = start_cursor,
stop_cursor = stop_cursor,
pairing = pairing,
params = {},
}
return setmetatable(range, Range)
end
function Range:parse()
local param = nil
local param_index = 1
local append_param_value = function(value, type)
if not param then
param = Param.new(self, param_index)
param_index = param_index + 1
end
param:append(value, type)
end
local append_param = function()
if param and not param:is_empty() then
table.insert(self.params, param:stripped())
end
param = nil
end
local cursor = self.start_cursor:next()
while cursor ~= self.stop_cursor do
local value = cursor:get_value()
if cursor:is_literal() then
append_param_value(cursor, 'cursor')
else
local range = find_at_cursor(cursor)
if range then
append_param_value(range, 'range')
cursor = range.stop_cursor
elseif value == ',' then
append_param()
else
append_param_value(cursor, 'cursor')
end
end
cursor = cursor:next()
end
append_param()
end
function Range:hit_test(cursor)
if self:contains(cursor) then
local trace = nil
for i, param in pairs(self.params) do
local trace_param = param:hit_test(cursor)
if trace_param then
trace = {i}
for _, trace_param_frame in ipairs(trace_param) do
table.insert(trace, trace_param_frame)
end
break
end
end
if not trace then
trace = {0}
end
return trace
end
end
function Range:hit_search(trace, depth)
local index = trace[depth]
if index == 0 then
return self.start_cursor
else
return self.params[index]:hit_search(trace, depth + 1)
end
end
function Range:write(builder, wrapped)
if wrapped == nil then
wrapped = self:is_wrapped()
end
if wrapped then
builder:write(self.pairing.open)
if #self.params > 0 then
builder:endline()
builder:push_indent()
for i, param in ipairs(self.params) do
param:write(builder)
local is_last_param = i == #self.params
if not is_last_param or self:query_option('comma_last') then
builder:write(',')
end
if not is_last_param then
builder:endline()
end
end
if self:query_option('brace_last_wrap') then
builder:endline()
builder:pop_indent()
end
end
if not self:query_option('brace_last_wrap') then
if self:query_option('brace_last_indent') then
builder:indent()
end
if self:query_option('brace_pad') then
builder:write(' ')
end
end
builder:write(self.pairing.close)
else
builder:write(self.pairing.open)
if self:query_option('brace_pad') then
builder:write(' ')
end
for i, param in ipairs( self.params ) do
param:write(builder)
local is_last_param = i == #self.params
if not is_last_param then
builder:write(', ')
end
end
if self:query_option('brace_pad') then
builder:write(' ')
end
builder:write(self.pairing.close)
end
end
function Range:contains(cursor)
return cursor >= self.start_cursor and cursor <= self.stop_cursor
end
function Range:query_option(name)
return Options.query(name, self.pairing)
end
function Range:is_wrapped()
return self.start_cursor.row ~= self.stop_cursor.row
end
function Range.__lt(range_1, range_2)
local cursor = Cursor:get_current()
local row_diff1 = range_1.start_cursor.row - cursor.row
local col_diff1 = range_1.start_cursor.col - cursor.col
local row_diff2 = range_2.start_cursor.row - cursor.row
local col_diff2 = range_2.start_cursor.col - cursor.col
if row_diff1 < row_diff2 then
return false
elseif row_diff1 > row_diff2 then
return true
elseif col_diff1 < col_diff2 then
return false
elseif col_diff1 > col_diff2 then
return true
else
return true
end
end
return {
find_closest = find_closest,
find_at_cursor = find_at_cursor,
}

View File

@ -1,620 +0,0 @@
--
-- Cursor
--
local Cursor = {}
Cursor.__index = Cursor
function Cursor.new(row, col)
local cursor = {row = row, col = col}
return setmetatable(cursor, Cursor)
end
function Cursor:is_valid()
return self.row > 0 and self.col > 0
end
function Cursor:is_literal()
assert(self:is_valid())
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$') ~= nil or syn_attr:find('Comment$') ~= nil
end
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.setcursorcharpos({self.row, self.col})
end
function Cursor.__eq(self, other)
return self.row == other.row and self.col == other.col
end
--
-- BracePair
--
local BracePair = {}
BracePair.__index = BracePair
function BracePair.new(open, close)
local pair = {open = open, close = close}
return setmetatable(pair, BracePair)
end
function BracePair.from_brace(brace)
local all_pairs = {
{'(', ')'},
{'[', ']'},
{'{', '}'},
{'<', '>'},
}
for _, pair in ipairs(all_pairs) do
if pair[1] == brace or pair[2] == brace then
return BracePair.new(pair[1], pair[2])
end
end
end
function BracePair:get_escaped()
local escape_func = function(brace_raw)
if brace_raw == '[' or brace_raw == ']' then
return '\\' .. brace_raw
else
return brace_raw
end
end
return BracePair.new(
escape_func(self.open),
escape_func(self.close)
)
end
function BracePair:find_closest(backward)
-- See flags: https://neovim.io/doc/user/builtin.html#search()
local flags = 'Wcn'
if backward then
flags = 'Wcnb'
end
local ignore_func = function()
local cursor = Cursor.get_current()
return cursor:is_literal()
end
local escaped_pair = self:get_escaped()
local position = vim.fn.searchpairpos(
escaped_pair.open,
'',
escaped_pair.close,
flags,
ignore_func
)
local brace_cursor = Cursor.new(unpack(position))
if brace_cursor:is_valid() then
return brace_cursor
end
end
function BracePair.__eq(self, other)
return self.open == other.open and self.close == other.close
end
--
-- BraceStack
--
local BraceStack = {}
BraceStack.__index = BraceStack
function BraceStack.new()
local stack = {stack = {}}
return setmetatable(stack, BraceStack)
end
function BraceStack:update(brace)
local pair = BracePair.from_brace(brace)
if pair then
if brace == pair.close and self:top() == 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
--
-- BraceRange
--
local BraceRange = {}
BraceRange.__index = BraceRange
function BraceRange.new(start, stop, pair)
local range = {
start = start,
stop = stop,
pair = pair,
}
return setmetatable(range, BraceRange)
end
function BraceRange.find_closest(pair)
local stop = pair:find_closest(false)
if stop then
local start = pair:find_closest(true)
if start then
return BraceRange.new(start, stop, pair)
end
end
end
function BraceRange.find_closest_any()
local ranges = {}
for _, brace in ipairs({'(', '[', '{', '<'}) do
local pair = BracePair.from_brace(brace)
local range = BraceRange.find_closest(pair)
if range then
table.insert(ranges, range)
end
end
if #ranges > 0 then
table.sort(ranges)
return ranges[1]
end
end
function BraceRange:get_rows()
return self.stop.row - self.start.row
end
function BraceRange:is_wrapped()
return self.start.row < self.stop.row
end
function BraceRange.__lt(range_1, range_2)
local cursor = Cursor:get_current()
local row_diff1 = range_1.start.row - cursor.row
local col_diff1 = range_1.start.col - cursor.col
local row_diff2 = range_2.start.row - cursor.row
local col_diff2 = range_2.start.col - cursor.col
if row_diff1 < row_diff2 then
return false
elseif row_diff1 > row_diff2 then
return true
elseif col_diff1 < col_diff2 then
return false
elseif col_diff1 > col_diff2 then
return true
else
return true
end
end
--
-- Param
--
local Param = {}
Param.__index = Param
function Param.new(pair, opt)
local param = {
pair = pair,
opt = opt,
text = '',
literals = {},
offset = nil,
start = nil,
stop = nil,
terminator = nil,
}
return setmetatable(param, Param)
end
function Param:append(char, cursor)
assert(cursor:is_valid())
self.text = self.text .. char
table.insert(self.literals, cursor:is_literal())
if cursor == Cursor.get_current() then
self.offset = #self.text
end
if not self.start then
self.start = cursor
end
self.stop = cursor
end
function Param:terminate(cursor)
self.terminator = cursor
if self.terminator == Cursor.get_current() then
self.offset = #self.text
end
end
function Param:is_active()
return self.offset ~= nil
end
function Param:slice(start, stop)
assert(#self.text == #self.literals)
local text = ''
local literals = {}
for i = start, stop do
text = text .. self.text:sub(i, i)
table.insert(literals, self.literals[i])
end
self.text = text
self.literals = literals
if self.offset then
self.offset = math.min(self.offset, stop)
self.offset = math.max(self.offset - start + 1, 1)
end
end
function Param:trim()
assert(#self.text == #self.literals)
self:slice(1, #self.text - #self.text:match('%s*$'))
self:slice(1 + #self.text:match('^%s*'), #self.text)
local text = ''
local literals = {}
local offset = self.offset
for i = 1, #self.text do
local char = self.text:sub(i, i)
local literal = self.literals[i]
if literal or not char:match('%s') or not text:match('%s$') then
text = text .. char
table.insert(literals, literal)
elseif offset and offset >= i then
self.offset = math.max(1, self.offset - 1)
end
end
self.text = text
self.literals = literals
return #self.text > 0
end
--
-- ParamList
--
local ParamList = {}
ParamList.__index = ParamList
function ParamList.new(range, opt)
local params = {
range = range,
opt = opt,
current = nil,
parsed = {},
}
return setmetatable(params, ParamList)
end
function ParamList:flush(cursor)
if self.current then
if cursor then
self.current:terminate(cursor)
end
if self.current:trim() then
table.insert(self.parsed, self.current)
end
self.current = nil
end
end
function ParamList:update(char, brace_stack, cursor)
if not cursor:is_literal() then
brace_stack:update(char)
if brace_stack:empty() and char == ',' then
self:flush(cursor)
return
end
end
if not self.current then
self.current = Param.new(self.range, self.opt)
end
self.current:append(char, cursor)
end
function ParamList:parse()
local brace_stack = BraceStack:new()
for row = self.range.start.row, self.range.stop.row do
local line = vim.fn.getline(row)
local start_col = 1
if row == self.range.start.row then
start_col = self.range.start.col + 1
end
local stop_col = #line
if row == self.range.stop.row then
stop_col = self.range.stop.col - 1
end
for col = start_col, stop_col do
self:update(line:sub(col, col), brace_stack, Cursor.new(row, col))
end
end
self:flush()
end
--
-- Builder
--
local Builder = {}
Builder.__index = Builder
function Builder.new(indent_level, indent_block)
local builder = {
lines = {},
line = '',
indent_level = indent_level,
indent_block = indent_block,
}
return setmetatable(builder, Builder)
end
function Builder:indent()
self.indent_level = self.indent_level + 1
end
function Builder:unindent()
assert(self.indent_level > 0)
self.indent_level = self.indent_level - 1
end
function Builder:update(text)
self.line = self.line .. text
end
function Builder:flush()
local indent = string.rep(self.indent_block, self.indent_level)
table.insert(self.lines, indent .. self.line)
self.line = ''
end
function Builder:get_offset()
local indent = string.rep(self.indent_block, self.indent_level)
return Cursor.new(#self.lines, #self.line + #indent)
end
--
-- WrapContext
--
local WrapContext = {}
WrapContext.__index = WrapContext
function WrapContext.new(opt)
local wrap_context = {
opt = nil,
base_opt = opt,
indent = '',
prefix = '',
suffix = '',
range = nil,
params = nil,
}
return setmetatable(wrap_context, WrapContext)
end
function WrapContext:config_opt()
self.opt = {}
for key, value in pairs(self.base_opt) do
if type(value) == 'table' then
self.opt[key] = false
for _, brace in ipairs(value) do
if self.range.pair == BracePair.from_brace(brace) then
self.opt[key] = true
break
end
end
else
self.opt[key] = value
end
end
end
function WrapContext:config_indent(line)
local padding = #line:match('^(%s*)')
if vim.o.expandtab then
self.indent_level = math.floor(padding / vim.o.shiftwidth)
self.indent_block = string.rep(' ', vim.o.shiftwidth)
else
self.indent_level = padding
self.indent_block = '\t'
end
end
function WrapContext:parse()
self.range = BraceRange.find_closest_any()
if not self.range then
return false
end
self:config_opt()
if self.range:get_rows() > self.opt.line_max then
return false
end
local first_line = vim.fn.getline(self.range.start.row)
local last_line = vim.fn.getline(self.range.stop.row)
self:config_indent(first_line)
self.prefix = first_line:sub(self.indent_level * #self.indent_block + 1, self.range.start.col)
self.suffix = last_line:sub(self.range.stop.col)
self.params = ParamList.new(self.range, self.opt)
self.params:parse()
return true
end
function WrapContext:update_builder_param(builder, param)
local cursor = nil
if param:is_active() then
cursor = builder:get_offset()
cursor.row = cursor.row + self.range.start.row
cursor.col = cursor.col + param.offset
end
builder:update(param.text)
return cursor
end
function WrapContext:wrap()
local builder = Builder.new(self.indent_level, self.indent_block)
builder:update(self.prefix)
builder:flush()
builder:indent()
local cursor = nil
for i, param in ipairs(self.params.parsed) do
local is_first_param = i == 1
local is_last_param = i == #self.params.parsed
if self.opt.comma_prefix then
if not is_first_param then
builder:update(', ')
elseif self.opt.comma_prefix_indent and not is_last_param then
builder:update(' ')
end
cursor = self:update_builder_param(builder, param) or cursor
else
cursor = self:update_builder_param(builder, param) or cursor
if not is_last_param or self.opt.comma_last then
builder:update(',')
end
end
if is_last_param and not self.opt.brace_last_wrap then
builder:update(self.suffix)
end
builder:flush()
end
if not self.opt.brace_last_indent then
builder:unindent()
end
if self.opt.brace_last_wrap then
builder:update(self.suffix)
builder:flush()
end
local row = self.range.start.row
for i, line in ipairs(builder.lines) do
if i == 1 then
vim.fn.setline(row, line)
else
vim.fn.append(row, line)
row = row + 1
end
end
if not cursor then
cursor = self.range.start
end
cursor:set_current()
end
function WrapContext:unwrap()
local padding = ''
if self.opt.brace_pad then
padding = ' '
end
local builder = Builder.new(self.indent_level, self.indent_block)
builder:update(self.prefix)
builder:update(padding)
local cursor = nil
for i, param in ipairs(self.params.parsed) do
cursor = self:update_builder_param(builder, param) or cursor
if i < #self.params.parsed then
builder:update(', ')
end
end
builder:update(padding)
builder:update(self.suffix)
builder:flush()
vim.fn.setline(self.range.start.row, builder.lines[1])
vim.fn.execute(string.format('%d,%dd_', self.range.start.row + 1, self.range.stop.row))
if not cursor then
cursor = self.range.start
end
cursor:set_current()
end
function WrapContext:toggle()
if self.range:is_wrapped() then
self:unwrap()
else
self:wrap()
end
end
return {
WrapContext = WrapContext,
}

View File

@ -1,4 +1,4 @@
if not vim.g.argonaut then if not vim.g.argonaut_loaded then
local function argonaut_reload() local function argonaut_reload()
for name, _ in pairs(package.loaded) do for name, _ in pairs(package.loaded) do
if vim.startswith(name, 'argonaut') then if vim.startswith(name, 'argonaut') then
@ -8,11 +8,12 @@ if not vim.g.argonaut then
end end
local function argonaut_reflow() local function argonaut_reflow()
argonaut_reload()
require('argonaut').reflow() require('argonaut').reflow()
end end
vim.api.nvim_create_user_command('ArgonautReload', argonaut_reload, {}) vim.api.nvim_create_user_command('ArgonautReload', argonaut_reload, {})
vim.api.nvim_create_user_command('ArgonautReflow', argonaut_reflow, {}) vim.api.nvim_create_user_command('ArgonautReflow', argonaut_reflow, {})
vim.g.argonaut = true vim.g.argonaut_loaded = true
end end