Coerce is a Neovim plugin that enables you to quickly change a keyword’s case. Coerce’s framework is also capable of any short-text, single-command text manipulation, e.g., turning selected text into its numeronym.

Install the plugin with your preferred package manager, such as Lazy:
{
"gregorias/coerce.nvim",
tag = 'v5.0.0',
config = function()
-- Example configuration. For more options, see the configuration section.
require("coerce").setup()
vim.keymap.set("n", "cr", "<Plug>(coerce-normal)", { desc = "Coerce word" })
vim.keymap.set("n", "gcr", "<Plug>(coerce-motion)", { desc = "Coerce motion" })
vim.keymap.set("x", "gcr", "<Plug>(coerce-visual)", { desc = "Coerce visual" })
end,
}
[!TIP] I recommend using
event = 'VeryLazy'for setup. This way, Coerce’s setup does not happen during the initial render.
This plugin effectively replaces Abolish’s coercion functionality. If you wish to keep it for its other features, you can disable the coercion feature like so:
{
"tpope/vim-abolish",
init = function()
-- Disable coercion mappings. I use coerce.nvim for that.
vim.g.abolish_no_mappings = true
end,
}
You can use Coerce to coerce words into various cases using modes.
crX, where X stands for your desired case.
Which key, if present, will show you hints.| Case | Key |
|---|---|
| camelCase | c |
| dot.case | d |
| kebab-case | k |
| n12e | n |
| PascalCase | p |
| snake_case | s |
| UPPER_CASE | u |
| path/case | / |
| space case | <space> |
| Vim mode | Selector | Transformer |
|---|---|---|
| Normal | current word | LSP rename/local |
| Normal | motion selection | local |
| Visual | visual selection | local |
[!TIP] I recommend
cras keymap for the normal mode. For the visual mode, I recommendgcrand notcrin order to avoid a conflict with the defaultc.
The plugin exposes the built-in modes as the following keymaps:
<Plug>(coerce-normal)<Plug>(coerce-motion)<Plug>(coerce-visual)You may coerce a keyword in such a way that it stops being keyword, e.g., you
use the path case in most programming languages.
In that case, just running cr again won’t fully revert the case.
You’ll need to visually select the word to fix it.
To quickly select a changed keyword, you can configure a special keymap for doing that. For example, here’s how I have it set up:
require"which-key".register({
g = {
p = {
-- "p" makes sense, gv selects the last Visual selection, so this one
-- selects the last pasted text.
function()
vim.api.nvim_feedkeys("`[" .. vim.fn.strpart(vim.fn.getregtype(), 0, 1) .. "`]", "n", false)
end,
"Switch to VISUAL using last paste/change",
},
},
})
With that, I can use gp to select whatever I have just coerced.
I recommend the following config:
require"coerce".setup{}
local wke = require("coerce.keymaps").which_key_expand
require("which-key").add({
{ "cr", group = "+Coerce word", expand = wke.normal_mode, mode = "n" },
{ "gcr", group = "+Coerce motion", expand = wke.motion_mode, mode = "n" },
{ "gcr", group = "+Coerce visual", expand = wke.visual_mode, mode = "x" },
})
but you may also use vim.keymap instead of Which Key:
vim.keymap.set("n", "cr", "<Plug>(coerce-normal)", { desc = "Coerce word" })
vim.keymap.set("n", "gcr", "<Plug>(coerce-motion)", { desc = "Coerce motion" })
vim.keymap.set("x", "gcr", "<Plug>(coerce-visual)", { desc = "Coerce visual" })
The default configuration looks like so:
require"coerce".setup{
-- If you don’t like the default cases, you can override this.
cases = require"coerce".default_cases,
}
You may freely modify the config parameters to your liking.
You can register a new case after setup like so:
require"coerce".register_case{
keymap = "l",
case = function(str)
return vim.fn.tolower(str)
end,
description = "lowercase",
}
You can register a new mode like so:
---@type coerce.Mode
local foo_mode = {
selector = function(cb)
local s, e = -- Your function that finds start and end points.
-- For example, returning {0, 0}, {0, 5} selects the first 6
-- characters of the current buffer.
local region_m = require"coerce.region"
cb(region_m(region_m.modes.INLINE, s, e))
end,
transformer = require"coerce.transformer".transform_local,
}
vim.keymap.set("v", "gc", function() require"coerce.keymaps".action(foo_mode) end, { desc = "Coerce with foo mode" })
If you don’t like that the default normal mode binding uses LSP rename (e.g., because it’s too slow), you can provide your own implementation like so:
require"coerce".setup{
-- …
}
-- Register a custom `cr` binding that uses the local-only transformation.
vim.keymap.set("n", "cr", function ()
require"coerce.keymaps".action{
selector = require"coerce.selector".select_current_word,
transformer = require"coerce.transformer".transform_local,
}
end)
Text-case is more feature-rich than Coerce, but if you just need to change case of the current keyword, Coerce is simpler.
| Feature | Coerce | Text-case | Abolish |
|---|---|---|---|
| Full Unicode support | ✅ | ❌ | ❌ |
| Which Key integration | ✅ | ✅ | ❌ |
| nvim-notify integration | ✅ | ❌ | ❌ |
| Current keyword coerce | ✅ | ❌ | ✅ |
| Visual selection | ✅ | ✅ | ❌ |
| Motion selection | ✅ | ✅ | ❌ |
| LSP rename | ✅ | ✅ | ❌ |
| Kebab case | ✅ | ✅ | ✅ |
| Numeronym “case” | ✅ | ❌ | ❌ |
| Dot repeat support | ✅ | ✅ | ✅ |
| Custom case support | ✅ | ❌ | ❌ |
| Custom mode support | ✅ | ❌ | ❌ |
This plugin was inspired by Abolish’s coercion feature. I created this plugin to address Abolish’s shortcomings, which are:
I used Text-case’s source code to inform myself on how to do things in Neovim.
The logo is based on a fist SVG from SVG Repo.