Full commit with working app
parent
9a4c929eb8
commit
9e51e33869
@ -0,0 +1 @@
|
|||||||
|
doc/tags
|
@ -0,0 +1,162 @@
|
|||||||
|
<div align="center">
|
||||||
|
# DelayTrain
|
||||||
|
_Train yourself to stop repeating keys... gently_
|
||||||
|
|
||||||
|
[How does it work?](#how-does-it-work) • [Installation](#installation)
|
||||||
|
• [Configuration](#configuration) • [Commands](#commands)
|
||||||
|
• [Contributing](#contributing)
|
||||||
|
|
||||||
|
TODO: quick demo gif ~/Videos/delaytrain.gif
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## "Stop using arrow keys!" "Stop using hjkl!"
|
||||||
|
|
||||||
|
If you're familiar with the (neo)vim community you've probably heard this a
|
||||||
|
million times. The whole point of vim is to seamlessly navigate where you
|
||||||
|
need to go without constantly repeating keypresses. Why press `j` ten times
|
||||||
|
when you can press `10j`, or `}`, or search.
|
||||||
|
|
||||||
|
But if you've been using vim for a while this may be a harder habit to break.
|
||||||
|
You try to stop relying on this type of navigation but you only catch
|
||||||
|
yourself after the fact. You might chastize yourself and move on but the
|
||||||
|
habit stays there.
|
||||||
|
|
||||||
|
Most recommendations involve disabling these keys altogether, but this only
|
||||||
|
increases frustration. Sometimes your next position is directly above and
|
||||||
|
the quickest option is to press `k`. This might help in the long run but
|
||||||
|
it's incredibly annoying and hard to stick with.
|
||||||
|
|
||||||
|
That's where DelayTrain comes in.
|
||||||
|
|
||||||
|
DelayTrain will still let you use these keybindings, but it only punishes
|
||||||
|
you when you _keep_ hitting them. If you need to navigate directly below,
|
||||||
|
you can still do that. But if you need to navigate 5 lines below using
|
||||||
|
repeated keypresses, DelayTrain will gently remind you that there might
|
||||||
|
be a better way by stopping the keypress from working for a certain
|
||||||
|
amount of time.
|
||||||
|
|
||||||
|
And DelayTrain doesn't just work for hjkl. Mappings are included to
|
||||||
|
prevent repeated arrow key presses and you can configure delaytrain to
|
||||||
|
prevent anything else like `w` or `b`.
|
||||||
|
|
||||||
|
## How does it work?
|
||||||
|
|
||||||
|
DelayTrain takes two configurable values, `delay_ms` and `grace_period`.
|
||||||
|
|
||||||
|
When you first hit a configured keypress (like `j`), the `delay_ms` timer
|
||||||
|
starts. You are given a `grace_period` of repeated keypresses within the
|
||||||
|
`delay_ms` timer before the key stops working. Once the `delay_ms` timer
|
||||||
|
ends, everything is reset.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
We'll use a few tables to show how this works. Assume the follwing:
|
||||||
|
|
||||||
|
* `delay_ms = 1000`
|
||||||
|
* `grace_period = 2`
|
||||||
|
|
||||||
|
By default the `grace_period` is 1, but setting it to 2 allows you to
|
||||||
|
press the key twice before it stops working.
|
||||||
|
|
||||||
|
| Keypress | Time | Grace Period | Does it work? |
|
||||||
|
| ---------- | ----------- | ------------ | ------------- |
|
||||||
|
| `j` | 0ms | 1 | Yes |
|
||||||
|
| `j` | 200ms | 2 | Yes |
|
||||||
|
| `j` | 500ms | 3 | No |
|
||||||
|
| `j` | 1000ms | 1 | Yes |
|
||||||
|
|
||||||
|
Each keypress starts a dedicated `delay_ms` timer and has a dedicated
|
||||||
|
`grace_period`. So if you're trying to navigate down and to the left,
|
||||||
|
this still works.
|
||||||
|
|
||||||
|
| Keypress | Time | Grace Period | Does it work? |
|
||||||
|
| ---------- | ----------- | ------------ | ------------- |
|
||||||
|
| `j' | 0ms | 1 | Yes |
|
||||||
|
| `j' | 200ms | 2 | Yes |
|
||||||
|
| `h' | 500ms | 1 | Yes |
|
||||||
|
| `j' | 700ms | 3 | No |
|
||||||
|
| `h' | 1000ms | 2 | Yes |
|
||||||
|
| `j' | 1200ms | 1 | Yes |
|
||||||
|
| `h' | 1400ms | 3 | No |
|
||||||
|
| `h' | 1500ms | 1 | Yes |
|
||||||
|
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Install with [vim-plug](https://github.com/junegunn/vim-plug):
|
||||||
|
|
||||||
|
```vim
|
||||||
|
Plug '[TODO GITHUB NAME]/delaytrain.nvim'
|
||||||
|
```
|
||||||
|
|
||||||
|
or with [packer](https://github.com/wbthomason/packer.nvim):
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Delay repeat execution of certain keys
|
||||||
|
use '[TODO GITHUB NAME]/delaytrain.nvim'
|
||||||
|
```
|
||||||
|
|
||||||
|
For the default setup (see defaults below), you can simply place the following
|
||||||
|
into your `init.lua`:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
require('delaytrain').setup()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
You can configure all DelayTrain settings through the `setup()` function. The
|
||||||
|
default DelayTrain mappings are included below:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
require('delaytrain').setup {
|
||||||
|
delay_ms = 1000, -- How long repeated usage of a key should be prevented
|
||||||
|
grace_period = 1, -- How many repeated keypresses are allowed
|
||||||
|
keys = { -- Which keys (in which modes) should be delayed
|
||||||
|
['nv'] = {'h', 'j', 'k', 'l'},
|
||||||
|
['nvi'] = {'<Left>', '<Down>', '<Up>', '<Right>'},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mappings
|
||||||
|
|
||||||
|
The keys option allows you to delay different keypresses in different modes.
|
||||||
|
This takes the following KV pair:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
['list_of_applicable_modes'] = {'keys', 'you', 'want', 'delayed'},
|
||||||
|
```
|
||||||
|
|
||||||
|
Modes can be added based on their short-names (ex: normal is 'n', insert is
|
||||||
|
'i') and multiple modes can be added to a single keymap.
|
||||||
|
|
||||||
|
This option ties into a call to `vim.keymap.set()`, so mode short-names and
|
||||||
|
key names should match what is possible in that function.
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
Global options can be modified to change delay/grace period settings on the
|
||||||
|
fly:
|
||||||
|
|
||||||
|
* `g:delaytrain_delay_ms`
|
||||||
|
* `g:delaytrain_grace_period`
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
The following commands allow you to turn DelayTrain on and off without
|
||||||
|
calling `setup()` again:
|
||||||
|
|
||||||
|
* `:DelayTrainEnable`
|
||||||
|
* `:DelayTrainDisable`
|
||||||
|
* `:DelayTrainToggle`
|
||||||
|
|
||||||
|
By default, DelayTrain is turned on when the `setup()` function is called.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
This has been tested on my personal and work machines using nvim-nightly and
|
||||||
|
[Neovide](https://github.com/neovide/neovide). This is a REALLY SMALL plugin
|
||||||
|
so while there shouldn't be a lot of issues it's entirely possible I missed
|
||||||
|
something. I'm also brand new to plugin development so if you notice anything
|
||||||
|
off please feel free to open up an issue or send me a PR!
|
@ -0,0 +1,118 @@
|
|||||||
|
*delaytrain.txt* DelayTrain
|
||||||
|
*delaytrain.nvim*
|
||||||
|
|
||||||
|
Author: James Ford <james@jamesford.io>
|
||||||
|
Version: 1.0.0
|
||||||
|
Homepage: <>
|
||||||
|
|
||||||
|
=============================================================================
|
||||||
|
INTRODUCTION *delaytrain*
|
||||||
|
|
||||||
|
When learning how to use Vim keybindings, sometimes it's helpful to avoid
|
||||||
|
repetitive keypresses. For example, repetitive use of hjkl or arrow keys can
|
||||||
|
often be replaced with a numeric prefix, or an even better key to navigate
|
||||||
|
exactly where you want to go.
|
||||||
|
|
||||||
|
To help train your muscle memory, most people suggest un-mapping these keys
|
||||||
|
to avoid using them completely. But there are certain cases where hjkl may
|
||||||
|
be necessary and another key combination would be overkill.
|
||||||
|
|
||||||
|
DelayTrain helps train this muscle memory by preventing a keypress from
|
||||||
|
being used after a certain amount of repeated usage inside a timeframe,
|
||||||
|
rather than disabling the key completely. This way you can still go about
|
||||||
|
your work with a gentle reminder that there might be a better way to do
|
||||||
|
what you want.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
USAGE *delaytrain-usage*
|
||||||
|
|
||||||
|
Most people will be training with hjkl and arrow keys, so for a default setup
|
||||||
|
you can simply run:
|
||||||
|
>
|
||||||
|
require('delaytrain').setup()
|
||||||
|
<
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
CONFIGURATION *delaytrain-configuration*
|
||||||
|
|
||||||
|
For more granular configuration and additional keymaps, you can pass the
|
||||||
|
following configuration (default settings included):
|
||||||
|
>
|
||||||
|
require('delaytrain').setup {
|
||||||
|
delay_ms = 1000, -- How long repeated usage of a key should be prevented
|
||||||
|
grace_period = 1, -- How many repeated keypresses are allowed
|
||||||
|
keys = { -- Which keys (in which modes) should be delayed
|
||||||
|
['nv'] = {'h', 'j', 'k', 'l'},
|
||||||
|
['nvi'] = {'<Left>', '<Down>', '<Up>', '<Right>'},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
<
|
||||||
|
Keep in mind that the `delay_ms` timer starts on the FIRST keypress and not the
|
||||||
|
final `grace_period` keypress. For example (with default settings) if you hit j,
|
||||||
|
the timer starts immediately. If you hit j again after 500ms, the key will not
|
||||||
|
work. If you wait ANOTHER 500ms and hit j again, it will work. Likewise, if
|
||||||
|
you hit j, wait 1000ms, and hit j again, both keypresses will work.
|
||||||
|
|
||||||
|
With an increased `grace_period`, you can hit a key `grace_period` amount of times
|
||||||
|
inside `delay_ms` before it stops working.
|
||||||
|
|
||||||
|
`grace_period` and `delay_ms` only affect the current key being pressed. With
|
||||||
|
the default settings, if you hit j and then hit k after 500ms, both keypresses
|
||||||
|
will work. If you wait another 200ms and hit j again, the keypress will not
|
||||||
|
work.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
MAPPINGS *delaytrain-mappings*
|
||||||
|
|
||||||
|
The keys option allows you to delay different keypresses in different modes.
|
||||||
|
This takes the following KV pair:
|
||||||
|
>
|
||||||
|
['list_of_applicable_modes'] = {'keys', 'you', 'want', 'delayed'},
|
||||||
|
<
|
||||||
|
Modes can be added based on their short-names (ex: normal is 'n', insert is
|
||||||
|
'i') and multiple modes can be added to a single keymap.
|
||||||
|
|
||||||
|
This option ties into a call to |vim.keymap.set()|, so mode short-names and
|
||||||
|
key names should match what is possible in that function.
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
OPTIONS *delaytrain-options*
|
||||||
|
|
||||||
|
Global options can be modified to change delay/grace period settings on the
|
||||||
|
fly.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
g:delaytrain_delay_ms~ *g:delaytrain_delay_ms*
|
||||||
|
|
||||||
|
How long repeated usage of a key should be prevented. Keep in mind that the
|
||||||
|
timer starts on the FIRST keypress and delays repeated usage of a key based on
|
||||||
|
`grace_period` in this timeframe.
|
||||||
|
|
||||||
|
Type: |Number|
|
||||||
|
Default value: `1000`
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
g:delaytrain_grace_period~ *g:delaytrain_grace_period*
|
||||||
|
|
||||||
|
How many repeated keypresses are allowed inside the `delay_ms` timeframe. By
|
||||||
|
default this is 1, so you can press a key 1 time inside `delay_ms` before it's
|
||||||
|
disabled. Setting this value to 0 will completely disable the key.
|
||||||
|
|
||||||
|
Type: |Number|
|
||||||
|
Default value: `1`
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
COMMANDS *delaytrain-commands*
|
||||||
|
|
||||||
|
:DelayTrainEnable *:DelayTrainEnable*
|
||||||
|
Enable DelayTrain by setting defined keymaps
|
||||||
|
|
||||||
|
:DelayTrainDisable *:DelayTrainDisable*
|
||||||
|
Disable DelayTrain by deleting defined keymaps
|
||||||
|
|
||||||
|
:DelayTrainToggle *:DelayTrainToggle*
|
||||||
|
Toggle DelayTrain by setting/deleting keymaps
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
vim:tw=78:ts=8:ft=help:norl:
|
@ -0,0 +1,96 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
vim.g.delaytrain_delay_ms = 1000
|
||||||
|
vim.g.delaytrain_grace_period = 1
|
||||||
|
|
||||||
|
-- Map of keys to their individual current grace period
|
||||||
|
-- This keeps track of how many times a key has been pressed
|
||||||
|
local current_grace_period_intervals = {}
|
||||||
|
|
||||||
|
local keymaps = {
|
||||||
|
['nv'] = {'h', 'j', 'k', 'l'},
|
||||||
|
['nvi'] = {'<Left>', '<Down>', '<Up>', '<Right>'},
|
||||||
|
}
|
||||||
|
|
||||||
|
local is_enabled = false
|
||||||
|
|
||||||
|
function M.try_delay_keypress(key)
|
||||||
|
current_interval = current_grace_period_intervals[key]
|
||||||
|
|
||||||
|
-- Start a timer on the first keypress to reset the interval
|
||||||
|
if current_interval == 0 then
|
||||||
|
vim.loop.new_timer():start(vim.g.delaytrain_delay_ms, 0, function()
|
||||||
|
current_grace_period_intervals[key] = 0
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Pass the key through only if we haven't reached the grace period
|
||||||
|
if current_interval < vim.g.delaytrain_grace_period then
|
||||||
|
current_grace_period_intervals[key] = current_interval + 1
|
||||||
|
|
||||||
|
vim.api.nvim_feedkeys(
|
||||||
|
vim.api.nvim_replace_termcodes(key, true, false, true),
|
||||||
|
'n',
|
||||||
|
false
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.setup(opts)
|
||||||
|
if opts then
|
||||||
|
if opts.delay_ms then
|
||||||
|
vim.g.delaytrain_delay_ms = opts.delay_ms
|
||||||
|
end
|
||||||
|
|
||||||
|
if opts.grace_period then
|
||||||
|
vim.g.delaytrain_grace_period = opts.grace_period
|
||||||
|
end
|
||||||
|
|
||||||
|
if opts.keys then
|
||||||
|
keymaps = opts.keys
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
M.enable()
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.enable()
|
||||||
|
is_enabled = true
|
||||||
|
|
||||||
|
for modes, keys in pairs(keymaps) do
|
||||||
|
mode_array = {}
|
||||||
|
for mode in modes:gmatch"." do
|
||||||
|
table.insert(mode_array, mode)
|
||||||
|
end
|
||||||
|
for _, key in ipairs(keys) do
|
||||||
|
-- Set the current grace period for the given key
|
||||||
|
current_grace_period_intervals[key] = 0
|
||||||
|
|
||||||
|
vim.keymap.set(mode_array, key, function() M.try_delay_keypress(key) end, {expr = true})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.disable()
|
||||||
|
is_enabled = false
|
||||||
|
|
||||||
|
for modes, keys in pairs(keymaps) do
|
||||||
|
mode_array = {}
|
||||||
|
for mode in modes:gmatch"." do
|
||||||
|
table.insert(mode_array, mode)
|
||||||
|
end
|
||||||
|
for _, key in ipairs(keys) do
|
||||||
|
vim.keymap.del(mode_array, key)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.toggle()
|
||||||
|
if is_enabled then
|
||||||
|
M.disable()
|
||||||
|
else
|
||||||
|
M.enable()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
@ -0,0 +1,11 @@
|
|||||||
|
vim.api.nvim_create_user_command("DelayTrainEnable", function()
|
||||||
|
require("delaytrain").enable()
|
||||||
|
end, { desc = "Enable DelayTrain by setting defined keymaps" })
|
||||||
|
|
||||||
|
vim.api.nvim_create_user_command("DelayTrainDisable", function()
|
||||||
|
require("delaytrain").disable()
|
||||||
|
end, { desc = "Disable DelayTrain by deleting defined keymaps" })
|
||||||
|
|
||||||
|
vim.api.nvim_create_user_command("DelayTrainToggle", function()
|
||||||
|
require("delaytrain").toggle()
|
||||||
|
end, { desc = "Toggle DelayTrain by setting/deleting keymaps" })
|
Loading…
Reference in New Issue