# Keybinds {#sec-keybinds} As of 0.4, there exists an API for writing your own keybinds and a couple of useful utility functions are available in the [extended standard library](https://github.com/NotAShelf/nvf/tree/main/lib). The following section contains a general overview to how you may utilize said functions. ## Custom Key Mappings Support for a Plugin {#sec-custom-key-mappings} To set a mapping, you should define it in `vim.keymaps`. An example, simple keybinding, can look like this: ```nix { vim.keymaps = [ { key = "wq"; mode = ["n"]; action = ":wq"; silent = true; desc = "Save file and quit"; } ]; } ``` There are many settings available in the options. Please refer to the [documentation](https://notashelf.github.io/nvf/options.html#opt-vim.keymaps) to see a list of them. **nvf** provides a list of helper commands, so that you don't have to write the mapping attribute sets every time: - `mkBinding = key: action: desc:` - makes a basic binding, with `silent` set to true. - `mkExprBinding = key: action: desc:` - makes an expression binding, with `lua`, `silent`, and `expr` set to true. - `mkLuaBinding = key: action: desc:` - makes an expression binding, with `lua`, and `silent` set to true. Do note that the Lua in these bindings is actual Lua, and not pasted into a `:lua` command. Therefore, you should either pass in a function like `require('someplugin').some_function`, without actually calling it, or you should define your own functions, for example ```lua function() require('someplugin').some_function() end ``` Additionally, to not have to repeat the descriptions, there's another utility function with its own set of functions: Utility function that takes two attribute sets: - `{ someKey = "some_value" }` - `{ someKey = { description = "Some Description"; }; }` and merges them into `{ someKey = { value = "some_value"; description = "Some Description"; }; }` ```nix addDescriptionsToMappings = actualMappings: mappingDefinitions: ``` This function can be used in combination with the same `mkBinding` functions as above, except they only take two arguments - `binding` and `action`, and have different names: - `mkSetBinding = binding: action:` - makes a basic binding, with `silent` set to true. - `mkSetExprBinding = binding: action:` - makes an expression binding, with `lua`, `silent`, and `expr` set to true. - `mkSetLuaBinding = binding: action:` - makes an expression binding, with `lua`, and `silent` set to true. You can read the source code of some modules to see them in action, but their usage should look something like this: ```nix # plugindefinition.nix {lib, ...}: with lib; { options.vim.plugin = { enable = mkEnableOption "Enable plugin"; # Mappings should always be inside an attrset called mappings mappings = { # mkMappingOption is a helper function from lib, # that takes a description (which will also appear in which-key), # and a default mapping (which can be null) toggleCurrentLine = mkMappingOption "Toggle current line comment" "gcc"; toggleCurrentBlock = mkMappingOption "Toggle current block comment" "gbc"; toggleOpLeaderLine = mkMappingOption "Toggle line comment" "gc"; toggleOpLeaderBlock = mkMappingOption "Toggle block comment" "gb"; toggleSelectedLine = mkMappingOption "Toggle selected comment" "gc"; toggleSelectedBlock = mkMappingOption "Toggle selected block" "gb"; }; }; } ``` ```nix # config.nix { config, pkgs, lib, ... }: with lib; with builtins; let cfg = config.vim.plugin; self = import ./plugindefinition.nix {inherit lib;}; mappingDefinitions = self.options.vim.plugin; # addDescriptionsToMappings is a helper function from lib, # that merges mapping values and their descriptions # into one nice attribute set mappings = addDescriptionsToMappings cfg.mappings mappingDefinitions; in { config = mkIf (cfg.enable) { # ... vim.maps.normal = mkMerge [ # mkSetBinding is another helper function from lib, # that actually adds the mapping with a description. (mkSetBinding mappings.findFiles " Telescope find_files") (mkSetBinding mappings.liveGrep " Telescope live_grep") (mkSetBinding mappings.buffers " Telescope buffers") (mkSetBinding mappings.helpTags " Telescope help_tags") (mkSetBinding mappings.open " Telescope") (mkSetBinding mappings.gitCommits " Telescope git_commits") (mkSetBinding mappings.gitBufferCommits " Telescope git_bcommits") (mkSetBinding mappings.gitBranches " Telescope git_branches") (mkSetBinding mappings.gitStatus " Telescope git_status") (mkSetBinding mappings.gitStash " Telescope git_stash") (mkIf config.vim.lsp.enable (mkMerge [ (mkSetBinding mappings.lspDocumentSymbols " Telescope lsp_document_symbols") (mkSetBinding mappings.lspWorkspaceSymbols " Telescope lsp_workspace_symbols") (mkSetBinding mappings.lspReferences " Telescope lsp_references") (mkSetBinding mappings.lspImplementations " Telescope lsp_implementations") (mkSetBinding mappings.lspDefinitions " Telescope lsp_definitions") (mkSetBinding mappings.lspTypeDefinitions " Telescope lsp_type_definitions") (mkSetBinding mappings.diagnostics " Telescope diagnostics") ])) ( mkIf config.vim.treesitter.enable (mkSetBinding mappings.treesitter " Telescope treesitter") ) ]; # ... }; } ``` ::: {.note} If you have come across a plugin that has an API that doesn't seem to easily allow custom keybindings, don't be scared to implement a draft PR. We'll help you get it done. :::