{"id":13526363,"url":"https://github.com/chrisgrieser/nvim-rip-substitute","last_synced_at":"2025-04-12T21:34:17.280Z","repository":{"id":242757616,"uuid":"810481430","full_name":"chrisgrieser/nvim-rip-substitute","owner":"chrisgrieser","description":"Search and replace in the current buffer with incremental preview, a convenient UI, and modern regex syntax.","archived":false,"fork":false,"pushed_at":"2025-04-12T09:31:54.000Z","size":238,"stargazers_count":241,"open_issues_count":3,"forks_count":10,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-12T09:32:28.983Z","etag":null,"topics":["neovim-plugin","nvim","nvim-plugin","replace-text","search-and-replace","substitution"],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chrisgrieser.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"custom":"https://www.paypal.me/ChrisGrieser","ko_fi":"pseudometa"}},"created_at":"2024-06-04T19:27:56.000Z","updated_at":"2025-04-12T09:31:58.000Z","dependencies_parsed_at":"2024-06-04T21:36:59.513Z","dependency_job_id":"5f8ec2ff-8ba7-4043-be4e-48bedbe7b44c","html_url":"https://github.com/chrisgrieser/nvim-rip-substitute","commit_stats":null,"previous_names":["chrisgrieser/rip-substitute"],"tags_count":0,"template":false,"template_full_name":"chrisgrieser/nvim-pseudometa-plugin-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisgrieser%2Fnvim-rip-substitute","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisgrieser%2Fnvim-rip-substitute/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisgrieser%2Fnvim-rip-substitute/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisgrieser%2Fnvim-rip-substitute/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chrisgrieser","download_url":"https://codeload.github.com/chrisgrieser/nvim-rip-substitute/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248637011,"owners_count":21137529,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["neovim-plugin","nvim","nvim-plugin","replace-text","search-and-replace","substitution"],"created_at":"2024-08-01T06:01:28.553Z","updated_at":"2025-04-12T21:34:17.275Z","avatar_url":"https://github.com/chrisgrieser.png","language":"Lua","funding_links":["https://www.paypal.me/ChrisGrieser","https://ko-fi.com/pseudometa","https://ko-fi.com/Y8Y86SQ91'"],"categories":["Lua","Search"],"sub_categories":["Assembly","Markdown and LaTeX"],"readme":"\u003c!-- LTeX: enabled=false --\u003e\n# rip-substitute 🪦\n\u003c!-- LTeX: enabled=true --\u003e\n\u003ca href=\"https://dotfyle.com/plugins/chrisgrieser/nvim-rip-substitute\"\u003e\n\u003cimg alt=\"badge\" src=\"https://dotfyle.com/plugins/chrisgrieser/nvim-rip-substitute/shield\"/\u003e\u003c/a\u003e\n\nSearch and replace in the current buffer with incremental preview, a convenient\nUI, and modern regex syntax.\n\n\u003chttps://github.com/chrisgrieser/nvim-rip-substitute/assets/73286100/4afad8d8-c0d9-4ba6-910c-0510d4b9b669\u003e\n\n## Table of Contents\n\n\u003c!-- toc --\u003e\n\n- [Features](#features)\n- [Installation](#installation)\n- [Configuration](#configuration)\n- [Usage](#usage)\n- [Advanced](#advanced)\n- [Limitations](#limitations)\n- [About the developer](#about-the-developer)\n\n\u003c!-- tocstop --\u003e\n\n## Features\n- Search and replace in the current buffer using\n  [ripgrep](https://github.com/BurntSushi/ripgrep).\n- Uses **common regex syntax** — no more dealing with arcane vim regex.\n- **Incremental preview** of matched strings and replacements \u0026 **live count**\n  of matches.\n- **Popup window** instead of command line. This entails:\n\t+ Syntax highlighting of the regex.\n\t+ Editing with vim motions.\n\t+ No more dealing with delimiters.\n- **Sensible defaults**: entire buffer (`%`), all matches in a line (`/g`),\n  case-sensitive (`/I`).\n- Can substitute only in a **range**, with visual emphasis of the range\n- **History** of previous substitutions.\n- **Performant**: In a file with 5000 lines and thousands of matches, still\n  performs *blazingly fast*.™\n- **Regex101 integration**: Open the planned substitution in a pre-configured\n  [regex101](https://regex101.com/) browser tab for debugging.\n- **Quality-of-Life features**: automatic prefill of the escaped cursorword,\n  adaptive popup window width, toggle `ripgrep` flags, …\n- Syntax comparison:\n\n```txt\n# all three are equivalent\n\n# vim's :substitute\n:% s/\\(foo\\)bar\\(\\.\\)\\@!/\\1baz/gI\n\n# vim's :substitute (very magic mode)\n:% s/\\v(foo)bar(\\.)@!/\\1baz/gI\n\n# rip-substitute\n(foo)bar(?!\\.)\n$1baz\n```\n\n## Installation\n**Requirements**\n- nvim 0.10+\n- [ripgrep](https://github.com/BurntSushi/ripgrep) with `pcre2` support\n\t+ `brew install ripgrep` (already includes `pcre2` by default)\n\t+ `cargo install ripgrep --features pcre2`\n- Alternatively, you can also use this plugin without `pcre2` by setting\n  `regexOptions.pcre2 = false` in the config. However, some features like\n  lookaheads are not supported then.\n- *Optional:* `:TSInstall regex` to add syntax highlighting in the popup window\n\n```lua\n-- lazy.nvim\n{\n\t\"chrisgrieser/nvim-rip-substitute\",\n\tcmd = \"RipSubstitute\",\n\topts = {},\n\tkeys = {\n\t\t{\n\t\t\t\"\u003cleader\u003efs\",\n\t\t\tfunction() require(\"rip-substitute\").sub() end,\n\t\t\tmode = { \"n\", \"x\" },\n\t\t\tdesc = \" rip substitute\",\n\t\t},\n\t},\n},\n\n-- packer\nuse {\n\t\"chrisgrieser/nvim-rip-substitute\",\n\tconfig = function() \n\t\trequire(\"rip-substitute\").setup()\n\tend,\n}\n```\n\n## Configuration\n\n```lua\n-- default settings\nrequire(\"rip-substitute\").setup {\n\tpopupWin = {\n\t\ttitle = \" rip-substitute\",\n\t\tborder = getBorder(), -- `vim.o.winborder` on nvim 0.11, otherwise \"rounded\"\n\t\tmatchCountHlGroup = \"Keyword\",\n\t\tnoMatchHlGroup = \"ErrorMsg\",\n\t\tposition = \"bottom\", ---@type \"top\"|\"bottom\"\n\t\thideSearchReplaceLabels = false,\n\t\thideKeymapHints = false,\n\t\tdisableCompletions = true, -- disables all blink.cmp completions\n\t},\n\tprefill = {\n\t\tnormal = \"cursorWord\", ---@type \"cursorWord\"|false\n\t\tvisual = \"selectionFirstLine\", ---@type \"selectionFirstLine\"|false (does not work with ex-command – see README)\n\t\tstartInReplaceLineIfPrefill = false,\n\t\talsoPrefillReplaceLine = false,\n\t},\n\tkeymaps = { -- normal mode (if not stated otherwise)\n\t\tabort = \"q\",\n\t\tconfirm = \"\u003cCR\u003e\",\n\t\tinsertModeConfirm = \"\u003cC-CR\u003e\",\n\t\tprevSubstitutionInHistory = \"\u003cUp\u003e\",\n\t\tnextSubstitutionInHistory = \"\u003cDown\u003e\",\n\t\ttoggleFixedStrings = \"\u003cC-f\u003e\", -- ripgrep's `--fixed-strings`\n\t\ttoggleIgnoreCase = \"\u003cC-c\u003e\", -- ripgrep's `--ignore-case`\n\t\topenAtRegex101 = \"R\",\n\t\tshowHelp = \"?\",\n\t},\n\tincrementalPreview = {\n\t\tmatchHlGroup = \"IncSearch\",\n\t\trangeBackdrop = {\n\t\t\tenabled = true,\n\t\t\tblend = 50, -- between 0 and 100\n\t\t},\n\t},\n\tregexOptions = {\n\t\tstartWithFixedStringsOn = false,\n\t\tstartWithIgnoreCase = false,\n\t\tpcre2 = true, -- enables lookarounds and backreferences, but slightly slower\n\t\tautoBraceSimpleCaptureGroups = true, -- disable if using named capture groups (see README for details)\n\t},\n\teditingBehavior = {\n\t\t-- Typing `()` in the `search` line, automatically adds `$n` to the `replace` line.\n\t\tautoCaptureGroups = false,\n\t},\n\tnotification = {\n\t\tonSuccess = true,\n\t\ticon = \"\",\n\t},\n\tdebug = false, -- extra notifications for debugging\n}\n```\n\n\u003e [!NOTE]\n\u003e A `ripgrep` config file set via `RIPGREP_CONFIG_PATH` is ignored by this\n\u003e plugin.\n\n## Usage\n**lua function**  \n\n```lua\nvim.keymap.set(\n\t{ \"n\", \"x\" },\n\t\"\u003cleader\u003efs\",\n\tfunction() require(\"rip-substitute\").sub() end,\n\t{ desc = \" rip substitute\" }\n)\n```\n\n- Normal mode: prefills the *escaped* word under the cursor\n- Visual mode: prefills the *escaped* selection\n- Visual *line* mode: replacements are only applied to the selected lines\n  (= the selection is used as range)\n\n**Ex-command**  \nAlternatively, you can use the ex command `:RipSubstitute`, which also\naccepts [a range\nargument](https://neovim.io/doc/user/cmdline.html#cmdline-ranges). Note that\nwhen using the ex-command, visual mode and visual line mode both pass a range.\nTo prefill the current selection, you therefore need to use the lua function.\n\n```vim\n\" Substitute in entire file. Prefills the *escaped* word under the cursor.\n:RipSubstitute\n\n\" Substitute in line range of the visual selection.\n:'\u003c,'\u003eRipSubstitute\n\n\" Substitute in given range (in this case: current line to end of file).\n:.,$ RipSubstitute\n```\n\nYou can also pass a prefill for the search value, in which case the prefill\nis *not* escaped.\n\n```vim\n:RipSubstitute prefilled_unescaped_string\n```\n\n\u003e [!TIP]\n\u003e Use `showHelp` (default keymap: `?`) to show a notification containing all\n\u003e keymaps available in the popup window.\n\n## Advanced\n**Remember prefill**  \nThe function `require(\"rip-substitute\").rememberCursorWord()` can be used to\nsave the word under the cursor for the next time `rip-substitute` is called.\n(This overrides any other prefill for that run.)\n\nOne use case for this is to set a prefill for when you intend to run substitute\nwith a range, since calling `rip-substitute` in visual line is not able to pick\nup a prefill.\n\n**Filetype**  \nThe popup window uses the filetype `rip-substitute`. This can be useful, for\ninstance, to disable auto-pairing plugins in the popup window.\n\n**`autoBraceSimpleCaptureGroups`**  \nA gotcha of `ripgrep`'s regex syntax is that it treats `$1a` as the named\ncapture group \"1a\" and *not* as the first capture group followed by the\nletter \"a.\" (See `ripgrep`'s man page on `--replace` for details.)\n\nIf `regexOptions.autoBraceSimpleCaptureGroups = true` (the default),\n`rip-substitute` automatically changes `$1a` to `${1}a`, to make writing the\nregex more intuitive. However, if you regularly use named capture groups, you\nmay want to disable this setting.\n\n**Escape character**\nIf your substitution text contains a dollar sign, for example, if you want\n`/home/user` to be replaced with `$HOME`, remember that `ripgrep` requires `$`\nas an escape sequence. In short, you'll need to set `$$HOME` as substitution text.\n\n## Limitations\n- Searching/replacing for line breaks (`\\n` or `\\r`) is not supported. [See\n  issue #28](https://github.com/chrisgrieser/nvim-rip-substitute/issues/28).\n- This plugin only searches the current buffer. To search and replace in\n  multiple files via `ripgrep`, use a plugin like\n  [grug-far.nvim](https://github.com/MagicDuck/grug-far.nvim).\n\n## About the developer\nIn my day job, I am a sociologist studying the social mechanisms underlying the\ndigital economy. For my PhD project, I investigate the governance of the app\neconomy and how software ecosystems manage the tension between innovation and\ncompatibility. If you are interested in this subject, feel free to get in touch.\n\nI also occasionally blog about vim: [Nano Tips for Vim](https://nanotipsforvim.prose.sh)\n\n- [Website](https://chris-grieser.de/)\n- [Mastodon](https://pkm.social/@pseudometa)\n- [ResearchGate](https://www.researchgate.net/profile/Christopher-Grieser)\n- [LinkedIn](https://www.linkedin.com/in/christopher-grieser-ba693b17a/)\n\n\u003ca href='https://ko-fi.com/Y8Y86SQ91' target='_blank'\u003e\u003cimg height='36'\nstyle='border:0px;height:36px;' src='https://cdn.ko-fi.com/cdn/kofi1.png?v=3'\nborder='0' alt='Buy Me a Coffee at ko-fi.com' /\u003e\u003c/a\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisgrieser%2Fnvim-rip-substitute","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchrisgrieser%2Fnvim-rip-substitute","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisgrieser%2Fnvim-rip-substitute/lists"}