Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: CI

on:
pull_request:
push:
branches:
- main
tags:
- "v*"

jobs:
test:
name: Test (Neovim ${{ matrix.nvim }})
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
nvim:
- stable
# - nightly # Temporarily disabled: Lua print() is not recorded in :messages on Nightly.
- v0.10.0

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Checkout keyevent.nvim
uses: actions/checkout@v4
with:
repository: kibi2/keyevent.nvim
ref: v0.1.1
path: .deps/keyevent.nvim

- name: Setup Neovim
uses: rhysd/action-setup-vim@v1
with:
neovim: true
version: ${{ matrix.nvim }}

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.x"

- name: Install system packages
run: |
sudo apt-get update
sudo apt-get install -y jq luarocks

- name: Install LuaRocks & LuaCov
run: |
luarocks install --local luacov
luarocks install --local luacov-html
eval "$(luarocks path --bin)"

- name: Show versions
run: |
nvim --version
lua -v || true

- name: Run tests
run: |
chmod +x tests/run.sh
tests/run.sh

- name: Upload diff artifacts (on failure)
if: failure()
uses: actions/upload-artifact@v4
with:
name: test-diffs-${{ matrix.nvim }}
path: |
tests/cases/**/diff-*.txt
tests/stdout.txt
tests/stderr.txt

- name: Generate coverage report
run: |
chmod +x tests/coverage.sh
tests/coverage.sh

- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
name: coverage-${{ matrix.nvim }}
path: luacov.report.html

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
files: ./luacov.report.out
fail_ci_if_error: true
verbose: true
16 changes: 16 additions & 0 deletions .luacov
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
return {
statsfile = "luacov.stats.out",
reportfile = "luacov.report.out",

include = {
"lua/.*",
},

exclude = {
".*/nvim/runtime/.*",
".*/.luarocks/.*",
".*/site/pack/.*",
".*/tests/cases/.*",
".*/keyevent.nvim/.*",
},
}
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,20 @@ All notable changes to this project will be documented in this file.

## [0.2.1] - TBD

### Added

* Added shared counts across paired motion keys.
* Added CI tests.
* Added `luacov` for code coverage.

### Changed

* Changed deceleration to holding the opposite motion key. For example, holding `k` while holding `j` reduces the motion amount.
* Consecutive taps now increase the motion amount up to 4×; a third consecutive tap resets the acceleration.

### Fixed

* Prevented acceleration when an event is detected as an invalid repeat (`ng_repeat`).
* Fixed a one-key delay when applying the effect of a held key.

## [0.2.0] - 2026-09-26
Expand Down
123 changes: 82 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
# rush.nvim

