πŸšͺ tobira.nvim

August 7, 2026 Β· View on GitHub

πŸšͺ tobira.nvim

Learn the next Vim command from your own editing habits β€” not a cheat sheet.

tobira watches how you actually edit, and when it spots a pattern you could do better, it quietly shows you the one command that would have helped. No quizzes. No interruptions.

CI License: MIT Stars Dotfyle

Features β€’ Installation β€’ Usage β€’ Configuration β€’ vs hardtime.nvim

tobira detects a repeated f-search and suggests ;

✨ Features

  • πŸ‘€ Fully passive β€” reads keystrokes via vim.on_key(); no config required, zero impact on your existing mappings
  • 🎯 71 detected patterns across motion, edit, search, window, fold, mark, macro, diff, ex, and terminal β€” repeated f searches, dwβ†’i instead of cw, retyping the same :s/// substitution instead of &/g&, and more (see examples below)
  • ⌨️ Ex commands, too β€” :g, :norm, :s, and friends are tracked from the command line itself, not just normal-mode keys, so :g / :norm can be suggested to users who have never tried them (tobira's own :Tobira* commands are excluded, so checking your stats never counts as usage)
  • πŸ’¬ One suggestion at a time β€” shown after a natural pause, capped per session, with a cooldown between auto-suggestions β€” never a popup quiz
  • πŸ“ˆ Mastery-aware β€” once you've used a command ~100 times, tobira stops suggesting it and moves on
  • πŸͺœ Level-aware β€” beginner commands surface first, advanced ones once you're ready
  • πŸ—ΊοΈ 186 commands in the learning graph, covering the full Neovim command surface

⚑️ Requirements

  • Neovim 0.9+
  • nvim-notify (optional β€” if installed, tobira's suggestion window matches its color scheme)

πŸ“¦ Installation

lazy.nvim

{
  "kamegoro/tobira.nvim",
  event = "VeryLazy",
  opts = {},
}

packer.nvim

use {
  "kamegoro/tobira.nvim",
  config = function()
    require("tobira").setup()
  end,
}

πŸš€ Usage

CommandDescription
:TobiraShow the next suggestion now (ignores cooldown). Press q / Esc to dismiss.
:TobiraGuideToggle the cheatsheet panel
:TobiraProgressShow skill tree with mastery glyphs and a cursor-driven detail preview. x = suppress, p = pin, g/s = jump to guide/stats, q/Esc/<C-c> = close.
:TobiraStatsShow usage stats: command distribution (never/tried/familiar/mastered) and efficiency gap suggestions
:TobiraResetClear all usage data
:checkhealth tobiraDiagnose your install β€” Neovim version, data directory, usage.json validity, lang config

Full documentation is available in Neovim via :help tobira.

πŸ“Έ Screenshots β€” Guide, Stats & Progress panels

Guide panel

:TobiraGuide cheatsheet panel

:TobiraGuide opens a cheatsheet on the right side of the screen. Commands you've already mastered are automatically hidden, so only your next targets are shown β€” and if one of them fades from use after you'd gotten comfortable with it, it reappears with a ⟳ (forgotten) marker instead of staying hidden forever. Pinned commands always appear at the top, marked ●. Covers all 10 categories: motion, edit, search, window, fold, mark, macro, diff, ex, and terminal β€” each capped to 3 unmastered commands (preferring ones you've never tried) so the panel stays a compact reference to glance at while you keep coding, with a +N more line if a category has more to show. Opens automatically on first launch.

Usage stats

:TobiraStats usage stats

:TobiraStats leads with the one section that actually changes what you do next β€” Try these next, commands you're using heavily whose neighbors you've never tried β€” followed by a mastery bar and your top commands. Total keystrokes and how many commands you've discovered sit in a quiet line at the bottom: fun to see, but not the point. g / p jump straight to Guide / Progress.

Skill progress

:TobiraProgress skill tree

:TobiraProgress shows your current level and the full command learning graph as a calm grid β€” mastery glyphs only, no clutter. Move the cursor onto any command and a preview strip below the grid fills in with its usage sparkline, count, status, and how far it is from the next star. The header shows your overall {n} / {total} mastered ratio, and each category shows its own {done} / {total}.

GlyphMeaning
(blank)Not yet tried
β˜†Tried (1+ uses)
β˜…Familiar (100+ uses)
β˜…β˜…Practiced (1000+ uses)
β˜…β˜…β˜…Mastered (5000+ uses)
⟳Forgotten β€” recent use has fallen off well below its earlier pace
βœ—Suppressed β€” you don't want this suggested
●Pinned β€” always shown, in both :TobiraGuide and :TobiraProgress

Keys inside :TobiraProgress: x toggles suppress on the command under the cursor, p toggles pin, g / s jump to Guide / Stats, q / Esc / <C-c> closes.

βš™οΈ Configuration

All options are optional β€” the defaults work out of the box.

require("tobira").setup({
  lang                = 'en',    -- 'en' | 'ja' | 'zh' | 'es' | 'fr' | 'de'
  idle_delay          = 1500,    -- ms of inactivity before showing an ambient suggestion
  idle_suggestions    = true,    -- enable ambient idle suggestions
  suggestion_cooldown = 300,     -- s between automatic suggestions (default: 5 min)
  max_shown           = 2,       -- max times to suggest the same command per session
  integrations        = true,    -- boost suggestions when a known helper plugin is installed
})

tobira always respects your own :nmap/:nnoremap overrides β€” it never suggests a command you've remapped away, and integrations above only gates the optional plugin-detection boost, not that baseline behavior (see :help tobira-integrations). tobira also tells apart a genuine remap from Neovim's own factory-default mappings β€” gx, &, ]q/[q/]l/[l, and (Neovim 0.10+) even Y = y$ ship as built-in defaults out of the box, so none of them count as "remapped" on a stock install; they're suggested completely normally. Two narrow exceptions cover a mapping that genuinely was set by something but still does what tobira teaches: if you personally add nnoremap Y y$ yourself (redundant with the modern default above, but a real personal binding), it's recognized as equivalent, so the reactive y$ β†’ Y suggestion still fires instead of being suppressed outright β€” a Y remapped to anything else still suppresses it. Likewise, Neovim auto-loads matchit.vim by default, which remaps % to <Plug>(MatchitNormalForward) β€” a strict, compatible superset of the built-in % tobira teaches, so :TobiraGuide's cheat sheet still lists % (with a "mapped to ..." note) on a stock install instead of hiding it outright; a % remapped to anything else still hides it like any other override. Both exceptions are still excluded from the proactive idle/:Tobira picks and :TobiraStats's "Try these next" list either way β€” see :help tobira-integrations for why those two surfaces don't read this distinction.

🎯 Detected patterns (examples)

You do thistobira suggests
fa β†’ fa on the same line; β€” repeat the last f/t
dw β†’ icw β€” change word in one command
`j$ \times 10 \text{in} \text{a} \text{row}$}` β€” jump by paragraph
j$ \times 10 \text{in} \text{a} \text{row} \text{while} $&diff is set]c β€” jump to the next diff hunk
`dd$ \times 3 \text{in} \text{a} \text{row}${n}dd` β€” delete N lines at once
<C-w>q / `c$ \times 2 \text{in} \text{a} \text{row}$o` β€” close all other windows
G then gg (or gg then G)'' β€” jump straight back to your previous position
`cwFooBar$ \text{repeated} 3 \times (\text{navigation} \text{allowed} \text{between})$qq...q/@q` β€” record and replay a macro
Same `:s/pat/repl/$ \text{retyped} 3 \times \text{across} \text{lines}$g&` β€” repeat it across the whole file
`$ \times 2 \text{in} \text{terminal} \text{mode}, \text{no} \text{effect}$<C->` β€” exit terminal mode
<C-w>+ / <C-w>- / <C-w>< / `>$ \times 2 \text{in} \text{a} \text{row}$=` β€” equalize all window sizes
<C-e> / `$ \times 5 \text{in} \text{a} \text{row}$zz` β€” center the cursor line on screen
Same one-line edit (e.g. A;<Esc>) on 3 consecutive lines<C-v> β€” block-visual edit them all at once
Insert-mode edit right after a ]c/[c diff-hunk jumpdo / `dp$ β€” \text{diff} \text{obtain}/\text{put} \text{the} \text{whole} \text{hunk}
\text{Cursor} \text{returns} \text{to} \text{the} \text{same} \text{line} 3 \times \text{with} \text{real} \text{edits} \text{in} \text{between}$ma` β€” set a named mark to jump back to
`~$ \times 6 (\text{spans} \text{a} \text{word}) / \times 12 (\text{spans} \text{a} \text{line})gΒ iwβ€˜/β€˜gΒ g~iw` / `g~` β€” toggle case in one motion

71 patterns total β€” see :help tobira-patterns for the full list.

πŸ†š Similar plugins

PluginWhat it doesvs tobira
hardtime.nvimBlocks repeated keys, hints better motionsPunishes bad habits β€” tobira teaches without ever blocking input
precognition.nvimShows available motions as virtual textAlways-on overlay β€” tobira appears only when you would have benefited
spamguard.nvimDetects key spammingSpam detection only β€” tobira covers the full command graph and tracks mastery
pathfinder.vimSuggests more efficient cursor movementCursor movement only β€” tobira covers motion, edit, and search
vim-be-goodGame-based practiceGeneric drills β€” tobira personalizes to your actual usage

tobira is the only plugin that learns from your actual usage and shows you the specific commands you are missing.

❓ FAQ

QuestionAnswer
Will tobira slow down my Neovim?No β€” vim.on_key() stays minimal, no I/O per keystroke; usage data flushes only on exit.
Does it send my keystrokes anywhere?No. Everything stays local in tobira/usage.json.
Can I use it alongside hardtime.nvim?Yes β€” hardtime blocks bad habits, tobira teaches better ones. They complement each other.
A suggestion keeps appearing for something I already knowOpen :TobiraProgress, move to it, press x to suppress.
How do I reset my data?Run :TobiraReset.

🦾 Contributing

See CONTRIBUTING.md. This project follows strict TDD β€” tests before implementation, always.

Contributors to tobira.nvim

License

MIT