Two logical changes that landed as one file rewrite:
1. Tighten the code. SLOC 168 -> 126 (-42), total lines 201 -> 146
pre-comments. Moves: inline single-statement if/while bodies (via
stylua's `collapse_simple_statement = Always` and `column_width
= 120`), collapse K-termcode table to one line, combine api/fn/M
into one local declaration, drop verbose config comments, merge
`two` / `ivs_by_line` / `li` into one local, inline CR-branch
loop, drop the apply-indirection in setup(), and assorted smaller
trims.
2. Re-add prose where the golf made intent non-obvious. Comments now
explain: smartcase detection, the +start_line/-2 offset reconcile,
the *global* avail disqualification, the cursor-proximity sort
purpose, the slow/fast char encoding of pair labels (and why it
keeps `prefixes` minimal), the overlap-skip interval math and the
carryover-to-next-match behavior, why the augroup is recreated per
setup call, and why table-form labels own a dedicated highlight
group. No code change from the comment pass itself.
Final sizes: 126 SLOC, 194 total lines including 55 comment lines
and 13 blank lines. Still smaller than upstream (131 SLOC) and one
to two orders of magnitude smaller than every other jump plugin.
Two changes folded into one commit since they were developed
together:
1. Raise two-char label cap from 2*|L| to |L|^2. The cap was a
conservative guess against visual density; the gap+overlap-skip
logic makes density self-limiting regardless of pool size. Cost:
all 26 letters land in prefixes[] in two-char mode, so single-
char pattern extension doesn't work there. Acceptable since
you're in two-char mode precisely because you want to jump, not
extend further.
2. Fix: overlap check now compares a candidate label against ALL
labels already placed on the same line, not just the most recent
one. Previously line_end[line] tracked a single right edge,
which assumed left-to-right commit order. With cursor-proximity
sort, a right-of-cursor label can commit first and poison the
tracker, wrongly rejecting a left-of-it match whose label
wouldn't actually overlap. Now line_intervals[line] holds a list
of [start, end) intervals and the candidate is tested against
the whole list with a gap-aware interval-overlap check.
When there are more matches than the single-char label pool (>|L|,
i.e. > 26 with the default), issue two-char labels so matches past
the single-char ceiling are still reachable.
Tactics:
- Cap: 2 * |L| = 52 labels total in two-char mode. The full |L|^2 =
676 space produces visually unusable density on packed buffers;
52 is plenty once the cursor-proximity sort lands them near the
cursor.
- Label order: first char varies slow (26 labels starting with 'f',
next 26 starting with 'j') so only 'f' and 'j' become prefixes.
The remaining 24 letters stay usable for pattern extension in
two-char mode.
- Per-line readability spacing: a 2-char label requires one column
of original text between itself and the previous 2-char label on
the same line. Prevents visual bleed when matches are adjacent
(e.g. '0' search on timestamp-heavy text). Single-char mode
unchanged.
- No global mode flag: a 'prefixes' set, populated while assigning
labels, tells the input loop when to buffer a second keystroke.
The set resets each iteration with 'active'.
scan() returns matches in top-to-bottom reading order. With the
26-label cap, that meant the first 26 matches on screen ate all the
labels — your cursor could be at the bottom of the buffer and still
have no labels near it.
Sort matches by |line - cursor_line|, then by |col - cursor_col| as
tie-break, before label assignment. Labels now land on the 26 closest
matches rather than the 26 topmost. Same label pool, same public API,
big UX win for any buffer with more hits than labels.
Replace the physical-position label order with a finger-strength-based
order that alternates hands where possible: index > middle > ring >
pinky, home row > top row > bottom row. Home row pairs come first
(fjdksl), then home stretches, then top row index/middle pairs, and
so on, with the weakest pinky/bottom-row-stretch keys trailing.
Marginal but measurable ergonomic win for touch typists; conflict
avoidance logic unchanged.
Add the standard MPL Exhibit A notice at the top of init.lua along
with explicit copyright lines for both the original author (Yorick
Peterse) and the modifications in this fork. The LICENSE file
already carries the full MPL-2.0 text; this just makes per-file
provenance unambiguous.
- Fuse search() and available_labels() into a single scan() pass.
Drops the line_index bookkeeping field on match records and one
full traversal of the visible buffer.
- Replace the separate LABELS array with direct indexing into
M.config.labels (a string, ~26 ASCII chars). One less parallel
data structure; no fn.split at setup, no bottom-of-file auto-setup
call needed.
- Label picker switched from nested O(M*L) scan to an O(M+L) cursor
walk. No more double-duty 'avail' set acting as both conflict-set
and consumed-label marker.
- Termcode constants consolidated into a K = { ... } table using
vim.keycode.
- Normalize module state: CONFIG -> M.config.
- Collapse 'if opts then extend end' to 'opts or {}'.
- Fix: hoist create_augroup out of the table-valued label branch so
a second setup() call with a string label clears any stale autocmd
from a prior table-valued call.
- Drop redundant '#line > 0' guard in scan (string.find handles
empty lines correctly).
Net: 186 -> 148 lines. Public API unchanged.
Fork of https://github.com/yorickpeterse/nvim-jump (MPL-2.0) with one
addition: `label` accepts a table of nvim_set_hl attributes in
addition to a group name, and the plugin re-applies it on ColorScheme.
Upstream declined the feature as out of scope (the project is
considered feature-complete), so it lives here instead. Credit for the
core implementation goes to Yorick Peterse.
Changes relative to upstream:
- Module renamed 'jump' -> 'tiny-jump'
- Namespace and augroup renamed accordingly
- Internal managed highlight group renamed 'JumpLabel' -> 'TinyJumpLabel'
- Default `label` changed from 'FlashLabel' to 'IncSearch' (no longer
catering to flash.nvim migration)
- README rewritten