local action_state = require('telescope.actions.state') local actions = require('telescope.actions') local conf = require("telescope.config").values local finders = require("telescope.finders") local pickers = require("telescope.pickers") local previewers = require("telescope.previewers") local log = require('plenary.log').new { plugin = 'knot', level = 'info', } local _get_name = function(record) local name = record.name .. " (" .. record.rtype .. "): " .. record.data return name end local _get_records_and_entries = function() local results = vim.json.decode(vim.fn.system("knotctl --json list")) local records = {} for _, zone in ipairs(results) do for _, record in ipairs(zone.records) do if record then record.zone = zone.name table.insert(records, record) end end end local entries = vim.tbl_map( function(record) return _get_name(record) end, records ) return records, entries end local _filter_records = function(rec, records) local r = {} local record_name = rec.value for _, record in ipairs(records) do local name = _get_name(record) if name == record_name then r = { "ZONE: " .. record.zone, "RTYPE: " .. record.rtype, "NAME: " .. record.name, "DATA: " .. record.data, "TTL: " .. record.ttl } end end return r end local _parse_record = function(rec) local record = {} for _, v in ipairs(rec) do if v ~= nil and v ~= "" then local key = v:match("%w+"):lower() record[key] = v:gsub("%w+%:%s", "") end end return record end local M = {} M.add = function(_) local tempfile = vim.fn.tempname() local buffer = vim.api.nvim_create_buf(false, false) vim.api.nvim_buf_set_name(buffer, tempfile) local r = { "ZONE: example.net.", "RTYPE: A", "NAME: example.net.", "DATA: 127.0.0.1", "TTL: 360" } vim.api.nvim_buf_set_lines(buffer, 0, 0, false, r) local width = vim.api.nvim_win_get_width(0) local height = vim.api.nvim_win_get_height(0) - 10 vim.api.nvim_open_win(buffer, true, { relative = 'win', bufpos = { 0, 0 }, width = width, height = height, border = 'rounded' }) vim.api.nvim_create_autocmd({ "BufWipeout" }, { buffer = buffer, callback = function(_) vim.api.nvim_buf_delete(buffer, { force = true }) vim.fn.delete(tempfile) end }) vim.api.nvim_create_autocmd({ "BufWrite" }, { buffer = buffer, callback = function(_) local new = vim.api.nvim_buf_get_lines(buffer, 0, -1, false) local record = _parse_record(new) local command = "knotctl add -z " .. record.zone .. " -t " .. record.ttl .. " -n " .. record.name .. " -r " .. record.rtype .. " -d " .. record.data local choice = vim.fn.confirm("Create record?", "&Yes\n&No", 2) if choice == 1 then log.info(vim.fn.system(command)) end end }) end M.delete = function(opts) local records, entries = _get_records_and_entries() pickers.new(opts, { dropdown = true, prompt_title = "Records", sorter = conf.generic_sorter(opts), finder = finders.new_table(entries), previewer = previewers.new_buffer_previewer({ title = "Record", define_preview = function(self, entry) local record = _filter_records(entry, records) vim.api.nvim_buf_set_lines(self.state.bufnr, 0, 0, false, record) end }), attach_mappings = function(prompt_bufnr) actions.select_default:replace(function() local entry = action_state.get_selected_entry() local record = _parse_record(_filter_records(entry, records)) local choice = vim.fn.confirm("Delete record?", "&Yes\n&No", 2) if choice == 1 then local command = "knotctl delete -z " .. record.zone .. " -n " .. record.name .. " -r " .. record.rtype .. " -d " .. record.data log.info(vim.fn.system(command)) actions.close(prompt_bufnr) end end) return true end, }):find() end M.update = function(opts) local records, entries = _get_records_and_entries() pickers.new(opts, { dropdown = true, prompt_title = "Records", sorter = conf.generic_sorter(opts), finder = finders.new_table(entries), previewer = previewers.new_buffer_previewer({ title = "Record", define_preview = function(self, entry) local record = _filter_records(entry, records) vim.api.nvim_buf_set_lines(self.state.bufnr, 0, 0, false, record) end }), attach_mappings = function(prompt_bufnr) actions.select_default:replace(function() local entry = action_state.get_selected_entry() local record = _filter_records(entry, records) actions.close(prompt_bufnr) local tempfile = vim.fn.tempname() local buffer = vim.api.nvim_create_buf(false, false) vim.api.nvim_buf_set_name(buffer, tempfile) vim.api.nvim_buf_set_lines(buffer, 0, 0, false, record) local width = vim.api.nvim_win_get_width(0) local height = vim.api.nvim_win_get_height(0) - 10 vim.api.nvim_open_win(buffer, true, { relative = 'win', bufpos = { 0, 0 }, width = width, height = height, border = 'rounded' }) vim.api.nvim_create_autocmd({ "BufWipeout" }, { buffer = buffer, callback = function(_) vim.api.nvim_buf_delete(buffer, { force = true }) vim.fn.delete(tempfile) end }) vim.api.nvim_create_autocmd({ "BufWrite" }, { buffer = buffer, callback = function(_) local new = vim.api.nvim_buf_get_lines(buffer, 0, -1, false) if new ~= record then local cleanold = _parse_record(record) local updated = {} local did_update = false local cleannew = {} for _, v in ipairs(new) do if v ~= nil and v ~= "" then local key = v:match("%w+"):lower() cleannew[key] = v:gsub("%w+%:%s", "") if cleanold[key] ~= cleannew[key] and (key ~= "url" or key ~= "zone") then table.insert(updated, " -a " .. key .. "=" .. cleannew[key]) did_update = true end end end if did_update then local command = "knotctl update -z " .. cleanold.zone .. " -t " .. cleanold.ttl .. " -n " .. cleanold.name .. " -r " .. cleanold.rtype .. " -d " .. cleanold.data .. table.concat(updated, " ") local choice = vim.fn.confirm("Update record?", "&Yes\n&No", 2) if choice == 1 then log.info(vim.fn.system(command)) end end end end }) end) return true end, }):find() end return require('telescope').register_extension({ exports = { add = M.add, delete = M.delete, update = M.update, }, })