jsonfly.nvim/lua/jsonfly/insert.lua
2024-04-26 23:59:58 +02:00

173 lines
4.4 KiB
Lua

local utils = require"jsonfly.utils"
local M = {};
-- https://stackoverflow.com/a/24823383/9878135
function table.slice(tbl, first, last, step)
local sliced = {}
for i = first or 1, last or #tbl, step or 1 do
sliced[#sliced+1] = tbl[i]
end
return sliced
end
---@param entry Entry
---@param key string
---@param index number
local function check_key_equal(entry, key, index)
local splitted = utils:split_by_char(entry.key, ".")
return splitted[index] == key
end
---Find the entry in `entries` with the most matching keys at the beginning based on the `keys`.
---Returns the index of the entry
---@param entries Entry[]
---@param keys KeyDescription[]
---@return number|nil
local function find_best_fitting_entry(entries, keys)
local entry_index
local current_indexes = {1, #entries}
for kk=1, #keys do
local key = keys[kk].key
local start_index = current_indexes[1]
local end_index = current_indexes[2]
current_indexes = {nil, nil}
for ii=start_index, end_index do
if check_key_equal(entries[ii], key, kk) then
if current_indexes[1] == nil then
current_indexes[1] = ii
end
current_indexes[2] = ii
end
end
if current_indexes[1] == nil then
-- No entries found
break
else
entry_index = current_indexes[1]
end
end
return entry_index
end
---@param keys KeyDescription
---@param index number - Index of the key
local function write_keys(keys, index)
local lines = {}
if index == #keys then
return {
{ "\"" .. keys[index].key .. "\": \"\""},
true
}
end
local insertions = write_keys(keys, index + 1)
lines[#lines + 1] = "\"" .. keys[index].key .. "\": {"
for ii=1, #insertions do
lines[#lines + 1] = insertions[ii]
end
lines[#lines + 1] = "}"
return lines
end
---@param buffer number
---@param insertion_line number
local function add_comma(buffer, insertion_line)
local BUFFER_SIZE = 5
-- Find next non-empty character in reverse
for ii=insertion_line, 0, -BUFFER_SIZE do
local previous_lines = vim.api.nvim_buf_get_lines(buffer, ii - BUFFER_SIZE, ii, false)
if #previous_lines == 0 then
return
end
for jj=#previous_lines, 0, -1 do
local line = previous_lines[jj]
for char_index=#line, 0, -1 do
local char = line:sub(char_index, char_index)
if char ~= " " and char ~= "\t" and char ~= "\n" and char ~= "\r" then
if char == "," then
return
end
-- Insert comma at position
vim.api.nvim_buf_set_text(buffer, ii - 1, char_index, ii - 1, char_index, {","})
return
end
end
end
end
end
---@param entries Entry[]
---@param keys KeyDescription[]
---@param buffer number
function M:insert_new_key(entries, keys, buffer)
-- Close current buffer
vim.cmd [[quit!]]
local entry_index = find_best_fitting_entry(entries, keys) or 0
local entry = entries[entry_index]
local existing_keys_depth = #utils:split_by_char(entry.key, ".") + 1
local remaining_keys = table.slice(keys, existing_keys_depth, #keys)
local _writes = write_keys(remaining_keys, 1)
local writes = {}
for ii=1, #_writes do
if _writes[ii] == true then
-- Unwrap table
writes[#writes] = writes[#writes][1]
else
writes[#writes + 1] = _writes[ii]
end
end
vim.api.nvim_win_set_cursor(0, {entry.position.line_number, entry.position.value_start})
-- Hacky way to jump to end of object
vim.cmd [[execute "normal %"]]
local start_line = vim.api.nvim_win_get_cursor(0)[1] - 1
-- Add comma to previous line
add_comma(buffer, start_line)
--
-- Insert new lines
vim.api.nvim_buf_set_lines(buffer, start_line, start_line, false, writes)
--
-- -- -- Format lines
vim.api.nvim_win_set_cursor(0, {start_line, 1})
vim.cmd('execute "normal =' .. #writes .. 'j"')
--
-- -- Jump to the key
vim.api.nvim_win_set_cursor(0, {start_line + math.ceil(#writes / 2), 0})
vim.cmd [[execute "normal $a"]]
-- vim.schedule(function()
-- vim.cmd [[%]]
-- end)
end
return M;