From 3fe60dbed04447dc7025a918250e78123dece601 Mon Sep 17 00:00:00 2001 From: Gaetan Lepage Date: Mon, 22 Apr 2024 11:22:05 +0200 Subject: [PATCH] plugins/spectre: init --- plugins/default.nix | 1 + plugins/utils/spectre.nix | 283 ++++++++++++++++++ tests/test-sources/plugins/utils/spectre.nix | 292 +++++++++++++++++++ typos.toml | 1 + 4 files changed, 577 insertions(+) create mode 100644 plugins/utils/spectre.nix create mode 100644 tests/test-sources/plugins/utils/spectre.nix diff --git a/plugins/default.nix b/plugins/default.nix index ac6189df..f8f62d1e 100644 --- a/plugins/default.nix +++ b/plugins/default.nix @@ -192,6 +192,7 @@ ./utils/sleuth.nix ./utils/smart-splits.nix ./utils/specs.nix + ./utils/spectre.nix ./utils/spider.nix ./utils/startify ./utils/startup.nix diff --git a/plugins/utils/spectre.nix b/plugins/utils/spectre.nix new file mode 100644 index 00000000..4f4550d4 --- /dev/null +++ b/plugins/utils/spectre.nix @@ -0,0 +1,283 @@ +{ + lib, + helpers, + config, + pkgs, + ... +}: +with lib; + helpers.neovim-plugin.mkNeovimPlugin config { + name = "spectre"; + originalName = "nvim-spectre"; + defaultPackage = pkgs.vimPlugins.nvim-spectre; + + maintainers = [maintainers.GaetanLepage]; + + description = '' + You may want to set the package for your find/replace tool(s) like shown below: + + ```nix + plugins.spectre.findPackage = pkgs.rg; + plugins.spectre.replacePackage = pkgs.gnused; + ``` + ''; + + settingsOptions = let + mkEngineOption = type: + helpers.mkNullOrOption + ( + with types; + attrsOf (submodule { + options = { + cmd = mkOption { + type = types.str; + description = "Executable to run."; + }; + + args = helpers.defaultNullOpts.mkListOf types.str "[]" '' + List of arguments to provide to the engine. + ''; + + options = + helpers.defaultNullOpts.mkAttrsOf + (types.submodule { + options = { + value = mkOption { + type = types.str; + example = "-i"; + description = "The option flag."; + }; + + icon = mkOption { + type = types.str; + example = "[I]"; + description = "The option icon."; + }; + + desc = helpers.mkNullOrStr '' + The description for this option. + ''; + }; + }) + "{}" + "The options for this engine."; + }; + }) + ) + '' + Definition of the ${type} engines. + + default: see [here](https://github.com/nvim-pack/nvim-spectre/blob/master/lua/spectre/config.lua) + ''; + in { + color_devicons = helpers.defaultNullOpts.mkBool true '' + Whether to enable color devicons. + ''; + + open_cmd = helpers.defaultNullOpts.mkStr "vnew" '' + The open command. + ''; + + live_update = helpers.defaultNullOpts.mkBool false '' + Auto execute search again when you write to any file in vim. + ''; + + lnum_for_results = helpers.defaultNullOpts.mkBool false '' + Show line number for search/replace results. + ''; + + line_sep_start = + helpers.defaultNullOpts.mkStr + "┌──────────────────────────────────────────────────────" + "Start of the line separator"; + + result_padding = helpers.defaultNullOpts.mkStr "│ " '' + Result padding string. + ''; + + line_sep = + helpers.defaultNullOpts.mkStr + "└──────────────────────────────────────────────────────" + "Line separator."; + + highlight = + helpers.defaultNullOpts.mkAttrsOf types.str + '' + { + headers = "SpectreHeader"; + ui = "SpectreBody"; + filename = "SpectreFile"; + filedirectory = "SpectreDir"; + search = "SpectreSearch"; + border = "SpectreBorder"; + replace = "SpectreReplace"; + } + '' + "Highlight groups."; + + mapping = + helpers.mkNullOrOption + ( + with types; + attrsOf (submodule { + options = { + map = mkOption { + type = types.str; + description = "Keyboard shortcut."; + }; + + cmd = mkOption { + type = types.str; + description = "Command to run."; + example = "lua require('spectre').tab()"; + }; + + desc = helpers.mkNullOrStr '' + Description for this mapping. + ''; + }; + }) + ) + '' + Keymaps declaration. + + default: see [here](https://github.com/nvim-pack/nvim-spectre/blob/master/lua/spectre/config.lua) + ''; + + find_engine = mkEngineOption "find"; + + replace_engine = mkEngineOption "replace"; + + default = { + find = { + cmd = helpers.defaultNullOpts.mkStr "rg" '' + Which find engine to use. Pick one from the `find_engine` list. + ''; + + options = helpers.defaultNullOpts.mkListOf types.str ''["ignore-case"]'' '' + Options to use for this engine. + ''; + }; + + replace = { + cmd = helpers.defaultNullOpts.mkStr "rg" '' + Which find engine to use. Pick one from the `replace_engine` list. + ''; + + options = helpers.defaultNullOpts.mkListOf types.str "[]" '' + Options to use for this engine. + ''; + }; + }; + + replace_vim_cmd = helpers.defaultNullOpts.mkStr "cdo" '' + The replace command to use within vim. + ''; + + is_open_target_win = helpers.defaultNullOpts.mkBool true '' + Open file on opener window. + ''; + + is_insert_mode = helpers.defaultNullOpts.mkBool false '' + Start open panel in insert mode. + ''; + + is_block_ui_break = helpers.defaultNullOpts.mkBool false '' + Mapping backspace and enter key to avoid ui break. + ''; + }; + + settingsExample = { + live_update = true; + is_insert_mode = false; + find_engine = { + rg = { + cmd = "rg"; + args = [ + "--color=never" + "--no-heading" + "--with-filename" + "--line-number" + "--column" + ]; + options = { + ignore-case = { + value = "--ignore-case"; + icon = "[I]"; + desc = "ignore case"; + }; + hidden = { + value = "--hidden"; + desc = "hidden file"; + icon = "[H]"; + }; + line = { + value = "-x"; + icon = "[L]"; + desc = "match in line"; + }; + word = { + value = "-w"; + icon = "[W]"; + desc = "match in word"; + }; + }; + }; + }; + default = { + find = { + cmd = "rg"; + options = ["word" "hidden"]; + }; + replace = { + cmd = "sed"; + }; + }; + }; + + extraOptions = let + userCommandSettings = config.plugins.spectre.settings.default; + + findPackages = { + rg = pkgs.ripgrep; + }; + + replacePackages = { + sed = pkgs.gnused; + inherit (pkgs) sd; + }; + + # `toString` will turn `null` into `"null"` to allow for the attrs indexation. + findDefaultPackage = findPackages.${toString userCommandSettings.find.cmd} or null; + replaceDefaultPackage = replacePackages.${toString userCommandSettings.replace.cmd} or null; + in { + findPackage = mkOption { + type = with types; nullOr package; + default = findDefaultPackage; + description = '' + Which package to install for the find command. + Defaults to `pkgs.$\{settings.default.find.cmd}`. + + Set to `null` to prevent the installation. + ''; + }; + + replacePackage = mkOption { + type = with types; nullOr package; + default = replaceDefaultPackage; + description = '' + Which package to install for the find command. + Defaults to `pkgs.$\{settings.default.replace.cmd}`. + + Set to `null` to prevent the installation. + ''; + }; + }; + + extraConfig = cfg: { + extraPackages = [ + cfg.findPackage + cfg.replacePackage + ]; + }; + } diff --git a/tests/test-sources/plugins/utils/spectre.nix b/tests/test-sources/plugins/utils/spectre.nix new file mode 100644 index 00000000..b957256b --- /dev/null +++ b/tests/test-sources/plugins/utils/spectre.nix @@ -0,0 +1,292 @@ +{pkgs, ...}: let + # Fails on darwin with: `module 'plenary.job' not found` + enable = !pkgs.stdenv.isDarwin; +in { + empty = { + plugins.spectre.enable = enable; + }; + + package-options-manual = { + plugins.spectre = { + inherit enable; + + findPackage = pkgs.ripgrep; + replacePackage = pkgs.gnused; + }; + }; + + package-options-from-settings = { + plugins.spectre = { + inherit enable; + + settings.default = { + find.cmd = "rg"; + replace.cmd = "sed"; + }; + }; + }; + + example = { + plugins.spectre = { + inherit enable; + + settings = { + live_update = true; + is_insert_mode = false; + find_engine = { + rg = { + cmd = "rg"; + args = [ + "--color=never" + "--no-heading" + "--with-filename" + "--line-number" + "--column" + ]; + options = { + ignore-case = { + value = "--ignore-case"; + icon = "[I]"; + desc = "ignore case"; + }; + hidden = { + value = "--hidden"; + desc = "hidden file"; + icon = "[H]"; + }; + line = { + value = "-x"; + icon = "[L]"; + desc = "match in line"; + }; + word = { + value = "-w"; + icon = "[W]"; + desc = "match in word"; + }; + }; + }; + }; + default = { + find = { + cmd = "rg"; + options = ["word" "hidden"]; + }; + replace = { + cmd = "sed"; + }; + }; + }; + }; + }; + + defaults = { + plugins.spectre = { + inherit enable; + + settings = { + filetype = "spectre_panel"; + namespace.__raw = "vim.api.nvim_create_namespace('SEARCH_PANEL')"; + namespace_ui.__raw = "vim.api.nvim_create_namespace('SEARCH_PANEL_UI')"; + namespace_header.__raw = "vim.api.nvim_create_namespace('SEARCH_PANEL_HEADER')"; + namespace_status.__raw = "vim.api.nvim_create_namespace('SEARCH_PANEL_STATUS')"; + namespace_result.__raw = "vim.api.nvim_create_namespace('SEARCH_PANEL_RESULT')"; + + lnum_UI = 8; + line_result = 10; + + line_sep_start = "┌──────────────────────────────────────────────────────"; + result_padding = "│ "; + line_sep = "└──────────────────────────────────────────────────────"; + color_devicons = true; + open_cmd = "vnew"; + live_update = false; + lnum_for_results = false; + highlight = { + headers = "SpectreHeader"; + ui = "SpectreBody"; + filename = "SpectreFile"; + filedirectory = "SpectreDir"; + search = "SpectreSearch"; + border = "SpectreBorder"; + replace = "SpectreReplace"; + }; + mapping = { + tab = { + map = ""; + cmd = "lua require('spectre').tab()"; + desc = "next query"; + }; + shift-tab = { + map = ""; + cmd = "lua require('spectre').tab_shift()"; + desc = "previous query"; + }; + toggle_line = { + map = "dd"; + cmd = "lua require('spectre').toggle_line()"; + desc = "toggle item"; + }; + enter_file = { + map = ""; + cmd = "lua require('spectre.actions').select_entry()"; + desc = "open file"; + }; + send_to_qf = { + map = "q"; + cmd = "lua require('spectre.actions').send_to_qf()"; + desc = "send all items to quickfix"; + }; + replace_cmd = { + map = "c"; + cmd = "lua require('spectre.actions').replace_cmd()"; + desc = "input replace command"; + }; + show_option_menu = { + map = "o"; + cmd = "lua require('spectre').show_options()"; + desc = "show options"; + }; + run_current_replace = { + map = "rc"; + cmd = "lua require('spectre.actions').run_current_replace()"; + desc = "replace item"; + }; + run_replace = { + map = "R"; + cmd = "lua require('spectre.actions').run_replace()"; + desc = "replace all"; + }; + change_view_mode = { + map = "v"; + cmd = "lua require('spectre').change_view()"; + desc = "change result view mode"; + }; + change_replace_sed = { + map = "trs"; + cmd = "lua require('spectre').change_engine_replace('sed')"; + desc = "use sed to replace"; + }; + change_replace_oxi = { + map = "tro"; + cmd = "lua require('spectre').change_engine_replace('oxi')"; + desc = "use oxi to replace"; + }; + toggle_live_update = { + map = "tu"; + cmd = "lua require('spectre').toggle_live_update()"; + desc = "update when vim writes to file"; + }; + toggle_ignore_case = { + map = "ti"; + cmd = "lua require('spectre').change_options('ignore-case')"; + desc = "toggle ignore case"; + }; + toggle_ignore_hidden = { + map = "th"; + cmd = "lua require('spectre').change_options('hidden')"; + desc = "toggle search hidden"; + }; + resume_last_search = { + map = "l"; + cmd = "lua require('spectre').resume_last_search()"; + desc = "repeat last search"; + }; + select_template = { + map = "rp"; + cmd = "lua require('spectre.actions').select_template()"; + desc = "pick template"; + }; + }; + find_engine = { + rg = { + cmd = "rg"; + args = [ + "--color=never" + "--no-heading" + "--with-filename" + "--line-number" + "--column" + ]; + options = { + ignore-case = { + value = "--ignore-case"; + icon = "[I]"; + desc = "ignore case"; + }; + hidden = { + value = "--hidden"; + desc = "hidden file"; + icon = "[H]"; + }; + }; + }; + ag = { + cmd = "ag"; + args = [ + "--vimgrep" + "-s" + ]; + options = { + ignore-case = { + value = "-i"; + icon = "[I]"; + desc = "ignore case"; + }; + hidden = { + value = "--hidden"; + desc = "hidden file"; + icon = "[H]"; + }; + }; + }; + }; + replace_engine = { + sed = { + cmd = "sed"; + args = [ + "-i" + "-E" + ]; + options = { + ignore-case = { + value = "--ignore-case"; + icon = "[I]"; + desc = "ignore case"; + }; + }; + }; + oxi = { + cmd = "oxi"; + args = []; + options = { + ignore-case = { + value = "i"; + icon = "[I]"; + desc = "ignore case"; + }; + }; + }; + sd = { + cmd = "sd"; + options = {}; + }; + }; + default = { + find = { + cmd = "rg"; + options = ["ignore-case"]; + }; + replace = { + cmd = "sed"; + }; + }; + replace_vim_cmd = "cdo"; + is_open_target_win = true; + is_insert_mode = false; + is_block_ui_break = false; + open_template = {}; + }; + }; + }; +} diff --git a/typos.toml b/typos.toml index 465ab6f0..90ebb342 100644 --- a/typos.toml +++ b/typos.toml @@ -8,3 +8,4 @@ deffered = "deffered" # TODO: ./plugins/utils/vim-matchup.nix Highligt = "Highligt" # TODO: ./plugins/utils/neogen.nix Annote = "Annote" # TODO: ./plugins/lsp/fidget.nix ket = "ket" # ./plugins/utils/sandwich.nix +tro = "tro" # ./plugins/utils/spectre.nix