• Stars
    star
    387
  • Rank 108,574 (Top 3 %)
  • Language
    Lua
  • License
    Do What The F*ck ...
  • Created over 2 years ago
  • Updated 7 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Neovim plugin introducing a new operators motions to quickly replace and exchange text.

🪓 substitute.nvim

Lua GitHub Workflow Status

substitute.nvim aim is to provide new operator motions to make it very easy to perform quick substitutions and exchange.

If you are familiar with svermeulen/vim-subversive and tommcdo/vim-exchange, this plugin does almost the same but rewritten in lua (and I hope this will be more maintainable, readable and efficient).

✨ Features

See this plugin in action

⚡️ Requirements

  • Neovim >= 0.8.0

(Neovim 0.6.0 compat)

📦 Installation

Install the plugin with your preferred package manager:

packer

-- Lua
use({
  "gbprod/substitute.nvim",
  config = function()
    require("substitute").setup({
      -- your configuration comes here
      -- or leave it empty to use the default settings
      -- refer to the configuration section below
    })
  end
})

vim-plug

" Vim Script
Plug 'gbprod/substitute.nvim'
lua << EOF
  require("substitute").setup({
    -- your configuration comes here
    -- or leave it empty to use the default settings
    -- refer to the configuration section below
  })
EOF

⚙️ Configuration

Substitute comes with the following defaults:

{
  on_substitute = nil,
  yank_substituted_text = false,
  highlight_substituted_text = {
    enabled = true,
    timer = 500,
  },
  range = {
    prefix = "s",
    prompt_current_text = false,
    confirm = false,
    complete_word = false,
    motion1 = false,
    motion2 = false,
    suffix = "",
  },
  exchange = {
    motion = false,
    use_esc_to_cancel = true,
  },
}

More details on these options is available in the sections below corresponding to the different features.

🔂 Substitute operator

It contains no default mappings and will have no effect until you add your own maps to it.

-- Lua
vim.keymap.set("n", "s", require('substitute').operator, { noremap = true })
vim.keymap.set("n", "ss", require('substitute').line, { noremap = true })
vim.keymap.set("n", "S", require('substitute').eol, { noremap = true })
vim.keymap.set("x", "s", require('substitute').visual, { noremap = true })

Then you can then execute s<motion> to substitute the text object provided by the motion with the contents of the default register (or an explicit register if provided). For example, you could execute siw to replace the current word under the cursor with the current yank, or sip to replace the paragraph, etc.

This action is dot-repeatable.

Note: in this case you will be shadowing the change character key s so you will have to use the longer form cl.

Each functions (operator, line, eol and visual) are configurable:

lua require('substitute').operator({
  count = 1,      -- number of substitutions
  register = "a", -- register used for substitution
  motion = "iw",  -- only available for `operator`, this will automatically use
                  -- this operator for substitution instead of asking for.
})

⚙️ Configuration

on_substitute

Default : nil

Function that will be called each times a substitution is made. This function takes a param argument that contains the register used for substitution.

yank_substituted_text

Default : false

If true, when performing a substitution, substitued text is pushed into the default register.

highlight_substituted_text.enabled

Default : true

If true will temporary highlight substitued text.

highlight_substituted_text.timer

Default : 500

Define the duration of highlight.

🤝 Integration

gbprod/yanky.nvim

To enable gbprod/yanky.nvim swap when performing a substitution, you can add this to your setup:

require("substitute").setup({
  on_substitute = require("yanky.integration").substitute(),
})
svermeulen/vim-yoink

To enable vim-yoink swap when performing a substitution, you can add this to your setup:

require("substitute").setup({
  on_substitute = function(_)
    vim.cmd("call yoink#startUndoRepeatSwap()")
  end,
})

vim-yoink does not support swapping when doing paste in visual mode. With this plugin, you can add thoss mappings to enable it :

vim.keymap.set("x", "p", require('substitute').visual, { noremap = true })
vim.keymap.set("x", "P", require('substitute').visual, { noremap = true })

🔁 Substitute over range motion

Another operator provided allows specifying both the text to replace and the line range over which to apply the change by using multiple consecutive motions.

