Full commit with working app

main
James Ford 2 years ago
parent 9a4c929eb8
commit 9e51e33869

1
.gitignore vendored

@ -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…
Cancel
Save