From 91b6db9d76c61544ad1072d94deb306969c17c3e Mon Sep 17 00:00:00 2001 From: Ade Attwood Date: Sun, 16 Jun 2024 21:44:16 +0100 Subject: [PATCH] feat: split out register backend so it can be used as an external API Summary: Split out the register backend function from being a private function. A backend is a table that will define all of the attributes for a run function. The attributes are as follows: - **command** The name of the command that will be registered as a vim user command. - **items** The callback function that will be passed to the run function that will gather the items for selection - **callback** The callback function run when an item is selected The following are optional: - **keymap** The string passed to `nvim_set_keymap`, this will always be registered in normal mode - **description** The text description that will be used in the user command - **name** The name of the backend, this will fallback to the command if its not set It will also allow to register a backend in a couple of different ways: - With a backend module table - With a backend module name - With a backend module name and override options This will look for for a lua module `ivy.backends.files`. The module will be required and registered as a backend. ```lua register_backend "ivy.backends.files" ``` This will do the same with the module string however, before the backend is registered the keymap will be overridden ```lua register_backend({ "ivy.backends.file", { keymap = "" } }) ``` Test Plan: CI / Manual testing locally --- lua/ivy/register_backend.lua | 54 +++++++++++++++++++++++ lua/ivy/register_backend_spec.lua | 71 +++++++++++++++++++++++++++++++ plugin/ivy.lua | 33 +++----------- 3 files changed, 132 insertions(+), 26 deletions(-) create mode 100644 lua/ivy/register_backend.lua create mode 100644 lua/ivy/register_backend_spec.lua diff --git a/lua/ivy/register_backend.lua b/lua/ivy/register_backend.lua new file mode 100644 index 0000000..f314588 --- /dev/null +++ b/lua/ivy/register_backend.lua @@ -0,0 +1,54 @@ +---@class IvyBackend +---@field command string The command this backend will have +---@field items fun(input: string): { content: string }[] | string The callback function to get the items to select from +---@field callback fun(result: string, action: string) The callback function to run when a item is selected +---@field description string? The description of the backend, this will be used in the keymaps +---@field name string? The name of the backend, this will fallback to the command if its not set +---@field keymap string? The keymap to trigger this backend + +---@class IvyBackendOptions +---@field command string The command this backend will have +---@field keymap string? The keymap to trigger this backend + +---Register a new backend +--- +---This will create all the commands and set all the keymaps for the backend +---@param backend IvyBackend +local register_backend_class = function(backend) + local user_command_options = { bang = true } + if backend.description ~= nil then + user_command_options.desc = backend.description + end + + local name = backend.name or backend.command + vim.api.nvim_create_user_command(backend.command, function() + vim.ivy.run(name, backend.items, backend.callback) + end, user_command_options) + + if backend.keymap ~= nil then + vim.api.nvim_set_keymap("n", backend.keymap, "" .. backend.command .. "", { nowait = true, silent = true }) + end +end + +---@param backend IvyBackend | { ["1"]: string, ["2"]: IvyBackendOptions} | string The backend or backend module +---@param options IvyBackendOptions? The options for the backend, that will be merged with the backend +local register_backend = function(backend, options) + if type(backend[1]) == "string" then + options = backend[2] + backend = require(backend[1]) + end + + if type(backend) == "string" then + backend = require(backend) + end + + if options then + for key, value in pairs(options) do + backend[key] = value + end + end + + register_backend_class(backend) +end + +return register_backend diff --git a/lua/ivy/register_backend_spec.lua b/lua/ivy/register_backend_spec.lua new file mode 100644 index 0000000..ab51e4e --- /dev/null +++ b/lua/ivy/register_backend_spec.lua @@ -0,0 +1,71 @@ +local register_backend = require "ivy.register_backend" + +local function get_command(name) + local command_iter = vim.api.nvim_get_commands {} + + for _, cmd in pairs(command_iter) do + if cmd.name == name then + return cmd + end + end + + return nil +end + +local function get_keymap(mode, rhs) + local keymap_iter = vim.api.nvim_get_keymap(mode) + for _, keymap in pairs(keymap_iter) do + if keymap.rhs == rhs then + return keymap + end + end + + return nil +end + +describe("register_backend", function() + after_each(function() + vim.api.nvim_del_user_command "IvyFd" + + local keymap = get_keymap("n", "IvyFd") + if keymap then + vim.api.nvim_del_keymap("n", keymap.lhs) + end + end) + + it("registers a backend from a string with the default options", function() + register_backend "ivy.backends.files" + + local command = get_command "IvyFd" + assert.is_not_nil(command) + + local keymap = get_keymap("n", "IvyFd") + assert.is_not_nil(keymap) + end) + + it("allows you to override the keymap", function() + register_backend("ivy.backends.files", { keymap = "" }) + + local keymap = get_keymap("n", "IvyFd") + assert(keymap ~= nil) + assert.are.equal("", keymap.lhs) + end) + + it("allows you to pass in a hole backend module", function() + register_backend(require "ivy.backends.files") + + local command = get_command "IvyFd" + assert.is_not_nil(command) + + local keymap = get_keymap("n", "IvyFd") + assert.is_not_nil(keymap) + end) + + it("allows you to pass in a hole backend module", function() + register_backend { "ivy.backends.files", { keymap = "" } } + + local keymap = get_keymap("n", "IvyFd") + assert(keymap ~= nil) + assert.are.equal("", keymap.lhs) + end) +end) diff --git a/plugin/ivy.lua b/plugin/ivy.lua index 383e866..81169f9 100644 --- a/plugin/ivy.lua +++ b/plugin/ivy.lua @@ -1,30 +1,11 @@ local controller = require "ivy.controller" +local register_backend = require "ivy.register_backend" -- Put the controller in to the vim global so we can access it in mappings -- better without requires. You can call controller commands like `vim.ivy.xxx`. -- luacheck: ignore vim.ivy = controller -local register_backend = function(backend) - assert(backend.command, "The backend must have a command") - assert(backend.items, "The backend must have a items function") - assert(backend.callback, "The backend must have a callback function") - - local user_command_options = { bang = true } - if backend.description ~= nil then - user_command_options.desc = backend.description - end - - local name = backend.name or backend.command - vim.api.nvim_create_user_command(backend.command, function() - vim.ivy.run(name, backend.items, backend.callback) - end, user_command_options) - - if backend.keymap ~= nil then - vim.api.nvim_set_keymap("n", backend.keymap, "" .. backend.command .. "", { nowait = true, silent = true }) - end -end - vim.paste = (function(overridden) return function(lines, phase) local file_type = vim.api.nvim_buf_get_option(0, "filetype") @@ -36,15 +17,15 @@ vim.paste = (function(overridden) end end)(vim.paste) -register_backend(require "ivy.backends.buffers") -register_backend(require "ivy.backends.files") -register_backend(require "ivy.backends.lines") -register_backend(require "ivy.backends.lsp-workspace-symbols") +register_backend "ivy.backends.buffers" +register_backend "ivy.backends.files" +register_backend "ivy.backends.lines" +register_backend "ivy.backends.lsp-workspace-symbols" if vim.fn.executable "rg" then - register_backend(require "ivy.backends.rg") + register_backend "ivy.backends.rg" elseif vim.fn.executable "ag" then - register_backend(require "ivy.backends.ag") + register_backend "ivy.backends.ag" end vim.cmd "highlight IvyMatch cterm=bold gui=bold"