From 6e45d75a1e52dcc29e38c4c34e95422f7ea33642 Mon Sep 17 00:00:00 2001 From: Ade Attwood Date: Sun, 27 Oct 2024 09:19:50 +0000 Subject: [PATCH] feat: allow users to configure the window keymaps Summary: Now you can use the setup configuration to overwrite and override the keymaps in the ivy window. Each of the actions have now been givin a "key", you can use the keys to run action on keymaps defined in the config. They will all be registered in the buffer and run when pressed. The readme has been updated to document how do this. Test Plan: We have the tests that still run. It has been tested manually and gone though QA before getting merged. --- README.md | 69 +++++++++++++++++++++++++--------------------- lua/ivy/config.lua | 15 ++++++++++ lua/ivy/window.lua | 48 +++++++++++++++++++------------- 3 files changed, 81 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index ee98400..f3b0ded 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,23 @@ require("ivy").setup { -- default one. { "ivy.backends.files", { keymap = "" } }, }, + -- Set mappings of your own, you can use the action `key` to define the + -- action you want. + mappings = { + [""] = "complete" + } +} +``` + +When defining config overrides in the setup function, this will overwrite any +default config, not merge it. To merge the configuration you can use the +`vim.tbl_extend` use the default configuration and add any extra. + +```lua +require("ivy").setup { + mappings = vim.tbl_extend("force", config:get { "mappings" }, { + [""] = "destroy", + }), } ``` @@ -129,43 +146,31 @@ show. It will also provide functionality when actions are taken. The Command and Key Map are the default options provided by the backend, they can be customized when you register it. -| Module | Command | Key Map | Description | -| ------------------------------------ | ------------------ | ----------- | ----------------------------------------------------------- | -| `ivy.backends.files` | IvyFd | \p | Find files in your project with a custom rust file finder | -| `ivy.backends.ag` | IvyAg | \/ | Find content in files using the silver searcher | -| `ivy.backends.rg` | IvyRg | \/ | Find content in files using ripgrep cli tool | -| `ivy.backends.buffers` | IvyBuffers | \b | Search though open buffers | -| `ivy.backends.lines` | IvyLines | | Search the lines in the current buffer | -| `ivy.backends.lsp-workspace-symbols` | IvyWorkspaceSymbol | | Search for workspace symbols using the lsp workspace/symbol | +| Module | Command | Default Key Map | Description | +| ------------------------------------ | ------------------ | --------------- | ----------------------------------------------------------- | +| `ivy.backends.files` | IvyFd | \p | Find files in your project with a custom rust file finder | +| `ivy.backends.ag` | IvyAg | \/ | Find content in files using the silver searcher | +| `ivy.backends.rg` | IvyRg | \/ | Find content in files using ripgrep cli tool | +| `ivy.backends.buffers` | IvyBuffers | \b | Search though open buffers | +| `ivy.backends.lines` | IvyLines | | Search the lines in the current buffer | +| `ivy.backends.lsp-workspace-symbols` | IvyWorkspaceSymbol | | Search for workspace symbols using the lsp workspace/symbol | ### Actions Action can be run on selected candidates provide functionality -| Action | Key Map | Description | -| ------------------- | --------- | ---------------------------------------------------------------- | -| Complete | \ | Run the completion function, usually this will be opening a file | -| Vertical Split | \ | Run the completion function in a new vertical split | -| Split | \ | Run the completion function in a new split | -| Destroy | \ | Close the results window | -| Clear | \ | Clear the results window | -| Delete word | \ | Delete the word under the cursor | -| Next | \ | Move to the next candidate | -| Previous | \ | Move to the previous candidate | -| Next Checkpoint | \ | Move to the next candidate and keep Ivy open and focussed | -| Previous Checkpoint | \ | Move to the previous candidate and keep Ivy open and focussed | - -Add your own keymaps for an action by adding a `ftplugin/ivy.lua` file in your config. -Just add a simple keymap like this: - -```lua -vim.api.nvim_set_keymap( - "n", - "", - "lua vim.ivy.destroy()", - { noremap = true, silent = true, nowait = true } -) -``` +| Action | Key | Default Key Map | Description | +| ------------------- | --------------------- | --------------- | ---------------------------------------------------------------- | +| Complete | `complete` | \ | Run the completion function, usually this will be opening a file | +| Vertical Split | `vsplit` | \ | Run the completion function in a new vertical split | +| Split | `split` | \ | Run the completion function in a new split | +| Destroy | `destroy` | \ | Close the results window | +| Clear | `clear` | \ | Clear the results window | +| Delete word | `delete_word` | \ | Delete the word under the cursor | +| Next | `next` | \ | Move to the next candidate | +| Previous | `previous` | \ | Move to the previous candidate | +| Next Checkpoint | `next_checkpoint` | \ | Move to the next candidate and keep Ivy open and focussed | +| Previous Checkpoint | `previous_checkpoint` | \ | Move to the previous candidate and keep Ivy open and focussed | ## API diff --git a/lua/ivy/config.lua b/lua/ivy/config.lua index 8ff1e6a..e8095b9 100644 --- a/lua/ivy/config.lua +++ b/lua/ivy/config.lua @@ -28,6 +28,21 @@ config.default_config = { "ivy.backends.rg", "ivy.backends.lsp-workspace-symbols", }, + mappings = { + [""] = "destroy", + [""] = "clear", + [""] = "next", + [""] = "previous", + [""] = "next_checkpoint", + [""] = "previous_checkpoint", + [""] = "complete", + [""] = "vsplit", + [""] = "split", + [""] = "backspace", + [""] = "left", + [""] = "right", + [""] = "delete_word", + }, } return setmetatable(config, config_mt) diff --git a/lua/ivy/window.lua b/lua/ivy/window.lua index 087e94b..276d366 100644 --- a/lua/ivy/window.lua +++ b/lua/ivy/window.lua @@ -1,3 +1,5 @@ +local config = require "ivy.config" + -- Constent options that will be used for the keymaps local opts = { noremap = true, silent = true, nowait = true } @@ -37,6 +39,24 @@ local function call_gc(items) end end +local callbacks = { + destroy = "lua vim.ivy.destroy()", + clear = "lua vim.ivy.search('')", + next = "lua vim.ivy.next()", + previous = "lua vim.ivy.previous()", + next_checkpoint = "lua vim.ivy.next(); vim.ivy.checkpoint()", + previous_checkpoint = "lua vim.ivy.previous(); vim.ivy.checkpoint()", + + complete = "lua vim.ivy.complete(vim.ivy.action.EDIT)", + vsplit = "lua vim.ivy.complete(vim.ivy.action.VSPLIT)", + split = "lua vim.ivy.complete(vim.ivy.action.SPLIT)", + + backspace = "lua vim.ivy.input('BACKSPACE')", + left = "lua vim.ivy.input('LEFT')", + right = "lua vim.ivy.input('RIGHT')", + delete_word = "lua vim.ivy.input('DELETE_WORD')", +} + local window = {} window.index = 0 @@ -75,25 +95,15 @@ window.make_buffer = function() vim.api.nvim_buf_set_keymap(window.buffer, "n", chars[index], "lua vim.ivy.input('" .. char .. "')", opts) end - vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.destroy()", opts) - vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.search('')", opts) - vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.next()", opts) - vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.previous()", opts) - vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.next(); vim.ivy.checkpoint()", opts) - vim.api.nvim_buf_set_keymap( - window.buffer, - "n", - "", - "lua vim.ivy.previous(); vim.ivy.checkpoint()", - opts - ) - vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.complete(vim.ivy.action.EDIT)", opts) - vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.complete(vim.ivy.action.VSPLIT)", opts) - vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.complete(vim.ivy.action.SPLIT)", opts) - vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.input('BACKSPACE')", opts) - vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.input('LEFT')", opts) - vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.input('RIGHT')", opts) - vim.api.nvim_buf_set_keymap(window.buffer, "n", "", "lua vim.ivy.input('DELETE_WORD')", opts) + local mappings = config:get { "mappings" } + assert(mappings, "The mappings key is missing from the config, something has gone horribly wrong") + for key, value in pairs(mappings) do + if callbacks[value] == nil then + error("The mapping '" .. value .. "' is not a valid ivy callback") + end + + vim.api.nvim_buf_set_keymap(window.buffer, "n", key, callbacks[value], opts) + end end window.get_current_selection = function()