vim.keymap.set("n", "<leader>s", require('substitute.range').operator, { noremap = true })
vim.keymap.set("x", "<leader>s", require('substitute.range').visual, { noremap = true })
vim.keymap.set("n", "<leader>ss", require('substitute.range').word, { noremap = true })

After adding this map, if you execute <leader>s<motion1><motion2> then the command line will be filled with a substitute command that allow to replace the text given by motion1 by the text will enter in the command line for each line provided by motion2.

Alternatively, we can also select motion1 in visual mode and then hit <leader>s<motion2> for the same effect.

For convenience, <leader>ss<motion2> can be used to select complete word under the cursor as motion1 (complete word means that complete_word options is override to true so is different from siwip which will not require that there be word boundaries on each match).

You can select the default replacement value by selecting a register. Eg: "a<leader>s<motion1><motion2> will use the content of a register as replacement value.

You can override any default configuration (described later) by passing this to the operator function. By example, this will use S as prefix of the substitution command (and use tpope/vim-abolish):

vim.keymap.set("n", "<leader>S", function ()
    require('substitute.range').operator({ prefix = 'S' })
end, { noremap = true })

⚙️ Configuration

range.prefix

Default : s

Substitution command that will be used (set it to S to use tpope/vim-abolish substitution by default).

range.suffix

Default : ""

Suffix added at the end of the substitute command. For example, it can be used to not save substitution history calls by adding | call histdel(':', -1).

range.prompt_current_text

Default : false

Substitution command replace part will be set to the current text. Eg. instead of s/pattern//g you will have s/pattern/pattern/g.

range.confirm

Default : false

Will ask for confirmation for each substitutions.

range.complete_word

Default : false

Will require that there be word boundaries on each match (eg: \<word\> instead of word).

range.group_substituted_text

Default : false

This will capture substituted text as you can use \1 to quickly reuse it.

range.subject

Default : nil

This allows you to control how the subject (to be replaced) is resolved. It accepts either a function, string, or a table with some special keys.

If it is a string that will be used directly. If it is a function it will be called when the operator is used, and should return the subject to be replaced. If it is a table you may provide one of the following keys with appropriate values:

  • register = "a" Use the contents of this register as the subject.
  • expand = "<cword>" Use the string given as the argument to vim.fn.expand() to get the subject.
  • last_search = true Shortcut for register = "/" to use the last / search.
  • motion = "iw" Use this motion at the current cursor to get the subject

eg. lua require('substitute.range').operator({ subject = {motion = 'iW'} }) will select inner WORD as subject of substitution.

range.range

Default : nil

This allows you to control the range of the substitution. This takes either a function, string, or a table with some special keys. If it is a string that will be used directly. If it is a function it will be called after the subject is resolved and should return a string. If it is a table you may provide one of the following keys with appropriate values:

  • motion = "ap" Use this motion from the current cursor to get the range.

eg. specifying range = '%' will make the substitution run over the whole file. See :h [range] for all the possible values here.

eg. lua require('substitute.range').operator({ range = { motion = 'ap' } }) will select around paragraph as range of substitution.

You can combine subject and range : lua require('substitute.range').operator({ subject = { motion='iw' }, range = { motion = 'ap' } }) will prepare substitution for inner word around paragraph.

range.motion1 DEPRECATED

Default : false

This is option deprecated and equivalent to providing subject.motion.

range.motion2 DEPRECATED

Default : false

This option is deprecated and equivalent to range.motion

range.register

Default : nil

This will use the content of this register as replacement value.

eg. lua require('substitute.range').operator({ register = 'a' }) will use "a register content as replacement.

🤝 Integration

tpope/vim-abolish

You can use tpope/vim-abolish substitution by default.

require("substitute").setup({
  range = {
    prefix = "S",
  }
})

🔀 Exchange operator

This operator allows to quickly exchange text inside a buffer.

Eg. To exchange two words, place your cursor on the first word and type sxiw. Then move to the second word and type sxiw again.

Note: the {motion} used in the first and second use of sx don't have to be the same. Note 2: this is dot-repeatable, so you can use . instead of sxiw for the second word.

You can select a whole line using the line function (sxx in the example below).

Because this operator has to be invoked twice to change the document, if you change your mind after invoking the operator once, you can cancel you selection using <Esc> key or the cancel function (mapped to sxc in the example below).

