diff --git a/plugins/by-name/harpoon/default.nix b/plugins/by-name/harpoon/default.nix index 2a1ff6b7..b25f9854 100644 --- a/plugins/by-name/harpoon/default.nix +++ b/plugins/by-name/harpoon/default.nix @@ -1,264 +1,41 @@ { lib, - helpers, config, - pkgs, ... }: -with lib; let - cfg = config.plugins.harpoon; - - projectConfigModule = types.submodule { - options = { - termCommands = helpers.mkNullOrOption (with types; listOf str) '' - List of predefined terminal commands for this project. - ''; - - marks = helpers.mkNullOrOption (with types; listOf str) '' - List of predefined marks (filenames) for this project. - ''; - }; - }; + inherit (lib) mkEnableOption; in -{ - options.plugins.harpoon = lib.nixvim.plugins.neovim.extraOptionsOptions // { - enable = mkEnableOption "harpoon"; +lib.nixvim.plugins.mkNeovimPlugin { + name = "harpoon"; + package = "harpoon2"; - package = lib.mkPackageOption pkgs "harpoon" { - default = [ - "vimPlugins" - "harpoon" - ]; - }; + maintainers = [ lib.maintainers.GaetanLepage ]; + setup = ":setup"; + + # TODO: introduced 2025-04-03: remove after 25.11 + imports = [ + ./deprecations.nix + ]; + + extraOptions = { enableTelescope = mkEnableOption "telescope integration"; + }; - keymapsSilent = mkOption { - type = types.bool; - description = "Whether harpoon keymaps should be silent."; - default = false; - }; - - keymaps = { - addFile = helpers.mkNullOrOption types.str '' - Keymap for marking the current file."; - ''; - - toggleQuickMenu = helpers.mkNullOrOption types.str '' - Keymap for toggling the quick menu."; - ''; - - navFile = helpers.mkNullOrOption (with types; attrsOf str) '' - Keymaps for navigating to marks. - - Examples: - navFile = { - "1" = ""; - "2" = ""; - "3" = ""; - "4" = ""; - }; - ''; - - navNext = helpers.mkNullOrOption types.str '' - Keymap for navigating to next mark."; - ''; - - navPrev = helpers.mkNullOrOption types.str '' - Keymap for navigating to previous mark."; - ''; - - gotoTerminal = helpers.mkNullOrOption (with types; attrsOf str) '' - Keymaps for navigating to terminals. - - Examples: - gotoTerminal = { - "1" = ""; - "2" = ""; - "3" = ""; - "4" = ""; - }; - ''; - - cmdToggleQuickMenu = helpers.mkNullOrOption types.str '' - Keymap for toggling the cmd quick menu. - ''; - - tmuxGotoTerminal = helpers.mkNullOrOption (with types; attrsOf str) '' - Keymaps for navigating to tmux windows/panes. - Attributes can either be tmux window ids or pane identifiers. - - Examples: - tmuxGotoTerminal = { - "1" = ""; - "2" = ""; - "{down-of}" = "g"; - }; - ''; - }; - - saveOnToggle = helpers.defaultNullOpts.mkBool false '' - Sets the marks upon calling `toggle` on the ui, instead of require `:w`. - ''; - - saveOnChange = helpers.defaultNullOpts.mkBool true '' - Saves the harpoon file upon every change. disabling is unrecommended. - ''; - - enterOnSendcmd = helpers.defaultNullOpts.mkBool false '' - Sets harpoon to run the command immediately as it's passed to the terminal when calling `sendCommand`. - ''; - - tmuxAutocloseWindows = helpers.defaultNullOpts.mkBool false '' - Closes any tmux windows harpoon that harpoon creates when you close Neovim. - ''; - - excludedFiletypes = helpers.defaultNullOpts.mkListOf types.str [ "harpoon" ] '' - Filetypes that you want to prevent from adding to the harpoon list menu. - ''; - - markBranch = helpers.defaultNullOpts.mkBool false '' - Set marks specific to each git branch inside git repository. - ''; - - projects = mkOption { - default = { }; - description = '' - Predefined projetcs. The keys of this attrs should be the path to the project. - $HOME is working. - ''; - example = '' - projects = { - "$HOME/personal/vim-with-me/server" = { - termCommands = [ - "./env && npx ts-node src/index.ts" - ]; - }; - }; - ''; - type = types.attrsOf projectConfigModule; - }; - - menu = { - width = helpers.defaultNullOpts.mkInt 60 '' - Menu window width - ''; - - height = helpers.defaultNullOpts.mkInt 10 '' - Menu window height - ''; - - borderChars = helpers.defaultNullOpts.mkListOf types.str [ - "─" - "│" - "─" - "│" - "╭" - "╮" - "╯" - "╰" - ] "Border characters"; + settingsExample = { + settings = { + save_on_toggle = true; + sync_on_ui_close = false; }; }; - config = - let - projects = builtins.mapAttrs (name: value: { - term.cmds = value.termCommands; - mark.marks = helpers.ifNonNull' value.marks (map (mark: { filename = mark; }) value.marks); - }) cfg.projects; - - setupOptions = - with cfg; - { - global_settings = { - save_on_toggle = saveOnToggle; - save_on_change = saveOnChange; - enter_on_sendcmd = enterOnSendcmd; - tmux_autoclose_windows = tmuxAutocloseWindows; - excluded_filetypes = excludedFiletypes; - mark_branch = markBranch; - }; - - inherit projects; - - menu = { - inherit (menu) width height; - borderchars = menu.borderChars; - }; - } - // cfg.extraOptions; - in - mkIf cfg.enable { - assertions = lib.nixvim.mkAssertions "plugins.harpoon" [ - { - assertion = cfg.enableTelescope -> config.plugins.telescope.enable; - message = "The harpoon telescope integration needs telescope to function as intended."; - } - ]; - - extraPlugins = [ cfg.package ]; - - extraConfigLua = - let - telescopeCfg = ''require("telescope").load_extension("harpoon")''; - in - '' - require('harpoon').setup(${lib.nixvim.toLuaObject setupOptions}) - ${if cfg.enableTelescope then telescopeCfg else ""} - ''; - - keymaps = - let - km = cfg.keymaps; - - simpleMappings = flatten ( - mapAttrsToList - ( - optionName: luaFunc: - let - key = km.${optionName}; - in - optional (key != null) { - inherit key; - action.__raw = luaFunc; - } - ) - { - addFile = "require('harpoon.mark').add_file"; - toggleQuickMenu = "require('harpoon.ui').toggle_quick_menu"; - navNext = "require('harpoon.ui').nav_next"; - navPrev = "require('harpoon.ui').nav_prev"; - cmdToggleQuickMenu = "require('harpoon.cmd-ui').toggle_quick_menu"; - } - ); - - mkNavMappings = - name: genLuaFunc: - let - mappingsAttrs = km.${name}; - in - flatten ( - optionals (mappingsAttrs != null) ( - mapAttrsToList (id: key: { - inherit key; - action.__raw = genLuaFunc id; - }) mappingsAttrs - ) - ); - - allMappings = - simpleMappings - ++ (mkNavMappings "navFile" (id: "function() require('harpoon.ui').nav_file(${id}) end")) - ++ (mkNavMappings "gotoTerminal" (id: "function() require('harpoon.term').gotoTerminal(${id}) end")) - ++ (mkNavMappings "tmuxGotoTerminal" ( - id: "function() require('harpoon.tmux').gotoTerminal(${id}) end" - )); - in - helpers.keymaps.mkKeymaps { - mode = "n"; - options.silent = cfg.keymapsSilent; - } allMappings; + extraConfig = cfg: { + assertions = lib.nixvim.mkAssertions "plugins.harpoon" { + assertion = cfg.enableTelescope -> config.plugins.telescope.enable; + message = "The harpoon telescope integration needs telescope to function as intended."; }; + + plugins.telescope.enabledExtensions = lib.mkIf cfg.enableTelescope [ "harpoon" ]; + }; } diff --git a/plugins/by-name/harpoon/deprecations.nix b/plugins/by-name/harpoon/deprecations.nix new file mode 100644 index 00000000..43ecebaa --- /dev/null +++ b/plugins/by-name/harpoon/deprecations.nix @@ -0,0 +1,71 @@ +{ lib, ... }: +{ + imports = + let + basePluginPath = [ + "plugins" + "harpoon" + ]; + + commonWarning = '' + /!\ `plugins.harpoon` has been refactored to now use harpoon2 (https://github.com/ThePrimeagen/harpoon/tree/harpoon2). + ''; + + keymapsWarning = '' + ${commonWarning} + + The `plugins.harpoon` module no longer allows you to define your keymaps. + Please, manually define your keymaps using the top-level `keymaps` option. + + For example, + ``` + plugins.harpoon.keymaps = { + addFile = "a"; + toggleQuickMenu = ""; + navFile = { + "1" = ""; + "2" = ""; + "3" = ""; + "4" = ""; + }; + }; + ``` + + would become: + ``` + keymaps = [ + { mode = "n"; key = "a"; action.__raw = "function() require'harpoon':list():add() end"; } + { mode = "n"; key = ""; action.__raw = "function() require'harpoon'.ui:toggle_quick_menu(require'harpoon':list()) end"; } + { mode = "n"; key = ""; action.__raw = "function() require'harpoon':list():select(1) end"; } + { mode = "n"; key = ""; action.__raw = "function() require'harpoon':list():select(2) end"; } + { mode = "n"; key = ""; action.__raw = "function() require'harpoon':list():select(3) end"; } + { mode = "n"; key = ""; action.__raw = "function() require'harpoon':list():select(4) end"; } + ]; + ``` + ''; + + optionNames = [ + "saveOnToggle" + "saveOnChange" + "enterOnSendcmd" + "tmuxAutocloseWindows" + "excludedFiletypes" + "markBranch" + "projects" + "menu" + ]; + in + (map ( + optionName: + lib.mkRemovedOptionModule (basePluginPath ++ [ optionName ]) '' + ${commonWarning} + + You may now use `plugins.harpoon.settings` option to forward any value to the `require("harpoon"):setup()` call. + '' + ) optionNames) + ++ [ + (lib.mkRemovedOptionModule (basePluginPath ++ [ "keymaps" ]) keymapsWarning) + (lib.mkRemovedOptionModule (basePluginPath ++ [ "keymapsSilent" ]) keymapsWarning) + ]; + +} diff --git a/tests/test-sources/plugins/by-name/harpoon/default.nix b/tests/test-sources/plugins/by-name/harpoon/default.nix index 1da71b48..4e10b944 100644 --- a/tests/test-sources/plugins/by-name/harpoon/default.nix +++ b/tests/test-sources/plugins/by-name/harpoon/default.nix @@ -1,88 +1,96 @@ { empty = { - # Harpoon expects to access `~/.local/share/nvim/harpoon.json` which is not available in the - # test environment - test.runNvim = false; - plugins.harpoon.enable = true; }; - telescopeEnabled = { - # Harpoon expects to access `~/.local/share/nvim/harpoon.json` which is not available in the - # test environment - test.runNvim = false; - - plugins.telescope = { - enable = true; - }; - + defaults = { plugins.harpoon = { enable = true; - enableTelescope = true; - keymapsSilent = true; - keymaps = { - addFile = "a"; - navFile = { - "1" = ""; - "2" = ""; - "3" = ""; - "4" = ""; + # https://github.com/ThePrimeagen/harpoon/blob/harpoon2/lua/harpoon/config.lua + settings = { + settings = { + save_on_toggle = false; + sync_on_ui_close = false; + key.__raw = '' + function() + return vim.loop.cwd() + end + ''; }; - navNext = "b"; - navPrev = "c"; - gotoTerminal = { - "1" = "J"; - "2" = "K"; - "3" = "L"; - "4" = "M"; + default = { + select_with_nil = false; + encode.__raw = '' + function(obj) + return vim.json.encode(obj) + end + ''; + decode.__raw = '' + function(str) + return vim.json.decode(str) + end + ''; + display.__raw = '' + function(list_item) + return list_item.value + end + ''; + # Very long functions omitted for the sake of conciseness }; - cmdToggleQuickMenu = "d"; - tmuxGotoTerminal = { - "1" = ""; - "2" = ""; - "{down-of}" = "g"; - }; - }; - saveOnToggle = false; - saveOnChange = true; - enterOnSendcmd = false; - tmuxAutocloseWindows = false; - excludedFiletypes = [ "harpoon" ]; - markBranch = false; - projects = { - "$HOME/personal/vim-with-me/server" = { - termCommands = [ "./env && npx ts-node src/index.ts" ]; - }; - }; - menu = { - width = 60; - height = 10; - borderChars = [ - "─" - "│" - "─" - "│" - "╭" - "╮" - "╯" - "╰" - ]; }; }; - - plugins.web-devicons.enable = true; }; - telescopeDisabled = { - # Harpoon expects to access `~/.local/share/nvim/harpoon.json` which is not available in the - # test environment - test.runNvim = false; - + example = { plugins.harpoon = { enable = true; - enableTelescope = false; + settings = { + settings = { + save_on_toggle = true; + sync_on_ui_close = false; + }; + + # https://github.com/ThePrimeagen/harpoon/tree/harpoon2?tab=readme-ov-file#-api + cmd = { + add.__raw = '' + function(possible_value) + -- get the current line idx + local idx = vim.fn.line(".") + + -- read the current line + local cmd = vim.api.nvim_buf_get_lines(0, idx - 1, idx, false)[1] + if cmd == nil then + return nil + end + + return { + value = cmd, + context = { }, + } + end + ''; + + select.__raw = '' + function(list_item, list, option) + vim.cmd(list_item.value) + end + ''; + }; + }; + }; + }; + + telescopeEnabled = { + plugins = { + telescope.enable = true; + web-devicons.enable = true; + + harpoon = { + enable = true; + + enableTelescope = true; + }; }; }; }