diff --git a/plugins/default.nix b/plugins/default.nix index 71d4cf5c..454a7c77 100644 --- a/plugins/default.nix +++ b/plugins/default.nix @@ -106,6 +106,7 @@ ./utils/mark-radar.nix ./utils/mini.nix ./utils/mkdnflow.nix + ./utils/multicursors.nix ./utils/neorg.nix ./utils/neogen.nix ./utils/notify.nix diff --git a/plugins/utils/multicursors.nix b/plugins/utils/multicursors.nix new file mode 100644 index 00000000..a703f42b --- /dev/null +++ b/plugins/utils/multicursors.nix @@ -0,0 +1,241 @@ +{ + config, + pkgs, + lib, + ... +}: +with lib; let + cfg = config.plugins.multicursors; + helpers = import ../helpers.nix {inherit lib;}; + + keyOptionType = with types; + attrsOf ( + submodule { + options = { + method = mkOption { + type = either str (enum [false]); + description = '' + Assigning `"nil"` exits from multi cursor mode. + Assigning `false` removes the binding + ''; + example = '' + function() + require('multicursors.utils').call_on_selections( + function(selection) + vim.api.nvim_win_set_cursor(0, { selection.row + 1, selection.col + 1 }) + local line_count = selection.end_row - selection.row + 1 + vim.cmd('normal ' .. line_count .. 'gcc') + end + ) + end + ''; + }; + + opts = mkOption { + type = attrsOf str; + default = {}; + description = "You can pass `:map-arguments` here."; + example = { + desc = "comment selections"; + }; + }; + }; + } + ); +in { + options = { + plugins.multicursors = + helpers.extraOptionsOptions + // { + enable = mkEnableOption "multicursors.nvim"; + + package = helpers.mkPackageOption "multicursors.nvim" pkgs.vimPlugins.multicursors-nvim; + + debugMode = helpers.defaultNullOpts.mkBool false "Enable debug mode."; + + createCommands = helpers.defaultNullOpts.mkBool true "Create Multicursor user commands."; + + updatetime = helpers.defaultNullOpts.mkUnsignedInt 50 '' + Selections get updated if this many milliseconds nothing is typed in the insert mode see + `:help updatetime`. + ''; + + nowait = helpers.defaultNullOpts.mkBool true "see `:help :map-nowait`."; + + normalKeys = helpers.mkNullOrOption keyOptionType '' + Normal mode key mappings. + + Default: see the [README.md](https://github.com/smoka7/multicursors.nvim) + + Example: + ```nix + { + # to change default lhs of key mapping, change the key + "," = { + # assigning `null` to method exits from multi cursor mode + # assigning `false` to method removes the binding + method = "require 'multicursors.normal_mode'.clear_others"; + + # you can pass :map-arguments here + opts = { desc = "Clear others"; }; + }; + "" = { + method = \'\' + function() + require('multicursors.utils').call_on_selections( + function(selection) + vim.api.nvim_win_set_cursor(0, { selection.row + 1, selection.col + 1 }) + local line_count = selection.end_row - selection.row + 1 + vim.cmd('normal ' .. line_count .. 'gcc') + end + ) + end + \'\'; + opts = { desc = "comment selections"; }; + }; + } + ``` + ''; + + insertKeys = helpers.mkNullOrOption keyOptionType '' + Insert mode key mappings. + + Default: see the [README.md](https://github.com/smoka7/multicursors.nvim) + ''; + + extendKeys = helpers.mkNullOrOption keyOptionType '' + Insert mode key mappings. + + Default: see the [README.md](https://github.com/smoka7/multicursors.nvim) + ''; + + hintConfig = { + type = helpers.mkNullOrOption (types.enum ["window" "cmdline" "statusline"]) '' + - "window": show hint in a floating window; + - "cmdline": show hint in a echo area; + - "statusline": show auto-generated hint in the statusline. + ''; + + position = + helpers.defaultNullOpts.mkEnum + [ + "top-left" + "top" + "top-right" + "middle-left" + "middle" + "middle-right" + "bottom-left" + "bottom" + "bottom-right" + ] + "bottom" + "Set the position of the hint."; + + offset = helpers.mkNullOrOption types.int '' + The offset from the nearest editor border. + (valid when `type` if `"window"`). + ''; + + border = helpers.defaultNullOpts.mkBorder "none" "the hint window" ""; + + showName = helpers.mkNullOrOption types.bool '' + Show hydras name or `HYDRA:` label at the beginning of an auto-generated hint. + ''; + + funcs = helpers.mkNullOrOption (with types; attrsOf str) '' + Attrs where keys are function names and values are functions themselves. + Each function should return string. + This functions can be required from hint with `%{func_name}` syntaxis. + ''; + }; + + generateHints = + genAttrs + ["normal" "insert" "extend"] + ( + mode: + helpers.defaultNullOpts.mkNullable (with types; either bool str) "false" '' + Hints for ${mode} mode. + + Accepted values: + - `true`: generate hints + - `false`: don't generate hints + - str: provide your own hints + '' + ); + }; + }; + + config = let + setupOptions = with cfg; let + mkMaps = value: + helpers.ifNonNull' value ( + mapAttrs + ( + key: mapping: + with mapping; { + method = + # `false` + if isBool method + then method + else helpers.mkRaw method; + inherit opts; + } + ) + value + ); + in + { + DEBUG_MODE = debugMode; + create_commands = createCommands; + inherit + updatetime + nowait + ; + normal_keys = mkMaps normalKeys; + insert_keys = mkMaps insertKeys; + extend_keys = mkMaps extendKeys; + hint_config = with hintConfig; { + inherit + type + position + offset + border + ; + show_name = showName; + funcs = + helpers.ifNonNull' funcs + (mapAttrs (name: helpers.mkRaw) funcs); + }; + generate_hints = + genAttrs + ["normal" "insert" "extend"] + ( + mode: let + value = generateHints.${mode}; + in + helpers.ifNonNull' + value + ( + if isBool value + then value + else + helpers.mkRaw '' + [[ + ${value} + ]] + '' + ) + ); + } + // extraOptions; + in + mkIf cfg.enable { + extraPlugins = [cfg.package]; + + extraConfigLua = '' + require("multicursors").setup(${helpers.toLuaObject setupOptions}) + ''; + }; +} diff --git a/tests/test-sources/plugins/utils/multicursors.nix b/tests/test-sources/plugins/utils/multicursors.nix new file mode 100644 index 00000000..01584f1f --- /dev/null +++ b/tests/test-sources/plugins/utils/multicursors.nix @@ -0,0 +1,56 @@ +{ + empty = { + plugins.multicursors.enable = true; + }; + + example = { + plugins.multicursors = { + enable = true; + + debugMode = false; + createCommands = true; + updatetime = 50; + nowait = true; + normalKeys = { + # to change default lhs of key mapping, change the key + "," = { + # assigning `null` to method exits from multi cursor mode + # assigning `false` to method removes the binding + method = "require 'multicursors.normal_mode'.clear_others"; + + # you can pass :map-arguments here + opts = {desc = "Clear others";}; + }; + "" = { + method = '' + function() + require('multicursors.utils').call_on_selections( + function(selection) + vim.api.nvim_win_set_cursor(0, { selection.row + 1, selection.col + 1 }) + local line_count = selection.end_row - selection.row + 1 + vim.cmd('normal ' .. line_count .. 'gcc') + end + ) + end + ''; + opts = {desc = "comment selections";}; + }; + }; + insertKeys = null; + extendKeys = null; + hintConfig = { + type = "window"; + position = "bottom"; + offset = 0; + border = "none"; + showName = true; + funcs = null; + }; + generateHints = { + normal = false; + insert = false; + extend = false; + }; + }; + }; +}