vim.keymap.set("n", "sx", require('substitute.exchange').operator, { noremap = true })
vim.keymap.set("n", "sxx", require('substitute.exchange').line, { noremap = true })
vim.keymap.set("x", "X", require('substitute.exchange').visual, { noremap = true })
vim.keymap.set("n", "sxc", require('substitute.exchange').cancel, { noremap = true })

⚙️ Configuration

exchange.motion

Default : nil

This will use this motion for exchange.

eg. lua require('substitute.exchange').operator({ motion = 'ap' }) will select around paragraph as range of exchange.

exchange.use_esc_to_cancel

Default : true

If true, you can use the <Esc> key to cancel exchange selection. If set to false, consider map the cancel function:

vim.keymap.set("n", "sxc", require('substitute.exchange').cancel, { noremap = true })

🎨 Colors

Description Group Default
Selected range for range substitution SubstituteRange link to Search
Selected text for exchange SubstituteExchange link to Search

🎉 Credits

This plugin is a lua version of svermeulen/vim-subversive and tommcdo/vim-exchange awesome plugins.

Thanks to m00qek lua plugin template.

More Repositories

1

yanky.nvim

Improved Yank and Put functionalities for Neovim
Lua
603
star
2

cutlass.nvim

Plugin that adds a 'cut' operation separate from 'delete'
Lua
148
star
3

nord.nvim

An arctic, north-bluish clean and elegant Neovim theme.
Lua
109
star
4

stay-in-place.nvim

Neovim plugin that prevent cursor from moving when using shift and filter actions.
Lua
75
star
5

tree-sitter-gitcommit

A tree-sitter grammar for gitcommit messages
C
45
star
6

phpactor.nvim

Lua version of the Phpactor vim plugin to take advantage of the latest Neovim features
Lua
41
star
7

tree-sitter-twig

Twig grammar for Tree-sitter
C
31
star
8

uuid-normalizer

Normalizer to serialize Ramsey Uuid with Symfony Serializer
PHP
31
star
9

conf-makefile

PHP
11
star
10

gbvim

my neovim setup
Lua
9
star
11

sass-trigonometry

Sass trigonometry and inverse trigonometry functions
SCSS
7
star
12

open-related.nvim

A Neovim plugin to quickly open related files
Lua
7
star
13

doctrine-specification-bundle

This bundle provides integration with doctrine-specification with Symfony
PHP
6
star
14

doctrine-specification

This library allow you to write Doctrine ORM queries using the Specification pattern
PHP
6
star
15

specification

Yet another specification pattern implementation in PHP
PHP
5
star
16

php-enhanced-treesitter.nvim

Improve default php treesitter grammar using grammar injections
Scheme
4
star
17

elastica-bundle

Really simple bundle to use Elastica within Symfony applications
PHP
4
star
18

icantdecide

JavaScript
4
star
19

domain-event

Library to manage domain events in a DDD application
PHP
2
star
20

stars

2
star
21

vimium-nord-theme

Nord theme for vimium
CSS
2
star
22

domain-event-bundle

Integrates domain event library to a Symfony app
PHP
1
star
23

montmartre

PHP
1
star
24

elastica-provider-bundle

Bundle that can easely provide data in ElasticSearch using Elastica
PHP
1
star
25

elasticsearch-dataprovider-bundle

[deprecated] Bundle that allow easy data providing elasticsearch indices in Symfony
PHP
1
star
26

elastica-specification

This library allows to write Elastica queries using the Specification pattern
PHP
1
star
27

cours

1
star
28

gbprod.github.io

http://gb-prod.fr
HTML
1
star
29

algolia-specification

This library allows to write Algolia queries using the Specification pattern
PHP
1
star
30

elasticsearch-extra-bundle

[deprecated] Extra tools for m6web/elasticsearch-bundle
PHP
1
star
31

elastica-specification-bundle

This bundle integrates elastica-specification with Symfony
PHP
1
star
32

webhook-commander

Allows to trigger commands on github webhook calls
PHP
1
star
33

elastica-extra-bundle

Extra tools for index management with Elastica client
PHP
1
star
34

none-ls-luacheck.nvim

Luacheck diagnostics plugin for none-ls.nvim
Lua
1
star
35

none-ls-psalm.nvim

Psalm diagnostics plugin for none-ls.nvim
Lua
1
star