6.1 KiB
6.1 KiB
Navigation Mode Feature Plan
Concept
A custom navigation mode where keypresses are automatically prefixed with [ or ], making it easier to navigate using Neovim's bracket-based navigation pairs without repeatedly typing brackets.
User Experience
Entry
z[- Enter navigation mode with[prefix active (backward)z]- Enter navigation mode with]prefix active (forward)
In Mode
- All letter keys (
a-z,A-Z) get prefixed with current bracket - Examples:
- Press
c→ executes[cor]c(git hunks) - Press
d→ executes[dor]d(diagnostics) - Press
m→ executes[mor]m(methods) - Press
f→ executes[for]f(functions) - Press
b→ executes[bor]b(buffers) - Press
q→ executes[qor]q(quickfix)
- Press
Toggle Prefix
[- Switch to[prefix (backward navigation)]- Switch to]prefix (forward navigation)
Exit
<Esc>- Exit navigation mode and restore normal mappings
Technical Implementation
State Management
local NavMode = {
active = false,
prefix = '[', -- '[' or ']'
}
Key Remapping Strategy
- Store original mappings for letters a-z, A-Z
- On mode entry, create new mappings:
key → prefix .. key - On mode exit, restore original mappings
- On prefix toggle, update all mappings with new prefix
Keys to Remap
- Lowercase:
a-z(26 keys) - Uppercase:
A-Z(26 keys) - Total: 52 keys dynamically remapped
Common Bracket Navigation Pairs
[c/]c- Previous/next git hunk (gitsigns)[d/]d- Previous/next diagnostic[b/]b- Previous/next buffer (custom mapping)[q/]q- Previous/next quickfix item[l/]l- Previous/next location list item[m/]m- Previous/next method (treesitter textobjects)[f/]f- Previous/next function (treesitter textobjects)[p/]p- Previous/next parameter (treesitter textobjects)[t/]t- Previous/next tag
Implementation Details
Module Structure
Create lua/navigation-mode.lua:
local M = {}
local state = {
active = false,
prefix = '[',
stored_mappings = {},
}
local LETTERS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
function M.enter(prefix)
if state.active then return end
state.prefix = prefix or '['
state.active = true
-- Store original mappings and create prefixed ones
for i = 1, #LETTERS do
local key = LETTERS:sub(i, i)
-- Store original mapping (if exists)
-- Create new mapping: key → prefix .. key
vim.keymap.set('n', key, state.prefix .. key, { noremap = true, silent = true })
end
-- Special mappings for [ and ] to toggle prefix
vim.keymap.set('n', '[', function() M.set_prefix('[') end, { noremap = true, silent = true })
vim.keymap.set('n', ']', function() M.set_prefix(']') end, { noremap = true, silent = true })
-- Exit mapping
vim.keymap.set('n', '<Esc>', function() M.exit() end, { noremap = true, silent = true })
-- TODO: Set visual feedback (statusline, notification, etc.)
end
function M.set_prefix(new_prefix)
if not state.active then return end
state.prefix = new_prefix
-- Remap all letters with new prefix
for i = 1, #LETTERS do
local key = LETTERS:sub(i, i)
vim.keymap.set('n', key, state.prefix .. key, { noremap = true, silent = true })
end
-- TODO: Update visual feedback
end
function M.exit()
if not state.active then return end
-- Restore original mappings
for i = 1, #LETTERS do
local key = LETTERS:sub(i, i)
vim.keymap.del('n', key)
-- Restore stored mapping if it existed
end
-- Clean up special mappings
vim.keymap.del('n', '[')
vim.keymap.del('n', ']')
vim.keymap.del('n', '<Esc>')
state.active = false
-- TODO: Clear visual feedback
end
return M
Integration in keymaps.lua
-- Navigation mode
vim.keymap.set('n', 'z[', function()
require('navigation-mode').enter('[')
end, { desc = 'Enter navigation mode (backward)' })
vim.keymap.set('n', 'z]', function()
require('navigation-mode').enter(']')
end, { desc = 'Enter navigation mode (forward)' })
Visual Feedback Options (TODO)
Need to decide on one or more:
- Statusline indicator: Show
[NAV ←]or[NAV →]in statusline - Notification: Brief message on mode entry/exit
- Command line:
echomessage showing current prefix - Cursor highlight: Change cursor color/shape
- Virtual text: Floating indicator in corner of window
Open Questions
-
Mapping conflicts: How to handle if a letter already has a mapping?
- Overwrite temporarily?
- Skip that letter?
- Warn user?
-
Buffer-local mappings: Should mode respect buffer-local mappings?
- Store and restore per-buffer?
- Global mode only?
-
Visual feedback: Which approach is clearest without being intrusive?
-
Number keys: Should
0-9also be prefixed?- Useful for some navigation pairs
- But might conflict with counts
-
Operators: Should
d,c,ystill work as operators or only as navigation?- Current plan: They become navigation only while in mode
- Trade-off: Can't delete/change while navigating
Implementation Phases
-
Core functionality (essential)
- Mode enter/exit
- Key remapping for a-z
- Prefix toggle
- Basic state management
-
Visual feedback (important)
- Choose and implement feedback mechanism
- Ensure clarity when mode is active
-
Edge cases (polish)
- Handle existing mappings gracefully
- Buffer-local mapping preservation
- Mode timeout/auto-exit?
-
Documentation (completion)
- Update README.md with keymaps
- Add examples of useful navigation pairs
- Consider adding to help docs
Estimated Complexity
- Core: ~100-150 lines of Lua
- Visual feedback: +20-50 lines depending on approach
- Edge case handling: +30-50 lines
- Total: 150-250 lines
Testing Checklist
- Enter mode with
z[andz] - Verify letter keys are prefixed correctly
- Toggle between
[and]prefix - Exit cleanly with
<Esc> - No residual mappings after exit
- Works across different buffers
- Visual feedback is clear
- Common navigation pairs work (c, d, m, f, q)