**Time-based key mappings for Neovim.**
[![CI](https://github.com/kibi2/rush.nvim/actions/workflows/ci.yml/badge.svg)](https://github.com/kibi2/rush.nvim/actions)
[![codecov](https://codecov.io/gh/kibi2/rush.nvim/branch/main/graph/badge.svg)](https://codecov.io/gh/kibi2/rush.nvim)
![GitHub release](https://img.shields.io/github/v/release/kibi2/rush.nvim)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
![Neovim](https://img.shields.io/badge/Neovim-0.10+-57A143?logo=neovim)

**Control Vim motions with key repeats and holds.**

`rush.nvim` changes the amount of motion based on how you repeat and hold a key.

For example:

```text
For example:

j → normal Vim behavior
jj → normal Vim behavior
j + hold j → hold j is replaced with "2j"
jj + hold j → hold j is replaced with "4j"
7 + hold j → hold j is replaced with "7j"
5 + j + hold j → share count 5 between j and k
```

You can also change the motion amount while holding a key:
Expand All @@ -23,27 +29,30 @@ tap j + hold j → start with a larger motion

while holding j:
hold j again → increase the motion
tap j + hold j → decrease the motion
hold k → decrease the motion
```

The motion can eventually change direction:

j → larger j motions → smaller j motions → -j → larger -j motions
```text
j → larger j motions → smaller j motions → reverse direction → larger reverse motions
```

The idea is simple:

> **The meaning of a key can change depending on how it is typed over time.**

## Features

* Time-based key input detection
* Distinguishes clicks, taps, and key repeats
* Accelerates and decelerates repeated motions
* Uses tap sequences to change the motion amount
* Uses Ctrl and Alt as acceleration/deceleration controls on macOS
* Works in normal and visual modes
* Configurable timing thresholds
* Built-in diagnosis command for measuring key repeat timing
- Time-based key input detection
- Distinguishes clicks, taps, and key repeats
- Accelerates and decelerates repeated motions
- Uses tap sequences to change the motion amount
- Uses Ctrl and Alt as acceleration/deceleration controls on macOS
- Shares motion amounts between `j`/`k` or `h`/`l`
- Works in normal and visual modes
- Configurable timing thresholds
- Built-in diagnosis command for measuring key repeat timing

## Example

Expand All @@ -52,7 +61,7 @@ Except for held keys, `rush.nvim` behaves like normal Vim motions.
The following motion keys can be accelerated:

```text
h j k l w b e
h j k l w b
```

When you hold one of these keys, `rush.nvim` changes the motion amount according to the preceding taps, the repeat sequence, and, on macOS, the state of the modifier keys.
Expand All @@ -62,37 +71,36 @@ When you hold one of these keys, `rush.nvim` changes the motion amount according
A count is preserved throughout a key repeat sequence.

```text
5 + hold j → 5j, 5j, 5j, 5j, 5j, ...
5 + hold j → 5j repeat
```

In other words, each repeated `j` is effectively replaced with `5j`.

### Increase the motion from the beginning

The number of preceding taps determines the initial motion amount.

Acceleration starts one repeat later because `rush.nvim` needs to distinguish taps from holds.
The number of consecutive taps determines the motion amount for subsequent repeats.

```text
hold j → 1j, 1j, 1j, 1j, 1j, ...

tap j + hold j → 1j, 1j, 2j, 2j, 2j, ...

tap j + tap j + hold j → 1j, 1j, 1j, 4j, 4j, ...
hold j → 1j repeat
tap j + hold j → 2j repeat
tap j + tap j + hold j → 4j repeat
tap j + tap j + tap j + hold j → 1j repeat
```

Each additional tap before the hold doubles the motion amount.
The initial `j` of a sequence is always executed as `1j`; the increased motion applies to subsequent repeats.

Each additional tap doubles the motion amount, up to 4×. A third consecutive tap resets the acceleration.

### Increase the motion while holding

You can double the motion amount by releasing the key and holding it again.

```text
hold j → 1j, 1j, 1j, ...
hold j → 1j repeat

+ hold j → 1j, 2j, 2j, ...
+ hold j → 2j repeat

+ hold j → 2j, 4j, 4j, ...
+ hold j → 4j repeat
```

Each additional hold doubles the motion amount.
Expand All @@ -104,11 +112,11 @@ You can also halve the motion amount.
For example:

```text
+ hold j → 2j, 2j, 2j, 2j, ...
+ hold j → 2j repeat

+ tap j + hold j → 2j, 2j, 1j, 1j, ...
+ hold k → 1j repeat

+ tap j + hold j → 1j, 1j, -1j, -1j, ...
+ hold k → 1k repeat
```

When the motion amount reaches one, another decrease reverses the direction.
Expand All @@ -125,18 +133,51 @@ hold j + tap Ctrl → double the motion amount
hold j + tap Alt → halve the motion amount
```

Tapping Alt has the same effect as `tap j + hold j`.
Tapping Ctrl provides a convenient way to accelerate the motion without releasing the motion key.

Tapping Alt provides a convenient way to decelerate the motion without changing the direction.

> **Note:** On Windows and Ubuntu, pressing a modifier key while a key is repeating may stop the key repeat. macOS does not have this behavior, so modifier-key control is currently supported on macOS only.

You can also change the motion amount with `hold j` or `tap j + hold j`, but `hold j` requires a short wait before the repeat starts.
You can also change the motion amount with `hold j` or `hold k`. For example, while holding `j`, holding `k` decreases the motion amount and eventually reverses the direction.

Using a modifier key avoids this delay, so the motion amount can be changed immediately while the key is repeating.
Modifier keys provide an alternative way to change the motion amount while a key is repeating. Unlike `hold j` or `hold k`, they do not require releasing and holding another motion key.

Modifier keys are also less affected by tap/hold detection errors, since they do not require distinguishing between a tap and a hold.

However, modifier keys may be harder to press depending on the keyboard layout and the position of the motion key.

Choose whichever method feels more comfortable for your keyboard and workflow.

### Share the motion

A motion amount can be shared between `j` and `k`.

For example:

```text
5j + hold j → 5j repeat

k, k, j, k → 5k, 5k, 5j, 5k

1k, k, j, k → 1k, k, j, k
(shared count is reset)
```

The shared count can also be cleared by pressing `Esc`.

Motion amounts can be shared between the following pairs:

```text
j / k
h / l
w / b
```

Using another count with a motion key, such as `3h`, exits shared mode.

Using a count with another Vim command, such as `2dd`, does not exit shared mode.

## How it works

`rush.nvim` uses [`keyevent.nvim`](https://github.com/kibi2/keyevent.nvim) to detect sequences of key events and their timing.
Expand All @@ -147,8 +188,8 @@ The same motion key can therefore have different meanings depending on how it is
j
j
j ───────────── hold
↓
key repeats
↓
key repeats
```

The exact behavior depends on the timing thresholds configured for your environment.
Expand Down Expand Up @@ -219,18 +260,18 @@ The goal is not to replace Vim's motions, but to make moving around large docume

## Requirements

* Neovim 0.10+
* A system with key repeat support
- Neovim 0.10+
- A system with key repeat support

## Limitations

`rush.nvim` relies on Neovim's key mappings and input timing.

The exact timing characteristics depend on:

* your operating system
* your keyboard
* your OS key repeat settings
- your operating system
- your keyboard
- your OS key repeat settings

Use:

Expand All @@ -242,4 +283,4 @@ to measure the key repeat timing on your system and determine suitable values.

## License

MIT
MIT
24 changes: 0 additions & 24 deletions lua/rush/config.lua

This file was deleted.

Loading
Loading