Home

Awesome

Searchbox

Start your search from a more comfortable place, say the upper right corner?

Search and replace with a multi-step input

Getting Started

Make sure you have Neovim v0.5.1 or greater.

Dependencies

Installation

Use your favorite plugin manager. For example.

With vim-plug:

Plug 'MunifTanjim/nui.nvim'
Plug 'romgrk/searchbox.nvim'

With packer:

use {
  'romgrk/searchbox.nvim',
  requires = {
    {'MunifTanjim/nui.nvim'}
  }
}

With paq:

'VonHeikemen/searchbox.nvim';
'MunifTanjim/nui.nvim';

Types of search

There are four kinds of search:

Usage

There is a command for each kind of search which can be used in a keybinding.

vim.api.nvim_set_keymap(
  'n',
  '<leader>s',
  ':SearchBoxIncSearch<CR>',
  {noremap = true}
)
nnoremap <leader>s :SearchBoxIncSearch<CR>

They are also exposed as lua functions, so the following is also valid.

:lua require('searchbox').incsearch()<CR>

In-search mappings

In some cases, you can use the following mappings to toggle options while searching:

Visual mode

To get proper support in visual mode you'll need to add visual_mode=true to the list of arguments.

In this mode the search area is limited to the range set by the selected text. Similar to what the substitute command does in this case :'<,'>s/this/that/g.

vim.api.nvim_set_keymap(
  'x',
  '<leader>s',
  ':SearchBoxIncSearch visual_mode=true<CR>',
  {noremap = true}
)
xnoremap <leader>s :SearchBoxIncSearch visual_mode=true<CR>

When using the lua api add <Esc> at the beginning of the binding.

<Esc>:lua require('searchbox').incsearch({visual_mode = true})<CR>

Search arguments

You can tweak the behaviour of the search if you pass any of these properties:

Other arguments are exclusive to one type of search.

For match_all:

For replace:

Command Api

When using the command api the arguments are a space separated list of key/value pairs. The syntax for the arguments is this: key=value.

:SearchBoxMatchAll title=Match mode=exact visual_mode=true<CR>

Because whitespace acts like a separator between the arguments if you want to use it as a value you need to escape it, or use a quoted argument. If you want to use Match All as a title, these are your options.

:SearchBoxMatchAll title="Match All"<CR>
" or
:SearchBoxMatchAll title='Match All'<CR>
:SearchBoxMatchAll title=Match\ All<CR>

Note that escaping is specially funny inside a lua string, so you might need to use \\.

Is worth mention that argument parsing is done manually inside the plugin. Complex escape sequences are not taken into account. Just \" and \' to avoid conflict in quoted arguments, and \ to escape whitespace in a string argument without quotes.

Not being able to use whitespace freely makes it difficult to use default_value with this api, that's why it gets a special treatment. There is no default_value argument, instead everything that follows the -- argument is considered part of the search term.

:SearchBoxMatchAll title=Match clear_matches=true -- I want to search this<CR>

In the example above I want to search this will become the initial value for the search input. This becomes useful when you want to use advance techniques to set the initial value of your search (I'll show you some examples later).

If you only going to set the initial value, meaning you're not going to use any of the other arguments, you can omit the --. This is valid, too.

:SearchBoxMatchAll I want to search this<CR>

Lua api

In this case you'll be using lua functions of the searchbox module instead of commands. The arguments can be provided as a lua table.

:lua require('searchbox').match_all({title='Match All', clear_matches=true, default_value='I want to search this'})<CR>

Examples

Make a reverse search, like the default ?:

:SearchBoxIncSearch reverse=true<CR>

Make the highlight of match_all go away after submit.

:SearchBoxMatchAll clear_matches=true<CR>

Move to the nearest exact match without any fuss.

:SearchBoxSimple mode=exact<CR>

Start a search and replace.

:SearchBoxReplace<CR>

Use the word under the cursor to begin search and replace. (Normal mode).

:SearchBoxReplace -- <C-r>=expand('<cword>')<CR><CR>

Look for the exact word under the cursor.

:SearchBoxMatchAll mode=exact -- <C-r>=expand('<cword>')<CR><CR>

Use the selected text as a search term. (Visual mode):

Due to limitations on the input, it can't handle newlines well. So whatever you have selected, must be one line. The escape sequence \n can be use in the search term but will not be interpreted on the second input of search and replace.

y:SearchBoxReplace -- <C-r>"<CR>

Search and replace within the range of the selected text, and look for an exact match. (Visual mode)

:SearchBoxReplace mode=exact visual_mode=true<CR>

Confirm every match of search and replace.

:SearchBoxReplace confirm=menu<CR>
:SearchBoxReplace confirm=menu visual_mode=true<CR>

Default keymaps

Inside the input you can use the following keymaps:

In the confirm menu (of search and replace):

The "native" confirm method:

Configuration

If you want to change anything in the UI or add a "hook" you can use .setup().

This are the defaults.

require('searchbox').setup({
  icons = {
    search = ' ',
    case_sensitive = ' ',
    pattern = ' ',
    fuzzy = ' ',
  },
  popup = {
    relative = 'win',
    position = {
      row = '5%',
      col = '95%',
    },
    size = 30,
    border = {
      style = 'rounded',
      highlight = 'FloatBorder',
      text = {
        top = ' Search ',
        top_align = 'left',
      },
    },
    win_options = {
      winhighlight = 'Normal:Normal',
    },
  },
  hooks = {
    before_mount = function(input)
      -- code
    end,
    after_mount = function(input)
      -- code
    end,
    on_done = function(value, search_type)
      -- code
    end
  }
})

before_mount and after_mount receive the instance of the input, so you can do anything with it.

on_done it's executed after the search is finished. In the case of a successful search it gets the value submitted and the type of search as arguments. When doing a search and replace, it gets executed after the last substitution takes place. In case the search was cancelled, the first argument is nil and the second argument is the type of search.

Caveats

It's very possible that I can't simulate every feature of the built-in search (/ and ?).

Contributing

Bug fixes are welcome. Everything else? Let's discuss it first.

If you want to improve the UI it will be better if you contribute to nui.nvim.

Credit

Thanks to VonHeikemen for the original work.