Demicolon lets you repeat all ]/[-prefixed motions with ; (repeat forward). For example ]q, ]l, ]s and ]], as well as motions defined by nvim-treesitter-textobjects.
See Usage and Configuration for more information.
Note
Because of Demicolon's 2.0 reimplementation that now automagically lets you repeat all ] and [ prefixed motions, demicolon no longer creates plugin-specific (or diagnostics) keymaps. If you depended on Demicolon's default plugin keymaps, here is what's been removed:
Click to expand
-- Diagnostics
local function diagnostic_jump(count, severity)
return function()
vim.diagnostic.jump({ count = count, severity = severity })
end
end
local map, nxo = vim.keymap.set, { 'n', 'x', 'o' }
local severity = vim.diagnostic.severity
local error, warn, info, hint = severity.ERROR, severity.WARN, severity.INFO, severity.HINT
map(nxo, ']e', diagnostic_jump(1, error), { desc = 'Next error' })
map(nxo, '[e', diagnostic_jump(-1, error), { desc = 'Previous error' })
map(nxo, ']w', diagnostic_jump(1, warn), { desc = 'Next warning' })
map(nxo, '[w', diagnostic_jump(-1, warn), { desc = 'Previous warning' })
map(nxo, ']i', diagnostic_jump(1, info), { desc = 'Next info' })
map(nxo, '[i', diagnostic_jump(1, info), { desc = 'Previous info' })
map(nxo, ']h', diagnostic_jump(1, hint), { desc = 'Next hint' })
map(nxo, '[h', diagnostic_jump(-1, hint), { desc = 'Previous hint' })
-- Gitsigns (from its README):
local map = vim.keymap.set
map('n', ']c', function()
if vim.wo.diff then
vim.cmd.normal({ ']c', bang = true })
else
gitsigns.nav_hunk('next')
end
end)
map('n', '[c', function()
if vim.wo.diff then
vim.cmd.normal({ '[c', bang = true })
else
gitsigns.nav_hunk('prev')
end
end)
-- Neotest
local map, nxo = vim.keymap.set, { 'n', 'x', 'o' }
local function neotest_jump(direction, status)
return function()
require('neotest').jump[direction]({ status = status })
end
end
map(nxo, ']t', neotest_jump('next'), { desc = 'Next test' })
map(nxo, '[t', neotest_jump('prev'), { desc = 'Previous test' })
map(nxo, ']T', neotest_jump('next', 'failed'), { desc = 'Next failed test' })
map(nxo, '[T', neotest_jump('prev', 'failed'), { desc = 'Previous failed test' })demicolon.mp4
With lazy.nvim:
{
'mawkler/demicolon.nvim',
dependencies = {
'nvim-treesitter/nvim-treesitter',
'nvim-treesitter/nvim-treesitter-textobjects',
},
opts = {}
}After pressing a ]/[-prefixed key, for example ]q, Demicolon lets you repeat its motion with ; and ,.
Of course, Demicolon also lets you repeat t/T/f/F with ;/,. See :help t, :help T, :help f, and :help F respectively for more information.
Below are some examples of motions, both built-in and provided by plugins.
| Motion | Jumps to next/pevious... | Help page with more information |
|---|---|---|
]d/[d |
Next/previous diagnostic (any severity) | :help ]d/:help [d |
]q/[q |
Item in quickfix list | :help ]q/:help [q |
]l/[l |
Item in location list | :help ]l/:help [l |
]<C-q>/[<C-q> |
File in quickfix list | :help ]CTRL-Q/:help [CTRL-Q |
]<C-l>/[<C-l> |
File in location list | :help ]CTRL-L/:help [CTRL-L |
]z/[z |
Fold | :help z]/:help z[ |
]s/[s |
Spelling mistake | :help ]s/:help [s |
For a list of more native motions see :help ]
Nvim-treesitter-textobject motions
Demicolon lets you repeat any nvim-treesitter-textobjects motion. For example: ]f to "jump to next function", ]c to "jump to next class", etc.
Note
To use treesitter text-objects you need to configure textobjects.move in nvim-treesitter-textobjects.
Gitsigns motions
| Motion | Jumps to next/pevious... | Help page with more information |
|---|---|---|
]c/[c |
Git hunk | :help gitsigns.nav_hunk() |
Neotest motions
| Motion | Jumps to next/pevious... | Help page with more information |
|---|---|---|
]t/[t |
Test | :help neotest.jump |
]T/[T |
Failed test | :help neotest.jump |
VimTeX motions
Note that these mappings are only created in normal mode and visual mode. For some reason they don't work when created for operator-pending mode.
| Motion | Jumps to next/pevious... | Help page with more information |
|---|---|---|
]]/[[ |
Section start | :help vimtex-motions |
][/[] |
Section end | :help vimtex-motions |
]r/[r |
Frame start | :help vimtex-motions |
]R/[R |
Frame end | :help vimtex-motions |
]n/[n |
Math start | :help vimtex-motions |
]N/[N |
Math end | :help vimtex-motions |
]//[/ |
Comment start | :help vimtex-motions |
]*/[* |
Comment end | :help vimtex-motions |
]m/[m |
Environment start | :help vimtex-motions |
]M/[M |
Environment end | :help vimtex-motions |
Default options:
opts = {
-- Create default keymaps
keymaps = {
-- Create t/T/f/F key mappings
horizontal_motions = true,
-- Create ; and , key mappings. Set it to 'stateless', 'stateful', or false to
-- not create any mappings. 'stateless' means that ;/, move right/left.
-- 'stateful' means that ;/, will remember the direction of the original
-- jump, and `,` inverts that direction (Neovim's default behaviour).
repeat_motions = 'stateless',
-- Keys that shouldn't be repeatable (because aren't motions), excluding the prefix `]`/`[`
-- If you have custom motions that use one of these, make sure to remove that key from here
disabled_keys = { 'p', 'I', 'A', 'f', 'i' },
},
}If you’d rather use keymaps other than ; and ,, for example n and N, you can disable Demicolon’s default repeat mappings and create your own. Here's an example:
Click here to see the code
require('demicolon').setup({
keymaps = {
repeat_motions = false, -- Don't create ; and , keymaps
},
})
local map, nxo = vim.keymap.set, { 'n', 'x', 'o' }
-- Stateless: always forward/backward
map(nxo, 'n', require('demicolon.repeat_jump').forward)
map(nxo, 'N', require('demicolon.repeat_jump').backward)
-- Or, stateful (remember the original motion’s direction)
-- map(nxo, 'n', require('demicolon.repeat_jump').next)
-- map(nxo, 'N', require('demicolon.repeat_jump').prev)If you have custom motions that don't start with ]/[ that you want to make repetable (for example for flash.nvim) you can create your own custom repeatable jumps using repeatably_do() in demicolon.jump. repeatably_do() takes a funcion as its first argument and options to be passed to that function as its second argument. Make sure that the options include a boolean forward field to determine whether the action should be forward or backward. Take a look at how I've implemented the neotest integration for inspiration.
eyeliner.nvim can highlight unique letters in words when you press t/T/f/F. Below is my recommended configuration for using eyeliner.nvim together with Demicolon.
NOTE: make sure to set keymaps.horizontal_motions = false in your demicolon setup if you want to use this config.
return {
'jinh0/eyeliner.nvim',
-- keys = { 't', 'f', 'T', 'F' }, -- Uncomment this to lazy load eyeliner.nvim
config = function()
require('eyeliner').setup({
highlight_on_key = true,
default_keymaps = false,
dim = true, -- Optional
})
local function eyeliner_jump(key)
local forward = vim.list_contains({ 't', 'f' }, key)
return function()
require('eyeliner').highlight({ forward = forward })
return require('demicolon.jump').horizontal_jump(key)()
end
end
local nxo = { 'n', 'x', 'o' }
local opts = { expr = true }
vim.keymap.set(nxo, 'f', eyeliner_jump('f'), opts)
vim.keymap.set(nxo, 'F', eyeliner_jump('F'), opts)
vim.keymap.set(nxo, 't', eyeliner_jump('t'), opts)
vim.keymap.set(nxo, 'T', eyeliner_jump('T'), opts)
end,
}Here is a full configuration, including nvim-treesitter-textobjects and eyeliner.nvim:
Click here to see the code
require('lazy').setup({
{
'nvim-treesitter/nvim-treesitter-textobjects',
dependencies = 'nvim-treesitter/nvim-treesitter',
build = ':TSUpdate',
config = function()
require('nvim-treesitter.configs').setup({
ensure_installed = 'all',
textobjects = {
move = {
enable = true,
goto_next_start = {
[']f'] = '@function.outer',
[']a'] = '@argument.outer',
[']m'] = '@method.outer',
-- ...
},
goto_previous_start = {
['[f'] = '@function.outer',
['[a'] = '@argument.outer',
['[m'] = '@method.outer',
-- ...
},
},
},
})
end,
},
{
'jinh0/eyeliner.nvim',
keys = { 't', 'f', 'T', 'F' },
config = function()
require('eyeliner').setup({
highlight_on_key = true,
dim = true,
default_keymaps = false,
})
local function eyeliner_jump(key)
local forward = vim.list_contains({ 't', 'f' }, key)
return function()
require('eyeliner').highlight({ forward = forward })
return require('demicolon.jump').horizontal_jump(key)()
end
end
local map, nxo, opts = vim.keymap.set, { 'n', 'x', 'o' }, { expr = true }
map(nxo, 'f', eyeliner_jump('f'), opts)
map(nxo, 'F', eyeliner_jump('F'), opts)
map(nxo, 't', eyeliner_jump('t'), opts)
map(nxo, 'T', eyeliner_jump('T'), opts)
end,
},
{
'mawkler/demicolon.nvim',
dependencies = {
'nvim-treesitter/nvim-treesitter',
'nvim-treesitter/nvim-treesitter-textobjects',
},
opts = {
keymaps = {
horizontal_motions = false,
-- `f` is removed from this table because we have mapped it to
-- `@function.outer` with nvim-treesitter-textobjects
disabled_keys = { 'p', 'I', 'A', 'i' },
},
},
},
})Here's the full list of available treesitter textobjects.
nvim-treesitter-textobjects is used at the core of Demicolon's repeat logic. Credit to them for making an awesome plugin!