** ONLY BEGINNER LEVEL IS AVAILABLE Currently **
** Advanced Level coming soon! **
motioncoach-nvim is a Neovim plugin that watches your navigation and editing episodes and suggests more efficient Vim motions and techniques "WHEN YOU NEED THEM"
It is designed as a coach, not a tutor. Suggestions are:
- contextual to user's live motions/commands
- rate-limited by keystrokes and time frames
- undo-aware
- registers-aware
- jumplist-aware
- leveled (Beginner/Advanced)
- All suggestions are delivered via NeoVim notification messages
motioncoach-DEMO.mp4
- Selectable with Keymaps
- Apply to Neovim session
- Count compression (
10j,5k) - Scroll suggestions (
<C-d>,<C-u>) - Line landmarks (
0,^,$) - Large jump hints (
gg,G) - Horizontal efficiency (
w,bvsllll)
Some Advanced Suggestions:
- Key pattern analysis (via
vim.on_key) - State-diff validation (cursor movement, operators, undo)
- Text object suggestions:
ciw,di",ci(,dap, etc.
- Register coaching:
"_d,"0p,"+y,"+p
- Marks & jumplist coaching:
ma,'a,`a<C-o>/<C-i>
- Yank history capture (local, in-memory)
- Delimiter / surround coaching
- Optional Treesitter textobject hints
- Configurable plugin recommendations
- Coaching is disabled
{
"emo333/motioncoach-nvim",
config = function()
require("motioncoach-nvim").setup({
coachingLevel = 1, -- set to what you want to be your default
})
end
}use "emo333/motioncoach.nvim"vim.pack.add({
'https://github.com/emo333/motioncoach-nvim',
})
-- after add()
plugin1 = require('motioncoach-nvim'):MotionCoachOff
:MotionCoachBeginner
:MotionCoachAdvanced
:MotionCoachLevel 0|1|2
vim.keymap.set('n', '<leader>m', '[M]otion Coach -->')
vim.keymap.set('n', '<leader>m0', function()
require('motioncoach-nvim').set_level(0)
end, { desc = 'MotionCoach Disable(0)' })
vim.keymap.set('n', '<leader>m1', function()
require('motioncoach-nvim').set_level(1)
end, { desc = 'MotionCoach Beginner Level(1)' })
vim.keymap.set('n', '<leader>m2', function()
require('motioncoach-nvim').set_level(2)
end, { desc = 'MotionCoach Advanced Level(2)' })motioncoach-nvim is configured via:
require("motioncoach-nvim").setup({
-- options
})- either add this to your snacks.nvim configuration or;
add this as a plugin (eg.
snacks.lua):
return {
{
"folke/snacks.nvim",
config = function(_, opts)
require("snacks").setup(opts)
local original_notify = Snacks.notifier.notify
---@diagnostic disable-next-line: duplicate-set-field
Snacks.notifier.notify = function(msg, level, notify_opts)
notify_opts = notify_opts or {}
-- Custom logic: If msg has "Motion" in it, set timeout to 3 seconds
if msg:find("Motion") then
notify_opts.timeout = 3000
end
return original_notify(msg, level, notify_opts)
end
end,
opts = {
notifier = {
-- Set the maximum width for notifications before they wrap/expand
width = { min = 40, max = 0.4 }, -- max can be a percentage of screen width
-- Default is 3000ms (3 seconds)
-- timeout = 5000, -- Increase this value (in milliseconds)
-- Return true to keep the notification on screen
keep = function(notif)
local severity = vim.log.levels
return notif.level == severity.ERROR or notif.level == severity.WARN
end,
},
styles = {
notification = {
wo = {
wrap = true, -- Enable line wrapping for long messages
},
},
},
},
},
}
By default, motioncoach-nvim:
❌ Does not capture command-line input (: / ?)
❌ Does not capture insert-mode keys
You can override these, if you desire, to get more Advance suggestions:
require("motioncoach-nvim").setup({
captureCommandLineKeys = false,
captureInsertModeKeys = false,
})
✅ Uses a small rolling ring buffer for key patterns
✅ Captures yank contents locally only
❌ Never writes anything to disk
❌ Never sends data
motioncoach-nvim captures yank events via TextYankPost and stores them in a per-buffer yank ring.
-
This is used to suggest yank( or cut/delete )/register technique suggestions
-
Yank contents, captured for suggestions, never leave memory
motioncoach.nvim can suggest plugins only after repeated evidence(within a Neovim session) and only in advanced mode.
- Recommendations are:
-
Fully configurable
-
Evidence-based
-
Designed to evolve over time
-
- Disable all plugin recommendations(Default):
require("motioncoach-nvim").setup({
pluginRecommendations = {
enabled = false,
}
})- Disable a single recommendation:
require("motioncoach-nvim").setup({
pluginRecommendations = {
items = {
surround = { enabled = false },
}
}
})For long-term evolution, you will be able to supply a provider function that decides recommendations dynamically:
require("motioncoach-nvim").setup({
pluginRecommendations = {
provider = function(evidenceCounters, context)
if (evidenceCounters.surroundLikeEvidenceCount or 0) >= 6 then
evidenceCounters.surroundLikeEvidenceCount = 0
return "Plugin idea: consider mini.surround or nvim-surround."
end
return nil
end
}
})- The provider would run before built-in defaults.
- I am not a professional programmer.
- I have been programming most of my life; either as side duties inherent to work or as hobby at home.
- Vim/NeoVim/Lua are all new to me ( started delving into these around November 2025 ).
- I started this project based on my own desire to have something "inside" NeoVim to remind/assist/suggest/coach me learning Vim motions.
- I used ai to assist me developing this. But just when I needed to get over a hump.
Hell yeah! Bring em!
MIT(ch) <-- I crack me up ;)
