Dial.nvim
enhanced increment/decrement plugin for Neovim.
Install / Use
/learn @monaqa/Dial.nvimREADME
dial.nvim
Abstract
Extended increment/decrement plugin for Neovim. Written in Lua.

Features
- Increment/decrement based on various type of rules
- n-ary (
2 <= n <= 36) integers - decimal Fractions
- date and time
- constants (an ordered set of specific strings, such as a keyword or operator)
true⇄false&&⇄||a⇄b⇄ ... ⇄z
- hex colors
- semantic version
- n-ary (
- Support
<C-a>/<C-x>/g<C-a>/g<C-x>in VISUAL mode - Flexible configuration of increment/decrement targets
- Rules that are valid only in specific FileType
- Rules that are valid only in VISUAL mode
- Support counter
- Support dot repeat (without overriding the behavior of
.)
Similar plugins
Installation
dial.nvim requires Neovim >=0.11.0.
You can install dial.nvim by following the instructions of your favorite package manager.
Usage
This plugin does not provide or override any default key-mappings. To use this plugin, you need to assign the plugin key-mapping to the key you like, as shown below:
nnoremap <C-a> <Plug>(dial-increment)
nnoremap <C-x> <Plug>(dial-decrement)
nnoremap g<C-a> <Plug>(dial-g-increment)
nnoremap g<C-x> <Plug>(dial-g-decrement)
xnoremap <C-a> <Plug>(dial-increment)
xnoremap <C-x> <Plug>(dial-decrement)
xnoremap g<C-a> <Plug>(dial-g-increment)
xnoremap g<C-x> <Plug>(dial-g-decrement)
Or you can configure it with Lua as follows:
vim.keymap.set("n", "<C-a>", function()
require("dial.map").manipulate("increment", "normal")
end)
vim.keymap.set("n", "<C-x>", function()
require("dial.map").manipulate("decrement", "normal")
end)
vim.keymap.set("n", "g<C-a>", function()
require("dial.map").manipulate("increment", "gnormal")
end)
vim.keymap.set("n", "g<C-x>", function()
require("dial.map").manipulate("decrement", "gnormal")
end)
vim.keymap.set("x", "<C-a>", function()
require("dial.map").manipulate("increment", "visual")
end)
vim.keymap.set("x", "<C-x>", function()
require("dial.map").manipulate("decrement", "visual")
end)
vim.keymap.set("x", "g<C-a>", function()
require("dial.map").manipulate("increment", "gvisual")
end)
vim.keymap.set("x", "g<C-x>", function()
require("dial.map").manipulate("decrement", "gvisual")
end)
Configuration
In this plugin, flexible increment/decrement rules can be set by using augend and group, where augend represents the target of the increment/decrement operation, and group represents a group of multiple augends.
local augend = require("dial.augend")
require("dial.config").augends:register_group{
-- default augends used when no group name is specified
default = {
augend.integer.alias.decimal, -- nonnegative decimal number (0, 1, 2, 3, ...)
augend.integer.alias.hex, -- nonnegative hex number (0x01, 0x1a1f, etc.)
augend.date.alias["%Y/%m/%d"], -- date (2022/02/19, etc.)
},
-- augends used when group with name `mygroup` is specified
mygroup = {
augend.integer.alias.decimal,
augend.constant.alias.bool, -- boolean value (true <-> false)
augend.date.alias["%m/%d/%Y"], -- date (02/19/2022, etc.)
}
}
- To define a group, use the
augends:register_groupfunction in the"dial.config"module. The arguments is a dictionary whose keys are the group names and whose values are the list of augends. - Various augends are defined
"dial.augend"by default.
To specify the group of augends, you can use expression register (:h @=) as follows:
"=mygroup<CR><C-a>
If it is tedious to specify the expression register for each operation, you can "map" it:
nmap <Leader>a "=mygroup<CR><Plug>(dial-increment)
Alternatively, you can set the same mapping without expression register:
vim.keymap.set("n", "<Leader>a", require("dial.map").inc_normal("mygroup"))
When you don't specify any group name in the way described above, the addends in the default group is used instead.
Example Configuration
local augend = require("dial.augend")
require("dial.config").augends:register_group{
default = {
augend.integer.alias.decimal,
augend.integer.alias.hex,
augend.date.alias["%Y/%m/%d"],
},
only_in_visual = {
augend.integer.alias.decimal,
augend.integer.alias.hex,
augend.date.alias["%Y/%m/%d"],
augend.constant.alias.alpha,
augend.constant.alias.Alpha,
},
}
-- Use `only_in_visual` group only in VISUAL <C-a> / <C-x>
vim.keymap.set("x", "<C-a>", function()
require("dial.map").manipulate("increment", "visual", "only_in_visual")
end)
vim.keymap.set("x", "<C-x>", function()
require("dial.map").manipulate("decrement", "visual", "only_in_visual")
end)
require("dial.config").augends:on_filetype {
typescript = {
augend.integer.alias.decimal,
augend.integer.alias.hex,
augend.constant.new{ elements = {"let", "const"} },
},
}
List of Augends
For simplicity, we define the variable augend as follows.
local augend = require("dial.augend")
integer
n-based integer (2 <= n <= 36). You can use this rule with augend.integer.new{ ...opts }.
require("dial.config").augends:register_group{
default = {
-- uppercase hex number (0x1A1A, 0xEEFE, etc.)
augend.integer.new{
radix = 16,
prefix = "0x",
natural = true,
case = "upper",
},
},
}
date
Date and time.
require("dial.config").augends:register_group{
default = {
-- date with format `yyyy/mm/dd`
augend.date.new{
pattern = "%Y/%m/%d",
default_kind = "day",
-- if true, it does not match dates which does not exist, such as 2022/05/32
only_valid = true,
-- if true, it only matches dates with word boundary
word = false,
},
},
}
In the pattern argument, you can use the following escape sequences:
|Sequence|Meaning |
|-----|------------------------------------------------------------------------------|
|%Y |4-digit year. (e.g. 2022) |
|%y |Last 2 digits of year. The upper 2 digits are interpreted as 20. (e.g. 22)|
|%m |2-digit month. (e.g. 09) |
|%d |2-digit day. (e.g. 28) |
|%H |2-digit hour, expressed in 24 hours. (e.g. 15) |
|%I |2-digit hour, expressed in 12 hours. (e.g. 03) |
|%M |2-digit minute. (e.g. 05) |
|%S |2-digit second. (e.g. 08) |
|%-y|1- or 2-digit year. (e.g. 9 represents 2009) |
|%-m|1- or 2-digit month. (e.g. 9) |
|%-d|1- or 2-digit day. (e.g. 28) |
|%-H|1- or 2-digit hour, expressed in 24 hours. (e.g. 15) |
|%-I|1- or 2-digit hour, expressed in 12 hours. (e.g. 3) |
|%-M|1- or 2-digit minute. (e.g. 5) |
|%-S|1- or 2-digit second. (e.g. 8) |
|%a |English weekdays (Sun, Mon, ..., Sat) |
|%A |English full weekdays (Sunday, Monday, ..., Saturday) |
|%b |English month names (Jan, ..., Dec) |
|%B |English month full names (January, ..., December) |
|%p |AM or PM. |
|%J |Japanese weekdays (日, 月, ..., 土) |
constant
Predefined sequence of strings. You can use this rule with augend.constant.new{ ...opts }.
require("dial.config").augends:register_group{
default = {
-- uppercase hex number (0x1A1A, 0xEEFE, etc.)
augend.constant.new{
elements = {"and", "or"},
word = true, -- if false, "sand" is incremented into "sor", "doctor" into "doctand", etc.
cyclic = true, -- "or" is incremented into "and".
},
augend.constant.new{
elements = {"&&", "||"},
word = false,
cyclic = true,
},
},
}
hexcolor
RGB color code such as #000000 and #ffffff.
require("dial.config").augends:register_group{
default = {
-- hex colors (e.g. #1A1A1A, #EEFEFE, etc.)
augend.hexcolor.new{
case = "upper", -- or "lower", "prefer_upper", "prefer_lower", see below
},
},
}
Supported options for case are:
upper: use uppercase lettersA-Flower: use lowercase lettersa-fprefer_upper: try to keep the case, use uppercase as fallback#0a1bfewill be incremented to#0b1cff(keep existing case)#0A1BFEwill be incremented to#0B1CFF(keep existing case)#059799will be incremented to#06989A(no letter, use uppercase)#0a1BFfwill be incremented to#0B1CFF(mixed casing, use uppercase)
prefer_lower: try to keep the case, use lowercase as fallback#0a1bfewill be incremented to#0b1cff(keep existing case)#0A1BFEwill be incremented to#0B1CFF(keep existing case)#059799will be incremented to#06989a(no letter, use lowercase)#0a1BFfwill be incremented to#0b1cff(mixed casing, use lowercase)
semver
Semantic versions. You can use this
