Search and replace in the current buffer with incremental preview, a convenient UI, and modern regex syntax.
%
), all matches in a line
(/g
), case-sensitive (/I
).--fixed-strings
, …Syntax comparison:
# all three are equivalent
# vim's :substitute
:% s/\(foo\)bar\(\.\)\@!/\1baz/gI
# vim's :substitute (very magic mode)
:% s/\v(foo)bar(\.)@!/\1baz/gI
# rip-substitute
(foo)bar(?!\.)
$1baz
Requirements
pcre2
support
brew install ripgrep
(already includes pcre2
by default)cargo install ripgrep --features pcre2
pcre2
by setting
regexOptions.pcre2 = false
in the config. However, some features like
lookaheads are not supported then.:TSInstall regex
(adds syntax highlighting)-- lazy.nvim
{
"chrisgrieser/nvim-rip-substitute",
cmd = "RipSubstitute",
opts = {}
keys = {
{
"<leader>fs",
function() require("rip-substitute").sub() end,
mode = { "n", "x" },
desc = " rip substitute",
},
},
},
-- packer
use {
"chrisgrieser/nvim-rip-substitute",
config = function()
require("rip-substitute").setup()
end,
}
-- default settings
require("rip-substitute").setup {
popupWin = {
title = " rip-substitute",
border = "single",
matchCountHlGroup = "Keyword",
noMatchHlGroup = "ErrorMsg",
hideSearchReplaceLabels = false,
---@type "top"|"bottom"
position = "bottom",
},
prefill = {
---@type "cursorWord"| false
normal = "cursorWord",
---@type "selectionFirstLine"| false (does not work with ex-command – see README)
visual = "selectionFirstLine",
startInReplaceLineIfPrefill = false,
alsoPrefillReplaceLine = false,
},
keymaps = { -- normal & visual mode, if not stated otherwise
abort = "q",
confirm = "<CR>",
insertModeConfirm = "<C-CR>",
prevSubst = "<Up>",
nextSubst = "<Down>",
toggleFixedStrings = "<C-f>", -- ripgrep's `--fixed-strings`
toggleIgnoreCase = "<C-c>", -- ripgrep's `--ignore-case`
openAtRegex101 = "R",
},
incrementalPreview = {
matchHlGroup = "IncSearch",
rangeBackdrop = {
enabled = true,
blend = 50, -- between 0 and 100
},
},
regexOptions = {
startWithFixedStringsOn = false,
startWithIgnoreCase = false,
-- pcre2 enables lookarounds and backreferences, but performs slower
pcre2 = true,
-- disable if you use named capture groups (see README for details)
autoBraceSimpleCaptureGroups = true,
},
editingBehavior = {
-- When typing `()` in the `search` line, automatically adds `$n` to the
-- `replace` line.
autoCaptureGroups = false,
},
notification = {
onSuccess = true,
icon = "",
}
}
[!NOTE] Any
ripgrep
config file set viaRIPGREP_CONFIG_PATH
is ignored by this plugin.
lua function
vim.keymap.set(
{ "n", "x" },
"<leader>fs",
function() require("rip-substitute").sub() end,
{ desc = " rip substitute" }
)
Ex-command
Alternatively, you can use the ex command :RipSubstitute
, which also
accepts a range
argument. Note that
when using the ex-command, visual mode and visual line mode both pass a range.
To prefill the current selection, you therefore need to use the lua function.
" Substitute in entire file. Prefills the *escaped* word under the cursor.
:RipSubstitute
" Substitute in line range of the visual selection.
:'<,'>RipSubstitute
" Substitute in given range (in this case: current line to end of file).
:.,$ RipSubstitute
You can also pass a prefill for the search value, in which case the prefill is not escaped.
:RipSubstitute prefilled_unescaped_string
Remember prefill
The function require("rip-substitute").rememberCursorWord()
can be used to
save the word under the cursor for the next time rip-substitute
is called.
(This overrides any other prefill for that run.)
One use case for this is to set a prefill for when you intend to run substitute
with a range, since calling rip-substitute
in visual line is not able to pick
up a prefill.
Filetype
The popup window uses the filetype rip-substitute
. This can be useful, for
instance, to disable auto-pairing plugins in the popup window.
autoBraceSimpleCaptureGroups
A gotcha of ripgrep
's regex syntax is that it treats $1a
as the named
capture group "1a" and not as the first capture group followed by the
letter "a." (See ripgrep
's man page on --replace
for details.)
If regexOptions.autoBraceSimpleCaptureGroups = true
(the default),
rip-substitute
automatically changes $1a
to ${1}a
, to make writing the
regex more intuitive. However, if you regularly use named capture groups, you
may want to disable this setting.
\n
or \r
) is not supported. See
issue #28.ripgrep
, use a plugin like
grug-far.nvim.In my day job, I am a sociologist studying the social mechanisms underlying the digital economy. For my PhD project, I investigate the governance of the app economy and how software ecosystems manage the tension between innovation and compatibility. If you are interested in this subject, feel free to get in touch.
I also occasionally blog about vim: Nano Tips for Vim
<img height='36' style='border:0px;height:36px;' src='https://cdn.ko-fi.com/cdn/kofi1.png?v=3' border='0' alt='Buy Me a Coffee at ko-fi.com' />