diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 558492a..9d24607 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -23,3 +23,153 @@ The contribution process is mostly documented in the [pull request template](.gi This project does not quite have a code of conduct yet. And to be honest, I'm not sure if I want one. I'm not expecting this project to be a hotbed of activity, but I do want to make sure that everyone who does contribute feels welcome and safe. As such, I will do my best to make sure that those who distrupt the project are dealt with swiftly and appropriately. If you feel that you are not being treated with respect, please contact me directly. + +## Custom key mappings support for a plugin + +To add custom keymappings to a plugin, a couple of helper functions are available in the project. + +To set a mapping, you should define it on `vim.maps.`. +The available modes are: + +- normal +- insert +- select +- visual +- terminal +- normalVisualOp +- visualOnly +- operator +- insertCommand +- lang +- command + +An example, simple keybinding, can look like this: + +```nix + +{ + vim.maps.normal = { + "wq" = { + 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/neovim-flake/options.html#opt-vim.maps.command._name_.action) to see a list of them. + +neovim-flake 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. +Note - the lua in these bindings is _actual_ lua, not pasted into a `:lua`. +Therefore, you either pass in a function like `require('someplugin').some_function`, without actually calling it, +or you define your own function, like `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: + +```nix +# Utility function that takes two attrsets: +# { someKey = "some_value" } and +# { someKey = { description = "Some Description"; }; } +# and merges them into +# { someKey = { value = "some_value"; description = "Some Description"; }; } + +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 +{ + pkgs, + config, + 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") + ) + ]; + + # ... + }; +} +``` + +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. diff --git a/lib/stdlib-extended.nix b/lib/stdlib-extended.nix index f43bc15..a54e5e9 100644 --- a/lib/stdlib-extended.nix +++ b/lib/stdlib-extended.nix @@ -4,9 +4,70 @@ nixpkgsLib: let mkNvimLib = import ./.; in - nixpkgsLib.extend (self: super: { + nixpkgsLib.extend (self: super: rec { nvim = mkNvimLib {lib = self;}; + mkLuaBinding = key: action: desc: + self.mkIf (key != null) { + "${key}" = { + inherit action desc; + lua = true; + silent = true; + }; + }; + + mkExprBinding = key: action: desc: + self.mkIf (key != null) { + "${key}" = { + inherit action desc; + lua = true; + silent = true; + expr = true; + }; + }; + + mkBinding = key: action: desc: + self.mkIf (key != null) { + "${key}" = { + inherit action desc; + silent = true; + }; + }; + + mkMappingOption = description: default: + self.mkOption { + type = self.types.nullOr self.types.str; + inherit default description; + }; + + # Utility function that takes two attrsets: + # { someKey = "some_value" } and + # { someKey = { description = "Some Description"; }; } + # and merges them into + # { someKey = { value = "some_value"; description = "Some Description"; }; } + addDescriptionsToMappings = actualMappings: mappingDefinitions: + self.attrsets.mapAttrs (name: value: let + isNested = self.isAttrs value; + returnedValue = + if isNested + then addDescriptionsToMappings actualMappings."${name}" mappingDefinitions."${name}" + else { + value = value; + description = mappingDefinitions."${name}".description; + }; + in + returnedValue) + actualMappings; + + mkSetBinding = binding: action: + mkBinding binding.value action binding.description; + + mkSetExprBinding = binding: action: + mkExprBinding binding.value action binding.description; + + mkSetLuaBinding = binding: action: + mkLuaBinding binding.value action binding.description; + # For forward compatibility. literalExpression = super.literalExpression or super.literalExample; literalDocBook = super.literalDocBook or super.literalExample; diff --git a/modules/assistant/copilot/config.nix b/modules/assistant/copilot/config.nix index 62d0251..112a3f7 100644 --- a/modules/assistant/copilot/config.nix +++ b/modules/assistant/copilot/config.nix @@ -7,6 +7,18 @@ with lib; with builtins; let cfg = config.vim.assistant.copilot; + + wrapPanelBinding = luaFunction: key: '' + function() + local s, _ = pcall(${luaFunction}) + + if not s then + local termcode = vim.api.nvim_replace_termcodes(${builtins.toJSON key}, true, false, true) + + vim.fn.feedkeys(termcode, 'n') + end + end + ''; in { config = mkIf cfg.enable { vim.startPlugins = [ @@ -18,7 +30,50 @@ in { require("copilot").setup({ -- available options: https://github.com/zbirenbaum/copilot.lua copilot_node_command = "${cfg.copilot_node_command}", + panel = { + keymap = { + jump_prev = false, + jump_next = false, + accept = false, + refresh = false, + open = false, + }, + layout = { + position = "${cfg.panel.position}", + ratio = ${toString cfg.panel.ratio}, + }, + }, + suggestion = { + keymap = { + accept = false, + accept_word = false, + accept_line = false, + next = false, + prev = false, + dismiss = false, + }, + }, }) ''; + + vim.maps.normal = mkMerge [ + (mkLuaBinding cfg.mappings.panel.jumpPrev (wrapPanelBinding "require(\"copilot.panel\").jump_prev" cfg.mappings.panel.jumpPrev) "[copilot] Accept suggestion") + (mkLuaBinding cfg.mappings.panel.jumpNext (wrapPanelBinding "require(\"copilot.panel\").jump_next" cfg.mappings.panel.jumpNext) "[copilot] Accept suggestion") + (mkLuaBinding cfg.mappings.panel.accept (wrapPanelBinding ''require("copilot.panel").accept'' cfg.mappings.panel.accept) "[copilot] Accept suggestion") + (mkLuaBinding cfg.mappings.panel.refresh (wrapPanelBinding "require(\"copilot.panel\").refresh" cfg.mappings.panel.refresh) "[copilot] Accept suggestion") + (mkLuaBinding cfg.mappings.panel.open (wrapPanelBinding '' + function() require("copilot.panel").open({ position = "${cfg.panel.position}", ratio = ${toString cfg.panel.ratio}, }) end + '' + cfg.mappings.panel.open) "[copilot] Accept suggestion") + ]; + + vim.maps.insert = mkMerge [ + (mkLuaBinding cfg.mappings.suggestion.accept "require(\"copilot.suggestion\").accept" "[copilot] Accept suggestion") + (mkLuaBinding cfg.mappings.suggestion.acceptLine "require(\"copilot.suggestion\").accept_line" "[copilot] Accept suggestion (line)") + (mkLuaBinding cfg.mappings.suggestion.acceptWord "require(\"copilot.suggestion\").accept_word" "[copilot] Accept suggestion (word)") + (mkLuaBinding cfg.mappings.suggestion.next "require(\"copilot.suggestion\").next" "[copilot] next suggestion") + (mkLuaBinding cfg.mappings.suggestion.prev "require(\"copilot.suggestion\").prev" "[copilot] previous suggestion") + (mkLuaBinding cfg.mappings.suggestion.dismiss "require(\"copilot.suggestion\").dismiss" "[copilot] dismiss suggestion") + ]; }; } diff --git a/modules/assistant/copilot/copilot.nix b/modules/assistant/copilot/copilot.nix index ad3e622..5529dcb 100644 --- a/modules/assistant/copilot/copilot.nix +++ b/modules/assistant/copilot/copilot.nix @@ -9,6 +9,86 @@ with builtins; { options.vim.assistant.copilot = { enable = mkEnableOption "Enable GitHub Copilot"; + panel = { + position = mkOption { + type = types.enum [ + "bottom" + "top" + "left" + "right" + ]; + default = "bottom"; + description = "Panel position"; + }; + ratio = mkOption { + type = types.float; + default = 0.4; + description = "Panel size"; + }; + }; + + mappings = { + panel = { + jumpPrev = mkOption { + type = types.nullOr types.str; + default = "[["; + description = "Jump to previous suggestion"; + }; + jumpNext = mkOption { + type = types.nullOr types.str; + default = "]]"; + description = "Jump to next suggestion"; + }; + accept = mkOption { + type = types.nullOr types.str; + default = ""; + description = "Accept suggestion"; + }; + refresh = mkOption { + type = types.nullOr types.str; + default = "gr"; + description = "Refresh suggestions"; + }; + open = mkOption { + type = types.nullOr types.str; + default = ""; + description = "Open suggestions"; + }; + }; + suggestion = { + accept = mkOption { + type = types.nullOr types.str; + default = ""; + description = "Accept suggetion"; + }; + acceptWord = mkOption { + type = types.nullOr types.str; + default = null; + description = "Accept next word"; + }; + acceptLine = mkOption { + type = types.nullOr types.str; + default = null; + description = "Accept next line"; + }; + prev = mkOption { + type = types.nullOr types.str; + default = ""; + description = "Previous suggestion"; + }; + next = mkOption { + type = types.nullOr types.str; + default = ""; + description = "Next suggestion"; + }; + dismiss = mkOption { + type = types.nullOr types.str; + default = ""; + description = "Dismiss suggestion"; + }; + }; + }; + copilot_node_command = mkOption { type = types.str; default = "${lib.getExe pkgs.nodejs-slim-16_x}"; diff --git a/modules/assistant/tabnine/config.nix b/modules/assistant/tabnine/config.nix index d925ed0..b959495 100644 --- a/modules/assistant/tabnine/config.nix +++ b/modules/assistant/tabnine/config.nix @@ -10,13 +10,43 @@ in { config = mkIf cfg.enable { vim.startPlugins = ["tabnine-nvim"]; + vim.maps.insert = mkMerge [ + (mkExprBinding cfg.mappings.accept '' + function() + local state = require("tabnine.state") + local completion = require("tabnine.completion") + + if not state.completions_cache then + return "${builtins.toJSON cfg.mappings.accept}" + end + + vim.schedule(completion.accept) + end + '' "orzel") + (mkExprBinding cfg.mappings.dismiss '' + function() + local state = require("tabnine.state") + local completion = require("tabnine.completion") + + if not state.completions_cache then + return "${builtins.toJSON cfg.mappings.dismiss}" + end + + vim.schedule(function() + completion.clear() + state.completions_cache = nil + end) + end + '' "orzel") + ]; + vim.luaConfigRC.tabnine-nvim = nvim.dag.entryAnywhere '' require('tabnine').setup({ disable_auto_comment = ${boolToString cfg.disable_auto_comment}, - accept_keymap = ${cfg.accept_keymap}, - dismiss_keymap = ${cfg.dismiss_keymap}, + accept_keymap = null, + dismiss_keymap = null, debounce_ms = ${cfg.debounce_ms}, - execlude_filetypes = ${cfg.execlude_filetypes}, + exclude_filetypes = ${cfg.exclude_filetypes}, }) ''; }; diff --git a/modules/assistant/tabnine/tabnine.nix b/modules/assistant/tabnine/tabnine.nix index f0674cb..b977cef 100644 --- a/modules/assistant/tabnine/tabnine.nix +++ b/modules/assistant/tabnine/tabnine.nix @@ -1,8 +1,4 @@ -{ - config, - lib, - ... -}: +{lib, ...}: with lib; with builtins; { options.vim.assistant.tabnine = { @@ -14,16 +10,9 @@ with builtins; { description = "Disable auto comment"; }; - accept_keymap = mkOption { - type = types.str; - default = ""; - description = "Accept keymap"; - }; - - dismiss_keymap = mkOption { - type = types.str; - default = ""; - description = "Dismiss keymap"; + mappings = { + accept = mkMappingOption "Accept [Tabnine]" ""; + dismiss = mkMappingOption "Dismiss [Tabnine]" ""; }; debounce_ms = mkOption { @@ -32,10 +21,10 @@ with builtins; { description = "Debounce ms"; }; - execlude_filetypes = mkOption { + exclude_filetypes = mkOption { type = types.listOf types.str; default = ["TelescopePrompt" "NvimTree" "alpha"]; - description = "Execlude filetypes"; + description = "Exclude filetypes"; }; }; } diff --git a/modules/basic/config.nix b/modules/basic/config.nix index 2cab900..435b715 100644 --- a/modules/basic/config.nix +++ b/modules/basic/config.nix @@ -10,21 +10,51 @@ in { config = { vim.startPlugins = ["plenary-nvim"]; - vim.nmap = mkIf cfg.disableArrows { - "" = ""; - "" = ""; - "" = ""; - "" = ""; - }; + vim.maps.normal = + mkIf cfg.disableArrows { + "" = { + action = ""; - vim.imap = mkIf cfg.disableArrows { - "" = ""; - "" = ""; - "" = ""; - "" = ""; - }; + noremap = false; + }; + "" = { + action = ""; - vim.nnoremap = mkIf cfg.mapLeaderSpace {"" = "";}; + noremap = false; + }; + "" = { + action = ""; + noremap = false; + }; + "" = { + action = ""; + noremap = false; + }; + } + // mkIf cfg.mapLeaderSpace { + "" = { + action = ""; + }; + }; + + vim.maps.insert = mkIf cfg.disableArrows { + "" = { + action = ""; + noremap = false; + }; + "" = { + action = ""; + noremap = false; + }; + "" = { + action = ""; + noremap = false; + }; + "" = { + action = ""; + noremap = false; + }; + }; vim.configRC.basic = nvim.dag.entryAfter ["globalsScript"] '' " Debug mode settings diff --git a/modules/comments/comment-nvim/comment-nvim.nix b/modules/comments/comment-nvim/comment-nvim.nix index 0e1369b..6ffbce0 100644 --- a/modules/comments/comment-nvim/comment-nvim.nix +++ b/modules/comments/comment-nvim/comment-nvim.nix @@ -1,11 +1,18 @@ -{ - config, - lib, - ... -}: +{lib, ...}: with lib; with builtins; { options.vim.comments.comment-nvim = { enable = mkEnableOption "Enable comment-nvim"; + + mappings = { + 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"; + }; }; } diff --git a/modules/comments/comment-nvim/config.nix b/modules/comments/comment-nvim/config.nix index 1a2e150..bc29111 100644 --- a/modules/comments/comment-nvim/config.nix +++ b/modules/comments/comment-nvim/config.nix @@ -6,14 +6,45 @@ with lib; with builtins; let cfg = config.vim.comments.comment-nvim; + self = import ./comment-nvim.nix { + inherit lib; + }; + mappings = self.options.vim.comments.comment-nvim.mappings; in { config = mkIf cfg.enable { vim.startPlugins = [ "comment-nvim" ]; + vim.maps.normal = mkMerge [ + (mkBinding cfg.mappings.toggleOpLeaderLine "(comment_toggle_linewise)" mappings.toggleOpLeaderLine.description) + (mkBinding cfg.mappings.toggleOpLeaderBlock "(comment_toggle_blockwise)" mappings.toggleOpLeaderBlock.description) + + (mkExprBinding cfg.mappings.toggleCurrentLine '' + function() + return vim.api.nvim_get_vvar('count') == 0 and '(comment_toggle_linewise_current)' + or '(comment_toggle_linewise_count)' + end + '' + mappings.toggleCurrentLine.description) + (mkExprBinding cfg.mappings.toggleCurrentBlock '' + function() + return vim.api.nvim_get_vvar('count') == 0 and '(comment_toggle_blockwise_current)' + or '(comment_toggle_blockwise_count)' + end + '' + mappings.toggleCurrentBlock.description) + ]; + + vim.maps.visualOnly = mkMerge [ + (mkBinding cfg.mappings.toggleSelectedLine "(comment_toggle_linewise_visual)" mappings.toggleSelectedLine.description) + (mkBinding cfg.mappings.toggleSelectedBlock "(comment_toggle_blockwise_visual)" mappings.toggleSelectedBlock.description) + ]; + vim.luaConfigRC.comment-nvim = nvim.dag.entryAnywhere '' - require('Comment').setup() + require('Comment').setup({ + mappings = { basic = false, extra = false, }, + }) ''; }; } diff --git a/modules/completion/nvim-cmp/config.nix b/modules/completion/nvim-cmp/config.nix index 36aee08..7bdda3d 100644 --- a/modules/completion/nvim-cmp/config.nix +++ b/modules/completion/nvim-cmp/config.nix @@ -1,5 +1,4 @@ { - pkgs, lib, config, ... @@ -8,6 +7,12 @@ with lib; with builtins; let cfg = config.vim.autocomplete; lspkindEnabled = config.vim.lsp.enable && config.vim.lsp.lspkind.enable; + + self = import ./nvim-cmp.nix {inherit lib;}; + mappingDefinitions = self.options.vim.autocomplete.mappings; + + mappings = addDescriptionsToMappings cfg.mappings mappingDefinitions; + builtSources = concatMapStringsSep "\n" @@ -46,6 +51,133 @@ in { "path" = "[Path]"; }; + vim.maps.insert = mkMerge [ + (mkSetLuaBinding mappings.complete '' + require('cmp').complete + '') + (mkSetLuaBinding mappings.confirm '' + function() + if not require('cmp').confirm({ select = true }) then + local termcode = vim.api.nvim_replace_termcodes(${builtins.toJSON mappings.confirm.value}, true, false, true) + + vim.fn.feedkeys(termcode, 'n') + end + end + '') + (mkSetLuaBinding mappings.next '' + function() + local has_words_before = function() + local line, col = unpack(vim.api.nvim_win_get_cursor(0)) + return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil + end + + local cmp = require('cmp') + + local feedkey = function(key, mode) + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(key, true, true, true), mode, true) + end + + if cmp.visible() then + cmp.select_next_item() + elseif vim.fn['vsnip#available'](1) == 1 then + feedkey("(vsnip-expand-or-jump)", "") + elseif has_words_before() then + cmp.complete() + else + fallback() + end + end + '') + (mkSetLuaBinding mappings.previous '' + function() + local cmp = require('cmp') + + local feedkey = function(key, mode) + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(key, true, true, true), mode, true) + end + + if cmp.visible() then + cmp.select_prev_item() + elseif vim.fn['vsnip#available'](-1) == 1 then + feedkeys("(vsnip-jump-prev)", "") + end + end + '') + (mkSetLuaBinding mappings.close '' + require('cmp').mapping.abort + '') + (mkSetLuaBinding mappings.scrollDocsUp '' + function() + require('cmp').mapping.scroll_docs(-4) + end + '') + (mkSetLuaBinding mappings.scrollDocsDown '' + function() + require('cmp').mapping.scroll_docs(4) + end + '') + ]; + + vim.maps.command = mkMerge [ + (mkSetLuaBinding mappings.complete '' + require('cmp').complete + '') + (mkSetLuaBinding mappings.close '' + require('cmp').mapping.close + '') + (mkSetLuaBinding mappings.scrollDocsUp '' + function() + require('cmp').mapping.scroll_docs(-4) + end + '') + (mkSetLuaBinding mappings.scrollDocsDown '' + function() + require('cmp').mapping.scroll_docs(4) + end + '') + ]; + + vim.maps.select = mkMerge [ + (mkSetLuaBinding mappings.next '' + function() + local cmp = require('cmp') + local has_words_before = function() + local line, col = unpack(vim.api.nvim_win_get_cursor(0)) + return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil + end + + local feedkey = function(key, mode) + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(key, true, true, true), mode, true) + end + + if cmp.visible() then + cmp.select_next_item() + elseif vim.fn['vsnip#available'](1) == 1 then + feedkey("(vsnip-expand-or-jump)", "") + elseif has_words_before() then + cmp.complete() + else + fallback() + end + end + '') + (mkSetLuaBinding mappings.previous '' + function() + local cmp = require('cmp') + + local feedkey = function(key, mode) + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(key, true, true, true), mode, true) + end + + if cmp.visible() then + cmp.select_prev_item() + elseif vim.fn['vsnip#available'](-1) == 1 then + feedkeys("(vsnip-jump-prev)", "") + end + end + '') + ]; + vim.luaConfigRC.completion = mkIf (cfg.type == "nvim-cmp") (dagPlacement '' local nvim_cmp_menu_map = function(entry, vim_item) -- name for each source @@ -59,16 +191,6 @@ in { ${optionalString lspkindEnabled '' lspkind_opts.before = ${cfg.formatting.format} ''} - - local has_words_before = function() - local line, col = unpack(vim.api.nvim_win_get_cursor(0)) - return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil - end - - local feedkey = function(key, mode) - vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(key, true, true, true), mode, true) - end - local cmp = require'cmp' cmp.setup({ snippet = { @@ -79,37 +201,6 @@ in { sources = { ${builtSources} }, - mapping = { - [''] = cmp.mapping(cmp.mapping.scroll_docs(-4), { 'i', 'c' }), - [''] = cmp.mapping(cmp.mapping.scroll_docs(4), { 'i', 'c'}), - [''] = cmp.mapping(cmp.mapping.complete(), { 'i', 'c'}), - [''] = cmp.config.disable, - [''] = cmp.mapping({ - i = cmp.mapping.abort(), - c = cmp.mapping.close(), - }), - [''] = cmp.mapping.confirm({ - select = true, - }), - [''] = cmp.mapping(function (fallback) - if cmp.visible() then - cmp.select_next_item() - elseif vim.fn['vsnip#available'](1) == 1 then - feedkey("(vsnip-expand-or-jump)", "") - elseif has_words_before() then - cmp.complete() - else - fallback() - end - end, { 'i', 's' }), - [''] = cmp.mapping(function (fallback) - if cmp.visible() then - cmp.select_prev_item() - elseif vim.fn['vsnip#available'](-1) == 1 then - feedkeys("(vsnip-jump-prev)", "") - end - end, { 'i', 's' }) - }, completion = { completeopt = 'menu,menuone,noinsert', }, diff --git a/modules/completion/nvim-cmp/nvim-cmp.nix b/modules/completion/nvim-cmp/nvim-cmp.nix index 9010a99..8011feb 100644 --- a/modules/completion/nvim-cmp/nvim-cmp.nix +++ b/modules/completion/nvim-cmp/nvim-cmp.nix @@ -1,34 +1,6 @@ -{ - pkgs, - lib, - config, - ... -}: +{lib, ...}: with lib; -with builtins; let - cfg = config.vim.autocomplete; - lspkindEnabled = config.vim.lsp.enable && config.vim.lsp.lspkind.enable; - builtSources = - concatMapStringsSep - "\n" - (n: "{ name = '${n}'},") - (attrNames cfg.sources); - - builtMaps = - concatStringsSep - "\n" - (mapAttrsToList - (n: v: - if v == null - then "" - else "${n} = '${v}',") - cfg.sources); - - dagPlacement = - if lspkindEnabled - then nvim.dag.entryAfter ["lspkind"] - else nvim.dag.entryAnywhere; -in { +with builtins; { options.vim = { autocomplete = { enable = mkOption { @@ -37,6 +9,16 @@ in { description = "enable autocomplete"; }; + mappings = { + complete = mkMappingOption "Complete [nvim-cmp]" ""; + confirm = mkMappingOption "Confirm [nvim-cmp]" ""; + next = mkMappingOption "Next item [nvim-cmp]" ""; + previous = mkMappingOption "Previous item [nvim-cmp]" ""; + close = mkMappingOption "Close [nvim-cmp]" ""; + scrollDocsUp = mkMappingOption "Scroll docs up [nvim-cmp]" ""; + scrollDocsDown = mkMappingOption "Scroll docs down [nvim-cmp]" ""; + }; + type = mkOption { type = types.enum ["nvim-cmp"]; default = "nvim-cmp"; diff --git a/modules/core/default.nix b/modules/core/default.nix index 78232b3..1c18abc 100644 --- a/modules/core/default.nix +++ b/modules/core/default.nix @@ -13,12 +13,106 @@ with builtins; let EOF ''; - mkMappingOption = it: - mkOption ({ - default = {}; - type = with types; attrsOf (nullOr str); - } - // it); + mkBool = value: description: + mkOption { + type = types.bool; + default = value; + description = description; + }; + + # Most of the keybindings code is highly inspired by pta2002/nixvim. Thank you! + mapConfigOptions = { + silent = + mkBool false + (nvim.nmd.asciiDoc "Whether this mapping should be silent. Equivalent to adding to a map."); + + nowait = + mkBool false + (nvim.nmd.asciiDoc "Whether to wait for extra input on ambiguous mappings. Equivalent to adding to a map."); + + script = + mkBool false + (nvim.nmd.asciiDoc "Equivalent to adding