diff --git a/README.md b/README.md index e31cfc1..17db793 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ With [lazy.nvim](https://github.com/folke/lazy.nvim): "nvim-telescope/telescope.nvim", }, config = function() - require("telescope").load_extension('telescope_knot') + require("telescope").load_extension('knot') end, }, @@ -22,12 +22,17 @@ If knotctl works for you in your terminal, it should work in this plugin as well ## Usage This plugin provides a Telescope picker for viewing and editing DNS records. You can summon it with: ```vim -:Telescope telescope_knot +:Telescope knot update +``` +You can also add new records with: +```vim +:Telescope knot add ``` or add a mapping: ```vim -nnoremap k :Telescope telescope_knot +nnoremap ka :Telescope knot add +nnoremap ku :Telescope knot update ``` You can edit the record and when saving it with `:w` you will be asked to confirm. You can quit the buffer with `:q` or `q` to discard changes. diff --git a/lua/telescope/_extensions/knot.lua b/lua/telescope/_extensions/knot.lua new file mode 100644 index 0000000..5e0b6e5 --- /dev/null +++ b/lua/telescope/_extensions/knot.lua @@ -0,0 +1,174 @@ +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 _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 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 = {} + for _, v in ipairs(new) do + if v ~= nil and v ~= "" then + local key = v:match("%w+"):lower() + record[key] = v:gsub("%w+%:%s", "") + end + end + 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.update = function(opts) + 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 + ) + 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 = {} + local updated = {} + local did_update = false + for _, v in ipairs(record) do + if v ~= nil and v ~= "" then + local key = v:match("%w+"):lower() + cleanold[key] = v:gsub("%w+%:%s", "") + end + end + 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, + update = M.update, + }, +}) diff --git a/lua/telescope/_extensions/telescope_knot.lua b/lua/telescope/_extensions/telescope_knot.lua deleted file mode 100644 index e61f133..0000000 --- a/lua/telescope/_extensions/telescope_knot.lua +++ /dev/null @@ -1,129 +0,0 @@ -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 = 'telescope_knot', - level = 'info', -} - -local _get_name = function(record) - local name = record.name .. " (" .. record.rtype .. "): " .. record.data - return name -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, "URL: " .. record.url } - end - end - return r -end - - -local telescope_knot = {} -telescope_knot.zones = function(opts) - 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 - ) - 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 buffer = vim.api.nvim_create_buf(false, true) - 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_buf_set_keymap(buffer, "n", "q", "bd", {}) - vim.api.nvim_buf_set_keymap(buffer, "c", "w", - "lua vim.api.nvim_exec_autocmds('User', { pattern = 'KnotWrite' })", {}) - vim.api.nvim_create_autocmd({ "User" }, { - buffer = buffer, - callback = function(event) - if event.match == "KnotWrite" then - local new = vim.api.nvim_buf_get_lines(buffer, 0, -1, false) - if new ~= record then - local cleanold = {} - local updated = {} - local did_update = false - for _, v in ipairs(record) do - if v ~= nil and v ~= "" then - local key = v:match("%w+"):lower() - cleanold[key] = v:gsub("%w+%:%s", "") - end - end - 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 and close buffer?", "&Yes\n&No", 2) - if choice == 1 then - log.info(vim.fn.system(command)) - vim.api.nvim_buf_delete(buffer, {}) - end - end - end - end - end - }) - end) - return true - end, - }):find() -end -return require('telescope').register_extension({ - exports = { - telescope_knot = telescope_knot.zones, - }, -})