-
-
Save trv6/353031dfb9b86f83e5318b93a2a2efb8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local api = vim.api | |
local U = require 'snippets/common' | |
local ns = api.nvim_create_namespace('pull it, twist it, bop it, snippet') | |
local get_cursor = api.nvim_win_get_cursor | |
local set_text = api.nvim_buf_set_text | |
local get_window = api.nvim_get_current_win | |
local get_buffer = api.nvim_get_current_buf | |
local get_line = api.nvim_get_current_line | |
local get_lines = api.nvim_buf_get_lines | |
local concat = table.concat | |
local function strwidth(s, c) | |
return api.nvim_call_function('strwidth', {s}) | |
end | |
local place_mark = api.nvim_buf_set_extmark | |
local get_mark = api.nvim_buf_get_extmark_by_id | |
local function entrypoint(structure) | |
local empty = {} | |
local head, tail, body | |
local marks = {} | |
local buf, win = get_buffer(), get_window() | |
local r_head, c = unpack(get_cursor(win)) | |
local r_tail = r_head | |
local resolved_inputs = {} | |
local evaluator = U.evaluate_snippet(structure) | |
local inputs = evaluator.inputs | |
local struct = evaluator.structure | |
local eval_inputs = evaluator.evaluate_inputs | |
local eval_struct = evaluator.evaluate_structure | |
local function update() | |
return eval_struct(eval_inputs(resolved_inputs)) | |
end | |
-- form the default text | |
local fragments = update() | |
body = concat(fragments, '') | |
set_text(buf, r_head-1, c, r_head-1, c, vim.split(body, '\n', true)) | |
-- set the extmarks | |
local cursor_landing_mark | |
local head_mark, tail_mark | |
head_mark = place_mark(buf, ns, r_head - 1, c, empty) | |
for i = 1, #struct do | |
local frag = struct[i] | |
if type(frag) == 'string' then | |
if frag == '\n' then | |
r_tail = r_tail + 1 | |
c = 0 | |
elseif frag ~= '' then | |
c = c + strwidth(frag) | |
end | |
else | |
local n = strwidth(fragments[i]) | |
if frag.is_input then | |
marks[i] = place_mark(buf, ns, r_tail - 1, c, {end_line = r_tail - 1, end_col = c+n, hl_group = 'Search', right_gravity = false, end_right_gravity = true}) | |
else | |
marks[i] = place_mark(buf, ns, r_tail - 1, c, empty) | |
end | |
if frag.order == 0 then -- $0 | |
cursor_landing_mark = marks[i] | |
end | |
c = c + n | |
end | |
end | |
tail_mark = place_mark(buf, ns, r_tail - 1, c, empty) | |
-- get the head and tail text | |
do | |
local lines = concat(get_lines(buf, r_head - 1, r_tail, true), '\n') | |
local n, m = string.find(lines, body) | |
head = string.sub(lines, 1, n-1) | |
tail = string.sub(lines, m+1) | |
end | |
local function delete_marks() | |
for k, id in pairs(marks) do | |
api.nvim_buf_del_extmark(buf, ns, id) | |
end | |
api.nvim_buf_del_extmark(buf, ns, tail_mark) | |
api.nvim_buf_del_extmark(buf, ns, head_mark) | |
end | |
local pattern | |
local current_index = 0 | |
local R = {} | |
R.aborted = false | |
R.finished = false | |
-- this function is called by the user's keybinds | |
function R.advance(offset) | |
local previous_index = current_index | |
current_index = current_index + (offset or 0) | |
if current_index < 0 then | |
R.aborted = true | |
delete_marks() | |
return true | |
end | |
-- if there is a pattern, we should match the input | |
if pattern then | |
local write = concat(get_lines(buf, r_head - 1, r_tail, true),'\n') | |
local capture = string.match(write, pattern) | |
local first_index = inputs[previous_index].first_index | |
if fragments[first_index] ~= capture then -- new input detected | |
fragments[first_index] = capture | |
resolved_inputs[previous_index] = capture | |
local new_fragments = update() | |
-- snippets.nvim should have expanded the structure where possible (instances of $1, etc.)... replace the expanded components | |
for i = 1, #fragments do | |
if fragments[i] ~= new_fragments[i] then | |
local r, c = unpack(get_mark(buf, ns, marks[i], empty)) | |
set_text(buf, r, c, r, c + strwidth(fragments[i]), vim.split(new_fragments[i], '\n')) | |
end | |
end | |
fragments = new_fragments | |
end | |
pattern = nil | |
end | |
-- are we done yet? | |
if current_index == 0 then return end | |
if current_index > #inputs then | |
R.finished = true | |
if cursor_landing_mark then | |
local pos = get_mark(buf, ns, cursor_landing_mark, empty) | |
pos[1], pos[2] = pos[1] + 1, pos[2] + 1 | |
api.nvim_win_set_cursor(win, pos) | |
end | |
delete_marks() | |
return true | |
end | |
-- create the pattern for the current offset | |
local first = inputs[current_index].first_index | |
pattern = head .. concat(fragments, '', 1, first - 1) .. '(.+)' .. concat(fragments, '', first + 1) .. tail | |
-- set the cursor location | |
local relevant_mark = get_mark(buf, ns, marks[first], empty) | |
relevant_mark[1], relevant_mark[2] = relevant_mark[1] + 1, relevant_mark[2] + 1 | |
api.nvim_win_set_cursor(win, relevant_mark) | |
-- switch to highlight mode | |
local scoot = strwidth(fragments[first]) - 1 | |
api.nvim_input('<Esc>v' .. (scoot > 0 and tostring(scoot) .. 'l' or '') .. '<C-g>') -- there must be a better way to do this | |
end | |
return R | |
end | |
return entrypoint |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment