nvim/NAVIGATION_MODE_PLAN.md

217 lines
6.1 KiB
Markdown

# 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 `[c` or `]c` (git hunks)
- Press `d` → executes `[d` or `]d` (diagnostics)
- Press `m` → executes `[m` or `]m` (methods)
- Press `f` → executes `[f` or `]f` (functions)
- Press `b` → executes `[b` or `]b` (buffers)
- Press `q` → executes `[q` or `]q` (quickfix)
### 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
```lua
local NavMode = {
active = false,
prefix = '[', -- '[' or ']'
}
```
### Key Remapping Strategy
1. Store original mappings for letters a-z, A-Z
2. On mode entry, create new mappings: `key → prefix .. key`
3. On mode exit, restore original mappings
4. 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`:
```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
```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:
1. **Statusline indicator**: Show `[NAV ←]` or `[NAV →]` in statusline
2. **Notification**: Brief message on mode entry/exit
3. **Command line**: `echo` message showing current prefix
4. **Cursor highlight**: Change cursor color/shape
5. **Virtual text**: Floating indicator in corner of window
## Open Questions
1. **Mapping conflicts**: How to handle if a letter already has a mapping?
- Overwrite temporarily?
- Skip that letter?
- Warn user?
2. **Buffer-local mappings**: Should mode respect buffer-local mappings?
- Store and restore per-buffer?
- Global mode only?
3. **Visual feedback**: Which approach is clearest without being intrusive?
4. **Number keys**: Should `0-9` also be prefixed?
- Useful for some navigation pairs
- But might conflict with counts
5. **Operators**: Should `d`, `c`, `y` still 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
1. **Core functionality** (essential)
- Mode enter/exit
- Key remapping for a-z
- Prefix toggle
- Basic state management
2. **Visual feedback** (important)
- Choose and implement feedback mechanism
- Ensure clarity when mode is active
3. **Edge cases** (polish)
- Handle existing mappings gracefully
- Buffer-local mapping preservation
- Mode timeout/auto-exit?
4. **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[` and `z]`
- [ ] 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)