From 04ad7937c0f74db1a8a1b8af44c895bd91792e15 Mon Sep 17 00:00:00 2001 From: theabm <72303015+theabm@users.noreply.github.com> Date: Mon, 8 Apr 2024 11:22:51 +0200 Subject: [PATCH] plugins/nvim-surround: init --- plugins/by-name/nvim-surround/default.nix | 129 ++++++ .../plugins/by-name/nvim-surround/default.nix | 367 ++++++++++++++++++ 2 files changed, 496 insertions(+) create mode 100644 plugins/by-name/nvim-surround/default.nix create mode 100644 tests/test-sources/plugins/by-name/nvim-surround/default.nix diff --git a/plugins/by-name/nvim-surround/default.nix b/plugins/by-name/nvim-surround/default.nix new file mode 100644 index 00000000..6445a481 --- /dev/null +++ b/plugins/by-name/nvim-surround/default.nix @@ -0,0 +1,129 @@ +{ + lib, + ... +}: +let + inherit (lib.nixvim) defaultNullOpts; + inherit (lib) types; +in +lib.nixvim.neovim-plugin.mkNeovimPlugin { + name = "nvim-surround"; + originalName = "nvim-surround"; + package = "nvim-surround"; + + maintainers = with lib.maintainers; [ + khaneliman + AndresBermeoMarinelli + ]; + + settingsOptions = { + keymaps = defaultNullOpts.mkAttrsOf types.str '' + { + insert = "s"; + insert_line = "S"; + normal = "ys"; + normal_cur = "yss"; + normal_line = "yS"; + normal_cur_line = "ySS"; + visual = "S"; + visual_line = "gS"; + delete = "ds"; + change = "cs"; + change_line = "cS"; + } + '' "Defines the keymaps used to perform surround actions."; + + aliases = defaultNullOpts.mkAttrsOf (with types; either str (listOf str)) '' + { + "a" = ">"; + "b" = ")"; + "B" = "}"; + "r" = "]"; + "q" = [ "\"" "'" "`" ]; + "s" = [ "}" "]" ")" ">" "\"" "'" "`" ]; + } + '' "Maps characters to other characters, or lists of characters."; + + surrounds = + let + surroundType = types.submodule { + options = { + add = lib.mkOption { + type = with types; either strLuaFn (listOf str); + description = '' + A function that returns the delimiter pair to be added to the buffer. + For "static" delimiter pairs it can be a list of strings representing the value + of the delimiter pair. + ''; + apply = v: if lib.isString v then lib.nixvim.mkRaw v else v; + example = [ + "( " + " )" + ]; + }; + find = lib.mkOption { + type = with types; maybeRaw str; + description = '' + A function that returns the "parent selection" for a surrounding pair. + It can also be string which is interpreted as a Lua pattern that represents the + selection. + ''; + example.__raw = '' + function() + return M.get_selection({ motion = "a(" }) + end + ''; + }; + delete = lib.mkOption { + type = with types; maybeRaw str; + description = '' + A function that returns the pair of selections to remove from the buffer when + deleting the surrounding pair. + It can also be string which is interpreted as a Lua pattern whose match groups + represent the left/right delimiter pair. + ''; + example = "^(. ?)().-( ?.)()$"; + }; + + change = lib.nixvim.mkNullOrOption (types.submodule { + options = { + target = lib.mkOption { + type = with types; maybeRaw str; + description = '' + A function that returns the pair of selections to be replaced in the buffer + when changing the surrounding pair. + It can also be a string which is interpreted as a Lua pattern whose match + groups represent the left/right delimiter pair. + ''; + example = "^<([^>]*)().-([^%/]*)()>$"; + }; + replacement = lib.mkOption { + type = types.rawLua; + description = '' + A function that returns the surrounding pair to replace the target + selections. + ''; + example.__raw = '' + function() + local result = M.get_input("Enter the function name: ") + if result then + return { { result }, { "" } } + end + end + ''; + }; + }; + }) "An attribute set with two keys: `target` and `replacement`."; + }; + }; + in + lib.nixvim.mkNullOrOption' { + type = with types; attrsOf (maybeRaw surroundType); + description = '' + Associates each key with a "surround". The attribute set contains + the 'add', 'find', 'delete', and 'change' keys. + ''; + pluginDefault = lib.literalMD "See [upstream default configuration](https://github.com/kylechui/nvim-surround/blob/main/lua/nvim-surround/config.lua)"; + }; + }; +} diff --git a/tests/test-sources/plugins/by-name/nvim-surround/default.nix b/tests/test-sources/plugins/by-name/nvim-surround/default.nix new file mode 100644 index 00000000..aa584dcf --- /dev/null +++ b/tests/test-sources/plugins/by-name/nvim-surround/default.nix @@ -0,0 +1,367 @@ +{ + empty = { + plugins.nvim-surround.enable = true; + }; + + default = { + plugins.nvim-surround = { + enable = true; + + settings = { + keymaps = { + insert = "s"; + insert_line = "S"; + normal = "ys"; + normal_cur = "yss"; + normal_line = "yS"; + normal_cur_line = "ySS"; + visual = "S"; + visual_line = "gS"; + delete = "ds"; + change = "cs"; + change_line = "cS"; + }; + + surrounds = { + "(" = { + add = [ + "( " + " )" + ]; + find.__raw = '' + function() + return M.get_selection({ motion = "a(" }) + end + ''; + delete = "^(. ?)().-( ?.)()$"; + }; + + ")" = { + add = [ + "(" + ")" + ]; + find.__raw = '' + function() + return M.get_selection({ motion = "a)" }) + end + ''; + delete = "^(.)().-(.)()$"; + }; + + "{" = { + add = [ + "{ " + " }" + ]; + find.__raw = '' + function() + return M.get_selection({ motion = "a{" }) + end + ''; + delete = "^(. ?)().-( ?.)()$"; + }; + + "}" = { + add = [ + "{" + "}" + ]; + find.__raw = '' + function() + return M.get_selection({ motion = "a}" }) + end + ''; + delete = "^(.)().-(.)()$"; + }; + + "<" = { + add = [ + "< " + " >" + ]; + find.__raw = '' + function() + return M.get_selection({ motion = "a<" }) + end + ''; + delete = "^(. ?)().-( ?.)()$"; + }; + + ">" = { + add = [ + "<" + ">" + ]; + find.__raw = '' + function() + return M.get_selection({ motion = "a>" }) + end + ''; + delete = "^(.)().-(.)()$"; + }; + + "[" = { + add = [ + "[ " + " ]" + ]; + find.__raw = '' + function() + return M.get_selection({ motion = "a[" }) + end + ''; + delete = "^(. ?)().-( ?.)()$"; + }; + + "]" = { + add = [ + "[" + "]" + ]; + find.__raw = '' + function() + return M.get_selection({ motion = "a]" }) + end + ''; + delete = "^(.)().-(.)()$"; + }; + + "'" = { + add = [ + "'" + "'" + ]; + find.__raw = '' + function() + return M.get_selection({ motion = "a'" }) + end + ''; + delete = "^(.)().-(.)()$"; + }; + + "\"" = { + add = [ + "\"" + "\"" + ]; + find.__raw = '' + function() + return M.get_selection({ motion = 'a"' }) + end + ''; + delete = "^(.)().-(.)()$"; + }; + + "`" = { + add = [ + "`" + "`" + ]; + find.__raw = '' + function() + return M.get_selection({ motion = "a`" }) + end + ''; + delete = "^(.)().-(.)()$"; + }; + + "i" = { + add = '' + function() + local left_delimiter = M.get_input("Enter the left delimiter: ") + local right_delimiter = left_delimiter and M.get_input("Enter the right delimiter: ") + if right_delimiter then + return { { left_delimiter }, { right_delimiter } } + end + end + ''; + find.__raw = "function() end"; + delete.__raw = "function() end"; + }; + + "t" = { + add = '' + function() + local user_input = M.get_input("Enter the HTML tag: ") + if user_input then + local element = user_input:match("^]*)") + local attributes = user_input:match("^]*%s+(.-)>?$") + local open = attributes and element .. " " .. attributes or element + local close = element + return { { "<" .. open .. ">" }, { "" } } + end + end + ''; + find.__raw = '' + function() + return M.get_selection({ motion = "at" }) + end + ''; + delete = "^(%b<>)().-(%b<>)()$"; + change = { + target = "^<([^%s<>]*)().-([^/]*)()>$"; + replacement.__raw = '' + function() + local user_input = M.get_input("Enter the HTML tag: ") + if user_input then + local element = user_input:match("^]*)") + local attributes = user_input:match("^]*%s+(.-)>?$") + local open = attributes and element .. " " .. attributes or element + local close = element + return { { open }, { close } } + end + end + ''; + }; + }; + + "T" = { + add = '' + function() + local user_input = M.get_input("Enter the HTML tag: ") + if user_input then + local element = user_input:match("^]*)") + local attributes = user_input:match("^]*%s+(.-)>?$") + local open = attributes and element .. " " .. attributes or element + local close = element + return { { "<" .. open .. ">" }, { "" } } + end + end + ''; + find.__raw = '' + function() + return M.get_selection({ motion = "at" }) + end + ''; + delete = "^(%b<>)().-(%b<>)()$"; + change = { + target = "^<([^>]*)().-([^/]*)()>$"; + replacement.__raw = '' + function() + local user_input = M.get_input("Enter the HTML tag: ") + if user_input then + local element = user_input:match("^]*)") + local attributes = user_input:match("^]*%s+(.-)>?$") + local open = attributes and element .. " " .. attributes or element + local close = element + return { { open }, { close } } + end + end + ''; + }; + }; + + "f" = { + add = '' + function() + local result = M.get_input("Enter the function name: ") + if result then + return { { result .. "(" }, { ")" } } + end + end + ''; + find.__raw = '' + function() + if vim.g.loaded_nvim_treesitter then + local selection = M.get_selection({ + query = { + capture = "@call.outer", + type = "textobjects", + }, + }) + if selection then + return selection + end + end + return M.get_selection({ pattern = "[^=%s%(%){}]+%b()" }) + end + ''; + delete = "^(.-%()().-(%))()$"; + change = { + target = "^.-([%w_]+)()%(.-%)()()$"; + replacement.__raw = '' + function() + local result = M.get_input("Enter the function name: ") + if result then + return { { result }, { "" } } + end + end + ''; + }; + }; + + invalid_key_behavior = { + add = '' + function(char) + if not char or char:find("%c") then + return nil + end + return { { char }, { char } } + end + ''; + find.__raw = '' + function(char) + if not char or char:find("%c") then + return nil + end + return M.get_selection({ + pattern = vim.pesc(char) .. ".-" .. vim.pesc(char), + }) + end + ''; + delete.__raw = '' + function(char) + if not char or char:find("%c") then + return nil + end + return M.get_selections({ + char = char, + pattern = "^(.)().-(.)()$", + }) + end + ''; + }; + }; + + aliases = { + "a" = ">"; + "b" = ")"; + "B" = "}"; + "r" = "]"; + "q" = [ + "\"" + "'" + "`" + ]; + "s" = [ + "}" + "]" + ")" + ">" + "\"" + "'" + "`" + ]; + }; + + highlight = { + duration = 0; + }; + + move_cursor = "begin"; + + indent_lines.__raw = '' + function(start, stop) + local b = vim.bo + -- Only re-indent the selection if a formatter is set up already + if start < stop and (b.equalprg ~= "" or b.indentexpr ~= "" or b.cindent or b.smartindent or b.lisp) then + vim.cmd(string.format("silent normal! %dG=%dG", start, stop)) + require("nvim-surround.cache").set_callback("") + end + end + ''; + }; + }; + }; +}