217 lines
6.1 KiB
Markdown
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)
|