From af57bad10d71ca49dd5d81afcca17451cb732069 Mon Sep 17 00:00:00 2001 From: Gaetan Lepage Date: Fri, 17 Oct 2025 22:49:30 +0200 Subject: [PATCH] plugins/treesitter-textobjects: migrate to mkNeovimPlugin --- flake/dev/list-plugins/list-plugins.py | 5 - .../treesitter-textobjects/default.nix | 271 ++---------------- .../treesitter-textobjects/deprecations.nix | 102 +++++++ .../treesitter-textobjects/default.nix | 124 ++++---- 4 files changed, 191 insertions(+), 311 deletions(-) create mode 100644 plugins/by-name/treesitter-textobjects/deprecations.nix diff --git a/flake/dev/list-plugins/list-plugins.py b/flake/dev/list-plugins/list-plugins.py index c0887836..d8f25444 100755 --- a/flake/dev/list-plugins/list-plugins.py +++ b/flake/dev/list-plugins/list-plugins.py @@ -71,11 +71,6 @@ KNOWN_PATHS: dict[ ], ] = { "plugins/by-name/coq-thirdparty/default.nix": (State.OLD, Kind.NEOVIM, False), - "plugins/by-name/treesitter-textobjects/default.nix": ( - State.OLD, - Kind.NEOVIM, - True, - ), } for telescope_extension_name, has_depr_warnings in { "advanced-git-search": False, diff --git a/plugins/by-name/treesitter-textobjects/default.nix b/plugins/by-name/treesitter-textobjects/default.nix index 2dcf9eff..228ed574 100644 --- a/plugins/by-name/treesitter-textobjects/default.nix +++ b/plugins/by-name/treesitter-textobjects/default.nix @@ -1,251 +1,32 @@ -{ - lib, - helpers, - config, - pkgs, - ... -}: -with lib; -{ - options.plugins.treesitter-textobjects = - let - disable = helpers.defaultNullOpts.mkListOf types.str [ ] '' - List of languages to disable this module for. - ''; +{ lib, config, ... }: +lib.nixvim.plugins.mkNeovimPlugin { + name = "treesitter-textobjects"; + package = "nvim-treesitter-textobjects"; + maintainers = [ lib.maintainers.GaetanLepage ]; - mkKeymapsOption = - desc: - helpers.defaultNullOpts.mkAttrsOf ( - with types; - either str (submodule { - options = { - query = mkOption { - type = str; - description = ""; - example = "@class.inner"; - }; + # TODO: introduced 2025-10-17: remove after 26.05 + inherit (import ./deprecations.nix lib) deprecateExtraOptions optionsRenamedToSettings imports; - queryGroup = helpers.mkNullOrOption str '' - You can also use captures from other query groups like `locals.scm` - ''; + settingsExample = { + enable = true; + lookahead = true; + keymaps = { + ab = "@block.outer"; + ib = "@block.inner"; + ac = "@call.outer"; + ic = "@call.inner"; + }; + }; - desc = helpers.mkNullOrOption str '' - You can optionally set descriptions to the mappings (used in the `desc` - parameter of `nvim_buf_set_keymap`) which plugins like _which-key_ display. - ''; - }; - }) - ) { } desc; - in - lib.nixvim.plugins.neovim.extraOptionsOptions - // { - enable = mkEnableOption "treesitter-textobjects (requires plugins.treesitter.enable to be true)"; - - package = lib.mkPackageOption pkgs "treesitter-textobjects" { - default = [ - "vimPlugins" - "nvim-treesitter-textobjects" - ]; - }; - - select = { - enable = helpers.defaultNullOpts.mkBool false '' - Text object selection: - - Define your own text objects mappings similar to `ip` (inner paragraph) and `ap` - (a paragraph). - ''; - - inherit disable; - - lookahead = helpers.defaultNullOpts.mkBool false '' - Whether or not to look ahead for the textobject. - ''; - - keymaps = mkKeymapsOption '' - Map of keymaps to a tree-sitter query (`(function_definition) @function`) or capture - group (`@function.inner`). - ''; - - selectionModes = - helpers.defaultNullOpts.mkAttrsOf - ( - with types; - enum [ - "v" - "V" - "" - ] - ) - { } - '' - Map of capture group to `v`(charwise), `V`(linewise), or ``(blockwise), choose a - selection mode per capture, default is `v`(charwise). - ''; - - includeSurroundingWhitespace = helpers.defaultNullOpts.mkStrLuaFnOr types.bool false '' - `true` or `false`, when `true` textobjects are extended to include preceding or - succeeding whitespace. - - Can also be a function which gets passed a table with the keys `query_string` - (`@function.inner`) and `selection_mode` (`v`) and returns `true` of `false`. - - If you set this to `true` (default is `false`) then any textobject is extended to - include preceding or succeeding whitespace. - Succeeding whitespace has priority in order to act similarly to eg the built-in `ap`. - ''; - }; - - swap = { - enable = helpers.defaultNullOpts.mkBool false '' - Swap text objects: - - Define your own mappings to swap the node under the cursor with the next or previous one, - like function parameters or arguments. - ''; - - inherit disable; - - swapNext = mkKeymapsOption '' - Map of keymaps to a list of tree-sitter capture groups (`{@parameter.inner}`). - Capture groups that come earlier in the list are preferred. - ''; - - swapPrevious = mkKeymapsOption '' - Same as `swapNext`, but it will swap with the previous text object. - ''; - }; - - move = { - enable = helpers.defaultNullOpts.mkBool false '' - Go to next/previous text object~ - - Define your own mappings to jump to the next or previous text object. - This is similar to `|]m|`, `|[m|`, `|]M|`, `|[M|` Neovim's mappings to jump to the next or - previous function. - ''; - - inherit disable; - - setJumps = helpers.defaultNullOpts.mkBool true "Whether to set jumps in the jumplist."; - - gotoNextStart = mkKeymapsOption '' - Map of keymaps to a list of tree-sitter capture groups (`{@function.outer, - @class.outer}`). - The one that starts closest to the cursor will be chosen, preferring row-proximity to - column-proximity. - ''; - - gotoNextEnd = mkKeymapsOption '' - Same as `gotoNextStart`, but it jumps to the start of the text object. - ''; - - gotoPreviousStart = mkKeymapsOption '' - Same as `gotoNextStart`, but it jumps to the previous text object. - ''; - - gotoPreviousEnd = mkKeymapsOption '' - Same as `gotoNextEnd`, but it jumps to the previous text object. - ''; - - gotoNext = mkKeymapsOption '' - Will go to either the start or the end, whichever is closer. - Use if you want more granular movements. - Make it even more gradual by adding multiple queries and regex. - ''; - - gotoPrevious = mkKeymapsOption '' - Will go to either the start or the end, whichever is closer. - Use if you want more granular movements. - Make it even more gradual by adding multiple queries and regex. - ''; - }; - - lspInterop = { - enable = helpers.defaultNullOpts.mkBool false "LSP interop."; - - border = helpers.defaultNullOpts.mkEnumFirstDefault [ - "none" - "single" - "double" - "rounded" - "solid" - "shadow" - ] "Define the style of the floating window border."; - - peekDefinitionCode = mkKeymapsOption '' - Show textobject surrounding definition as determined using Neovim's built-in LSP in a - floating window. - Press the shortcut twice to enter the floating window - (when https://github.com/neovim/neovim/pull/12720 or its successor is merged). - ''; - - floatingPreviewOpts = helpers.defaultNullOpts.mkAttrsOf types.anything { } '' - Options to pass to `vim.lsp.util.open_floating_preview`. - For example, `maximum_height`. - ''; - }; + callSetup = false; + hasLuaConfig = false; + settingsDescription = "Options provided to `plugins.treesitter.settings.textobjects`."; + extraConfig = cfg: { + warnings = lib.nixvim.mkWarnings "plugins.treesitter-textobjects" { + when = !config.plugins.treesitter.enable; + message = "This plugin needs treesitter to function as intended."; }; - config = - let - cfg = config.plugins.treesitter-textobjects; - in - mkIf cfg.enable { - warnings = lib.nixvim.mkWarnings "plugins.treesitter-textobjects" { - when = !config.plugins.treesitter.enable; - message = "This plugin needs treesitter to function as intended."; - }; - - extraPlugins = [ cfg.package ]; - - plugins.treesitter.settings.textobjects = - with cfg; - let - processKeymapsOpt = - keymapsOptionValue: - helpers.ifNonNull' keymapsOptionValue ( - mapAttrs ( - key: mapping: - if isString mapping then - mapping - else - { - inherit (mapping) query; - query_group = mapping.queryGroup; - inherit (mapping) desc; - } - ) keymapsOptionValue - ); - in - { - select = with select; { - inherit enable disable lookahead; - keymaps = processKeymapsOpt keymaps; - selection_modes = selectionModes; - include_surrounding_whitespace = includeSurroundingWhitespace; - }; - swap = with swap; { - inherit enable disable; - swap_next = processKeymapsOpt swapNext; - swap_previous = processKeymapsOpt swapPrevious; - }; - move = with move; { - inherit enable disable; - set_jumps = setJumps; - goto_next_start = processKeymapsOpt gotoNextStart; - goto_next_end = processKeymapsOpt gotoNextEnd; - goto_previous_start = processKeymapsOpt gotoPreviousStart; - goto_previous_end = processKeymapsOpt gotoPreviousEnd; - goto_next = processKeymapsOpt gotoNext; - goto_previous = processKeymapsOpt gotoPrevious; - }; - lsp_interop = with lspInterop; { - inherit enable border; - peek_definition_code = processKeymapsOpt peekDefinitionCode; - floating_preview_opts = floatingPreviewOpts; - }; - } - // cfg.extraOptions; - }; + plugins.treesitter.settings.textobjects = cfg.settings; + }; } diff --git a/plugins/by-name/treesitter-textobjects/deprecations.nix b/plugins/by-name/treesitter-textobjects/deprecations.nix new file mode 100644 index 00000000..24a64d21 --- /dev/null +++ b/plugins/by-name/treesitter-textobjects/deprecations.nix @@ -0,0 +1,102 @@ +lib: { + imports = + let + basePluginPath = [ + "plugins" + "treesitter-textobjects" + ]; + settingsPath = basePluginPath ++ [ "settings" ]; + + mkChangedKeymapOption = + oldSubPath: newSubPath: + let + oldPath = basePluginPath ++ oldSubPath; + newPath = settingsPath ++ newSubPath; + + processKeymaps = lib.mapAttrs ( + key: mapping: + if lib.isString mapping then + mapping + else + lib.warn + '' + WARNING: `${lib.showOption newPath}` will expect `query_group` instead of `queryGroup`. + '' + { + inherit (mapping) query; + query_group = mapping.queryGroup; + inherit (mapping) desc; + } + ); + in + lib.mkChangedOptionModule oldPath newPath ( + config: + let + oldValue = lib.getAttrFromPath oldPath config; + in + (if oldValue == null then lib.id else processKeymaps) oldValue + ); + in + [ + ( + let + oldPath = basePluginPath ++ [ + "select" + "includeSurroundingWhitespace" + ]; + newPath = settingsPath ++ [ + "select" + "include_surrounding_whitespace" + ]; + in + lib.mkChangedOptionModule oldPath newPath ( + config: + let + oldValue = lib.getAttrFromPath oldPath config; + in + ( + if lib.isString oldValue then + lib.warn '' + WARNING: `${lib.showOption newPath}` will not convert the value to a raw lua string. + '' lib.nixvim.mkRaw + else + lib.id + ) + oldValue + ) + ) + (mkChangedKeymapOption [ "select" "keymaps" ] [ "select" "keymaps" ]) + (mkChangedKeymapOption [ "swap" "swapNext" ] [ "swap" "swap_next" ]) + (mkChangedKeymapOption [ "swap" "swapPrevious" ] [ "swap" "swap_previous" ]) + (mkChangedKeymapOption [ "move" "gotoNextStart" ] [ "move" "goto_next_start" ]) + (mkChangedKeymapOption [ "move" "gotoNextEnd" ] [ "move" "goto_next_end" ]) + (mkChangedKeymapOption [ "move" "gotoPreviousStart" ] [ "move" "goto_previous_start" ]) + (mkChangedKeymapOption [ "move" "gotoPreviousEnd" ] [ "move" "goto_previous_end" ]) + (mkChangedKeymapOption [ "move" "gotoNext" ] [ "move" "goto_next" ]) + (mkChangedKeymapOption [ "move" "gotoPrevious" ] [ "move" "goto_previous" ]) + (mkChangedKeymapOption + [ "lspInterop" "peekDefinitionCode" ] + [ "lsp_interop" "peek_definition_code" ] + ) + ]; + + deprecateExtraOptions = true; + + optionsRenamedToSettings = lib.map (lib.splitString ".") [ + "select.enable" + "select.disable" + "select.lookahead" + "select.selectionModes" + + "swap.enable" + "swap.disable" + + "move.enable" + "move.disable" + "move.setJumps" + + "lspInterop.enable" + "lspInterop.border" + "lspInterop.floatingPreviewOpts" + ]; +} diff --git a/tests/test-sources/plugins/by-name/treesitter-textobjects/default.nix b/tests/test-sources/plugins/by-name/treesitter-textobjects/default.nix index 77746af8..542b4193 100644 --- a/tests/test-sources/plugins/by-name/treesitter-textobjects/default.nix +++ b/tests/test-sources/plugins/by-name/treesitter-textobjects/default.nix @@ -12,71 +12,73 @@ treesitter-textobjects = { enable = true; - select = { - enable = true; - disable = [ ]; - lookahead = true; - keymaps = { - af = "@function.outer"; - "if" = "@function.inner"; - ac = "@class.outer"; - ic = { - query = "@class.inner"; - desc = "Select inner part of a class region"; + settings = { + select = { + enable = true; + disable = [ ]; + lookahead = true; + keymaps = { + af = "@function.outer"; + "if" = "@function.inner"; + ac = "@class.outer"; + ic = { + query = "@class.inner"; + desc = "Select inner part of a class region"; + }; + }; + selection_modes = { + "@parameter.outer" = "v"; + "@function.outer" = "V"; + "@class.outer" = ""; + }; + include_surrounding_whitespace = true; + }; + swap = { + enable = true; + disable = [ ]; + swap_next = { + "a" = "@parameter.inner"; + }; + swap_previous = { + "A" = "@parameter.inner"; }; }; - selectionModes = { - "@parameter.outer" = "v"; - "@function.outer" = "V"; - "@class.outer" = ""; + move = { + enable = true; + disable = [ ]; + set_jumps = true; + goto_next_start = { + "]m" = "@function.outer"; + "]]" = "@class.outer"; + }; + goto_next_end = { + "]M" = "@function.outer"; + "][" = "@class.outer"; + }; + goto_previous_start = { + "[m" = "@function.outer"; + "[[" = "@class.outer"; + }; + goto_previous_end = { + "[M" = "@function.outer"; + "[]" = "@class.outer"; + }; + goto_next = { + "]d" = "@conditional.outer"; + }; + goto_previous = { + "[d" = "@conditional.outer"; + }; }; - includeSurroundingWhitespace = true; - }; - swap = { - enable = true; - disable = [ ]; - swapNext = { - "a" = "@parameter.inner"; + lsp_interop = { + enable = true; + border = "none"; + peek_definition_code = { + "df" = "@function.outer"; + "dF" = "@class.outer"; + }; + floating_preview_opts = { }; }; - swapPrevious = { - "A" = "@parameter.inner"; - }; - }; - move = { - enable = true; - disable = [ ]; - setJumps = true; - gotoNextStart = { - "]m" = "@function.outer"; - "]]" = "@class.outer"; - }; - gotoNextEnd = { - "]M" = "@function.outer"; - "][" = "@class.outer"; - }; - gotoPreviousStart = { - "[m" = "@function.outer"; - "[[" = "@class.outer"; - }; - gotoPreviousEnd = { - "[M" = "@function.outer"; - "[]" = "@class.outer"; - }; - gotoNext = { - "]d" = "@conditional.outer"; - }; - gotoPrevious = { - "[d" = "@conditional.outer"; - }; - }; - lspInterop = { - enable = true; - border = "none"; - peekDefinitionCode = { - "df" = "@function.outer"; - "dF" = "@class.outer"; - }; - floatingPreviewOpts = { }; }; }; };