AckslD / muren.nvim

Multiple replacements in neovim
363 stars 5 forks source link

muren.nvim

Neovim plugin for doing multiple search and replace with ease.

:warning: This plugins is in its early days so feel free to open issues if you stumble on issues, have ideas or are missing some things to configure.

What does this plugin do

Sometimes you may want to do some number of search-and-replacements that follow a certain structure. Lets say you have some variables named a_0, a_1 and a_2 but you instead want to rename them to x, y and z. Using builtin methods there are a few ways to do that (and maybe more which I don't know):

All of which I find somewhat cumbersome.

With muren ui, you get two buffers where you can enter a sequence of patterns in the left one (one per line) in the right buffer enter the replacements in the corresponding row. For example it would look something like:

a_0 | x
a_1 | y
a_2 | z

where you can use all your vim-skills to populate the buffers (eg <C-v>, <C-a> etc etc).

See examples below for some screencasts of how this looks like with muren.

Features

Installation

Use your favorite plugin manager, eg with lazy.nvim:

{
  'AckslD/muren.nvim',
  config = true,
}

:exclamation: requires nvim 0.9.

Usage

By default the following commands are created:

The UI uses the following normal mode keymaps (buffer local):

See below for how to configure there.

Pass create_commands = false to require('muren').setup to not create them.

You can also access this using lua as the following functions:

Muren's commands and exposed functions take optional arguments to position the ui windows by anchor and offset (command completion available), e.g.:

:MurenOpen top or :MurenToggle top_left 5 10 (anchor, vertical offset, horizontal offset)

require('muren.api').open_ui({anchor = "top_left", vertical_offset = 5, horizontal_offset = 10})

Configuration

Pass settings to require('muren').setup. The current defaults are:

{
  -- general
  create_commands = true,
  filetype_in_preview = true,
  -- default togglable options
  two_step = false,
  all_on_line = true,
  preview = true,
  cwd = false,
  files = '**/*',
  -- keymaps
  keys = {
    close = 'q',
    toggle_side = '<Tab>',
    toggle_options_focus = '<C-s>',
    toggle_option_under_cursor = '<CR>',
    scroll_preview_up = '<Up>',
    scroll_preview_down = '<Down>',
    do_replace = '<CR>',
    -- NOTE these are not guaranteed to work, what they do is just apply `:normal! u` vs :normal! <C-r>`
    -- on the last affected buffers so if you do some edit in these buffers in the meantime it won't do the correct thing
    do_undo = '<localleader>u',
    do_redo = '<localleader>r',
  },
  -- ui sizes
  patterns_width = 30,
  patterns_height = 10,
  options_width = 20,
  preview_height = 12,
  -- window positions
  anchor = 'center', -- Set to one of:
  -- 'center' | 'top' | 'bottom' | 'left' | 'right' | 'top_left' | 'top_right' | 'bottom_left' | 'bottom_right'
  vertical_offset = 0,  -- offsets are relative to anchors
  horizontal_offset = 0,
  -- options order in ui
  order = {
    'buffer',
    'dir',
    'files',
    'two_step',
    'all_on_line',
    'preview',
  },
  -- highlights used for options ui
  hl = {
    options = {
      on = '@string',
      off = '@variable.builtin',
    },
    preview = {
      cwd = {
        path = 'Comment',
        lnum = 'Number',
      },
    },
  },
}

Showcase

Basic usage

Basic usage replacing variables a_0, a_1 and a_2 to x, y and z:

https://user-images.githubusercontent.com/23341710/233819100-6e18e39e-37bc-42b4-82b4-237fa4eeee25.mp4

Swapping things

Using non-recursive (2-step) replacements one can swap variables with ease since they are first replaced to temporary placeholders. Toggle the option (see below) to see the difference.

https://user-images.githubusercontent.com/23341710/233819106-8d08cacd-2adc-467c-a784-6f5e59ef6ca1.mp4

Pick options interactively

You can change some options interactively while previewing your changes in the UI.

https://user-images.githubusercontent.com/23341710/233819114-406dcbe0-ec25-45fc-9240-84ba926a6c5e.mp4

Note in particular how things change in the preview.

Populate with unique previuous search matches

:MurenUnique might initially seem like a random command but something I find very useful. What it does is it finds all the matches of your last search and populates the unique set of these in the patterns pane of the UI. You can them replace all of them in some way but importantly you can do this differently for each unique match.

https://user-images.githubusercontent.com/23341710/233819184-df374312-8947-4b50-baf9-f3136b4d344e.mp4

Regexes

There is full support for builtin regex patterns. However this won't work then the two_step option is enabled:

https://user-images.githubusercontent.com/23341710/233902113-4b6a33d8-3f6b-4d33-bc46-67e865e0898e.mp4

Recursive search-replace in directory

By either pressing <CR> on dir or files in the options pan you enable search-replace across all files in the directory which matches the files-pattern:

https://github.com/AckslD/muren.nvim/assets/23341710/f41d5e58-e734-4283-9063-4607bd560f77

Undo/redo

You can also undo/redo replacements:

:warning: these keys are not guaranteed to work, what they do is just apply :normal! u vs :normal! ` on the last affected buffers so if you do some edit in these buffers in the meantime it won't do the correct thing.

Etymology

Here are two explanations for the name of the plugin, choose the one you like the most: