Your config says how you mean to edit. fingers.nvim measures how you actually do — in one buffer, when you ask.
14 against 3 is a sum anyone can check. That is the only kind of thing this plugin will say to you.
:Fingers.
Everything is observed live and nothing interrupts you: no notification, no virtual text, no flash at the moment you press the key. Being corrected mid-edit is how a coach becomes a thing you disable. It waits in the fingers buffer, which q closes.
fingers.nvim how you actually edit this week, one thing j×14 in a row ×9 this week try: {count}j, } , or <C-d> what your editing looked like j×14 in a row ×31 this wk 9 · last 22 ↘ moving down a line at a time try: {count}j, } , or <C-d> f{char} retyped for the same target ×8 repeating a find by spelling it out again try: ; repeats the last f/t in one key (and , goes back) <BS>×11 in insert mode ×6 erasing a character at a time try: <Esc>, then db or daw — or <C-w> without leaving insert drilled, then 2 of 5 reached your editing visual-block drilled 9d ago ×9 since macro drilled 9d ago ×2 since increment drilled 1d ago not once since global drilled 9d ago not watched — typed on the command line, which fingers never reads phrases you keep typing a mapping nobody wrote yet A;j ×23 a 2-key mapping would have saved 23 keys techniques in the wild . ×48 dot repeat ciw ×12 change inner word never sighted · 23 @@ ; , caw diw daw dap yiw ci( ci" …+13 the mouse clicks ×132 · scroll ×460 this wk 49 · last 126 ↘ your mappings 4 of 6 pressed <leader>ff 214 2026-07-26 find files <leader>gs 89 2026-07-27 git status <leader>ca 41 2026-07-26 code action · 1.6s pause before it <leader>rn 12 2026-07-18 rename never fired · 2 <leader>gd git diff · 3 of 5 census configs bind this <leader>xx diagnostics list · nobody on the census binds it either beyond the leader — plugins & remaps 0 of 1 pressed never fired · 1 ys add surround · nobody on the census binds it either census: 5 configs, fetched 2026-07-27 — refresh with :Fingers census counted locally; nothing has left this machine
Made-up mappings; the numbers are only ever your own. In
your editor the buffer takes its colors from your colorscheme — it links to
Title, String, Identifier,
Number, DiagnosticWarn and Comment, all
overridable.
Everything else you already knew — except perhaps the pause. A mapping you stop in front of for 1.6 seconds every time has a recall cost its fire-count can't see.
Mappings are read from the editor via nvim_get_keymap,
never by parsing your config. Everything typable counts — a plugin's
ys as much as your <leader>gs. Neovim's own
defaults are excluded.
. replays the change,
not your keystrokes. Press <leader>x then .
and the count is 1: your fingers reached the mapping once.<leader>x expands to ciwfoo<Esc>, that
ciw is not a technique you typed — one decision, already
counted.<leader>g and <leader>gs bound, typing
<leader>gs credits only <leader>gs — the
same rule vim itself uses via timeoutlen.No account. No server. Nothing automatic touches the network and nothing
is ever sent. Two calls exist — :Fingers census and
:Fingers practice — both explicit, both a GET of a public page,
both download-only. :checkhealth fingers states it as a check
rather than a promise.
A single JSON file, and nowhere else. Read it, diff it, delete it.
:lua= require("fingers.store").path() -- stdpath("data")/fingers/counts.json
Plus short normal-mode command phrases. Never insert-mode text, the files you opened, or anything about your projects.
Delete the file and your history is erased entirely; the plugin starts
over without complaint. :Fingers reset does the same from inside
the editor.
Insert is where text lives, so its gate admits <BS>,
arrows and control bytes — never a printable character. A backspace run
can be counted without your prose ever being readable. The contents of a
: or / line are never recorded.
It reads the key stream via vim.on_key. Your
mappings are never wrapped or re-registered, so nothing here can change what
they do, or break an expr or <Plug> mapping.
“You pressed j fourteen times to move fourteen lines” is a
fact, and 14 keys against 3 is a sum anyone can check.
“You use w more than f” is taste, it's usually
wrong, and it will never appear here — both are correct for different
jobs.
There is no score, no streak, and no efficiency rating. A shorter way to do the thing you just did is coaching; a percentage is a game, and this isn't one.
Runs of a single key past run_threshold (five by default),
where the alternative is named rather than gestured at:
Plus runs of ⌫ and arrows inside
insert mode (through the control-keys-only gate); retyping
f{char} for the target ; already remembers; and
selecting before operating — v, one motion, then
d/y/c, one key more than the
operator and motion for the identical edit.
Some things are counted but never coached, because no honest
arithmetic applies: mouse clicks and scrolling, and sightings of native
techniques like ciw and ..
The phrases section points the rule at absence. A phrase you keep typing is worth (keys − 2) × count — what a two-key mapping nobody wrote would have saved.
-- lazy.nvim { "vim-pro/fingers.nvim" } -- vim.pack vim.pack.add({ "https://github.com/vim-pro/fingers.nvim" })
| command | what it does |
|---|---|
:Fingers | the fingers buffer (q closes it) |
:Fingers all | the same, with the never-fired cap lifted |
:Fingers export | the report as a markdown buffer, for sharing |
:Fingers census | fetch the vim.pro mapping census — a public aggregate |
:Fingers practice | fetch your vim.pro practice record, to see what transferred |
:Fingers live | per-session whispers after a finding; same command turns them off |
:Fingers rescan | re-read your mappings after a lazy-loaded plugin lands |
:Fingers reset | forget every count and finding, start over |
:Fingers flush | write to disk now (otherwise: on exit) |
scripts/fingers | the report in a terminal, from the counts file alone |
:checkhealth fingers | why it might be counting nothing — usually a lazy-loaded config |
Lowercase :fingers works too — a command-line
abbreviation, since Neovim reserves lowercase command names. Set
abbrev = false to leave the command line alone.
require("fingers").setup({ modes = { "n", "x" }, -- modes counted; insert passes only control keys census = "all", -- count every typable mapping; "leader" narrows it coach = true, -- false = mapping counts only, no live observer techniques = true, -- tally native idioms (ciw, ., *, ``…) seen in the wild mine = true, -- mine recurring phrases: the mapping you don't have yet run_threshold = 5, -- repeats of one key before it's worth mentioning weekly_note = false, -- opt-in: one notify line, at most weekly site = "https://vim.pro", -- where census and practice read from login = "", -- your vim.pro login, for :Fingers practice abbrev = true, -- let `:fingers` expand to `:Fingers` enabled = true, -- false loads the plugin without counting }) require("fingers").stats() -- the numbers, for your own statusline
Counting begins at VimEnter and rescans once on the first
idle moment, which covers most lazy-loading. For the rest,
:Fingers rescan.
Counts are held in memory and written on exit — atomically and merged,
so two open editors don't clobber each other. A crash costs the current
session rather than corrupting the file. :Fingers flush
writes immediately.
An LSP on_attach or filetype-plugin mapping lives in a
buffer's own table, which the census doesn't read. A mapping that
overrides a Neovim default (a rebound gcc) is
excluded with the defaults — it still counts, it just doesn't render.
The plugin is useful without it — that's the design, not a staging post. The site knows things an editor cannot, and that asymmetry is the only reason to connect.
:Fingers census fetches the public mapping aggregate (one
GET, nothing sent) and your never-fired list gains its missing fact:
“3 of 5 census configs bind this” — or “nobody on the census binds it
either.” Delete or learn; the verdict stays yours.
A drill teaches you a technique; whether it stuck is a separate
question. :Fingers practice reads which techniques you
drilled and what each looks like as keys, then watches for them in real
work. The practice record comes from the site; the keys are counted
here.
Your config's changelog lives on vim.pro, so a mapping can be dated. “Added fourteen months ago, never fired” and “added Tuesday” are different advice.
vim.pro hands down what it knows, the plugin computes everything locally, and only aggregates you explicitly agree to are ever uploaded — never keys, never phrases, never text.