-
Notifications
You must be signed in to change notification settings - Fork 43
/
leap.txt
491 lines (395 loc) · 21.7 KB
/
leap.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
*leap.txt*
For Neovim version 0.7.0
Last change: 2022 November 18
==============================================================================
CONTENTS *leap.nvim* *leap-contents*
Introduction ············································· |leap-introduction|
Usage ··························································· |leap-usage|
Configuration ·················································· |leap-config|
Default mappings ····································· |leap-default-mappings|
Custom mappings ······································· |leap-custom-mappings|
Highlighting ················································ |leap-highlight|
Events ························································· |leap-events|
Extending Leap ············································· |leap-extensions|
==============================================================================
INTRODUCTION *leap-introduction*
Leap is a motion plugin that allows you to jump to any positions in the
visible editor area by entering a 2-character search pattern, and then
potentially a "label" character to pick your target from multiple matches,
similar to Justin M. Keyes' Sneak plugin. The novel idea in Leap is its
"clairvoyant" ability: you get a live preview of the target labels - by
mapping possible futures, Leap can show you which key(s) you will need to
press before you actually need to do that, resulting in a much more
frictionless experience.
==============================================================================
USAGE *leap-usage*
- Initiate the search in the forward (`s`) or backward (`S`) direction, or in
the other windows (`gs`).
- Start typing a 2-character pattern (`{c1}{c2}`).
- After typing the first character, you see "labels" appearing next to some of
the `{c1}{?}` pairs. You cannot _use_ the labels yet.
- Enter `{c2}`. If the pair was not labeled, then voilà, you're already there.
No need to be bothered by remaining labels, just continue editing.
- Else: select a label. In case of multiple groups, first switch to the desired
one, using `<space>` (step back with `<tab>`, if needed).
*leap-visual-mode* *leap-operator-pending-mode*
In Visual and Operator-pending mode, there are two different pairs of
directional motions available, providing the necessary additional comfort and
precision.
`s`/`S` are like their Normal-mode counterparts, except that `s` includes the
whole match in the selection/operation (which might be considered the more
intuitive behaviour for these modes).
On the other hand, `x`/`X` are like `t`/`T` for `f`/`F` - they exclude the
matched pair:
```
abcd| |bcde
████e ← Sab sde → █████
ab██e ← Xab xde → ███de
```
Note that each of the forward motions are |inclusive|, and |o_v| works as
expected on them.
*leap-match-newline*
A character at the end of a line can be targeted by pressing `<space>` after it.
There is no special mechanism behind this: you can set aliases for the
newline character simply by defining a set in |leap.opts.equivalence_classes|
that contains it. Empty lines can also be targeted, by pressing the newline
alias twice (`<space><space>` by default). This latter is a slightly more
magical feature, but fulfills the principle that any visible position you can
move to with the cursor should be reachable by Leap too.
*leap-repeat*
Pressing `<enter>` after invoking any of Leap's motions sets the search
pattern to the previous one (`special_keys.repeat_search`).
*leap-traversal*
After entering at least one input character, `<enter>` initiates "traversal"
mode, moving on to the next match on each keypress
(`special_keys.next_phase_one_target` or `special_keys.next_target`).
`<tab>` can revert the previous jump(s) in case you accidentally overshoot
your target (`special_keys.prev_target`).
Tips:
- When repeating the previous search, you can immediately move on:
`s|S <enter> <enter> ...`
- Accepting the first match after one input character is a useful shortcut in
operator-pending mode (e.g. `ds{char}<enter>`).
- Traversal mode can be used as a substitute for normal-mode `f`/`t` motions.
`s{char}<enter><enter>` is the same as `f{char};`, but works over multiple
lines.
Note: If |leap.opts.safe_labels| is in use, the labels will remain available
during the whole time.
Note: For cross-window search, traversal mode is not supported (since there's
no direction to follow), but you can still accept the first (presumably only)
match with `<enter>`, even after one input.
*leap-dot-repeat*
You can repeat change and delete operations with the `.` character, if
repeat.vim (https://github.com/tpope/vim-repeat) is installed.
==============================================================================
CONFIGURATION *leap-config* *leap.opts*
Below is the description of all configurable values in the `opts` table, with
their defaults.
Example configuration: >
local leap = require('leap')
leap.opts.case_sensitive = true
leap.opts.substitute_chars = { ['\r'] = '¬' }
leap.opts.special_keys.prev_target = { '<s-enter>', ',' }
<
Available options~
*leap.opts.max_phase_one_targets*
`max_phase_one_targets = nil`
By default, the plugin shows labels and/or highlights matches right after
the first input character. This option disables ahead-of-time displaying
of target beacons beyond a certain number of phase one targets (to
mitigate visual noise in extreme cases). Setting it to 0 disables
two-phase processing altogether.
*leap.opts.highlight_unlabeled_phase_one_targets*
`highlight_unlabeled_phase_one_targets = false`
Whether to highlight all unlabeled matches in phase one. (Matches covering
labels will get highlighted anyway.)
*leap.opts.max_highlighted_traversal_targets*
`max_highlighted_traversal_targets = 10`
Number of targets to be highlighted after the cursor in |leap-traversal|
mode (when there are no labels at all).
*leap.opts.case_sensitive*
`case_sensitive = false`
Whether to consider case in search patterns.
*leap.opts.equivalence_classes*
`equivalence_classes = { ' \t\r\n' }`
A character will match any other in its equivalence class. The sets can
either be defined as strings or tables.
Example: >
{
'\r\n',
')]}>',
'([{<',
{ '"', "'", '`' },
}
<
Note: Make sure to have a set containing `\n` if you want to be able to
target characters at the end of the line.
Note: Non-mutual aliases are not possible in Leap, for the same reason
that supporting |smartcase| is not possible: we would need to show two
different labels, corresponding to two different futures, at the same
time.
*leap.opts.substitute_chars*
`substitute_chars = {}`
The keys in this table will be substituted in labels and highlighted
matches by the given characters. This way special (e.g. whitespace)
characters can be made visible in matches, or even be used as labels.
Example: `{ ['\r'] = '¬' }`
*leap.opts.safe_labels*
`safe_labels` >
{ "s", "f", "n", "u", "t", "/",
"S", "F", "N", "L", "H", "M", "U", "G", "T", "?", "Z" }
<
When the number of matches does not exceed the number of these "safe"
labels plus one, the plugin jumps to the first match automatically after
entering the pattern. Obviously, for this purpose you should choose keys
that are unlikely to be used right after a jump!
Setting the list to `{}` effectively disables the autojump feature.
Note: Operator-pending mode ignores this, since we need to be able to
select the actual target before executing the operation.
*leap.opts.labels*
`labels` >
{ "s", "f", "n",
"j", "k", "l", "h", "o", "d", "w", "e", "m", "b",
"u", "y", "v", "r", "g", "t", "c", "x", "/", "z",
"S", "F", "N",
"J", "K", "L", "H", "O", "D", "W", "E", "M", "B",
"U", "Y", "V", "R", "G", "T", "C", "X", "?", "Z" }
<
Target labels to be used when there are more matches than the number of
safe labels plus one.
Setting the list to `{}` forces autojump to always be on (except for
Operator-pending mode, where it makes no sense). In this case, do not
forget to set `special_keys.next_group` to something "safe" too.
Heuristics behind the defaults:
- At least the first few labels should be the same on the two lists, since
those become muscle memory, and might be used automatically, a bit like
[count] values.
- Since the commands invoking the motions are mapped to left-hand keys by
default, we tend to prioritize right-hand keys to get a better balance
for the whole sequence on average.
*leap.opts.special_keys*
`special_keys` >
{
repeat_search = '<enter>',
next_phase_one_target = '<enter>',
next_target = {'<enter>', ';'},
prev_target = {'<tab>', ','},
next_group = '<space>',
prev_group = '<tab>',
multi_accept = '<enter>',
multi_revert = '<backspace>',
}
<
Keys captured by the plugin at runtime, to:
- repeat with the previous input after invocation
- jump to the next/previous match in traversal mode (|leap-traversal|)
- switch to the next/previous group of matches, when there are more
matches than available labels
- accept the selection, and deselect the last selected target in
|leap-multiselect| mode
==============================================================================
DEFAULT MAPPINGS *leap-default-mappings*
The defaults can be set by calling `require('leap').add_default_mappings()`.
Note that the function will check for conflicts with any custom mappings
created by you or other plugins, and will not overwrite them, unless explicitly
told so (called with a `true` argument).
Trigger keys~
Normal mode
*leap_s*
s{char1}{char2} Jump forward to a labeled or [count]'th visible
occurrence of {char1}{char2}. The cursor is placed on
{char1}.
*leap_S*
S{char1}{char2} Jump backward to a labeled or [count]'th visible
occurrence of {char1}{char2}. The cursor is placed on
{char1}.
Visual and Operator-pending mode
*leap_v_s* *leap_o_s*
s{char1}{char2} Jump forward to a labeled or [count]'th visible
occurrence of {char1}{char2}. The cursor is
placed on {char2} |inclusive|.
*leap_v_S* *leap_o_S*
S{char1}{char2} Jump backward to a labeled or [count]'th visible
occurrence of {char1}{char2}. The cursor
is placed on {char1}.
*leap_v_x* *leap_o_x*
x{char1}{char2} Jump forward to a labeled or [count]'th visible
occurrence of {char1}{char2}. The cursor is
placed on the character preceding {char1}
|inclusive|.
Mnemonic: "exclusive (s)".
*leap_v_X* *leap_o_X*
X{char1}{char2} Jump backward to a labeled or [count]'th visible
occurrence of {char1}{char2}. The cursor
is placed on the character following {char2}.
Mnemonic: "eXclusive (S)".
All modes
*leap_gs*
gs{char1}{char2} Jump to a labeled occurrence of {char1}{char2} in one
of the other windows on the tab page. The cursor is
placed on {char1} |exclusive|.
Mnemonic: "global s".
Special keys~
<enter> Repeat search with the previous input (after
invocation); jump to the next match in
|leap-traversal| mode; accept the selection in
|leap-multiselect| mode.
<space> Substitute for an EOL character (after the first
input); shift to the next group of labeled matches
(after the second input).
<tab> Revert the previous jump (|leap-traversal| mode) or
the previous group shift.
<backspace> Deselect the last selected target in
|leap-multiselect| mode.
<esc> Exit Leap at any stage cleanly.
==============================================================================
CUSTOM MAPPINGS *leap-custom-mappings*
<Plug> keys are aliases for right hand sides of mappings - in our case, calls
to |leap.leap()|. If you are not familiar with the concept, see |<Plug>| and
|using-<Plug>|.
Available <Plug> keys (with their corresponding argument tables):
*<Plug>(leap-forward-to)*
arguments: `{}` in Normal mode, otherwise `{ offset = +1, inclusive_op = true }`
default mapping: `s`
*<Plug>(leap-forward-till)*
arguments: `{ offset = -1, inclusive_op = true }`
default mapping: `x` (Visual and Operator-pending mode only)
*<Plug>(leap-backward-to)*
arguments: `{ backward = true }`
default mapping: `S`
*<Plug>(leap-backward-till)*
arguments: `{ backward = true, offset = 2 }`
default mapping: `X` (Visual and Operator-pending mode only)
*<Plug>(leap-from-window)*
arguments: `{ target_windows = require('leap.util').get_enterable_windows() }`
default mapping: `gs`
Example: `vim.keymap.set({'n', 'x', 'o'}, 'f', '<Plug>(leap-forward-to)')`
For creating custom motions with behaviours different from the above, see
|leap.leap()|.
The keys for repeating the search, and for switching between groups of matches
can be set via |leap.opts.special_keys|.
==============================================================================
HIGHLIGHTING *leap-highlight*
Leap uses the following highlight groups that you can configure to your own
liking (using |nvim_set_hl()|):
*LeapMatch*
Matches that can be reached directly, without having to use a label.
(By default, this is only used for traversal mode, or when a match
covers a label, indicating the conflict.)
*LeapLabelPrimary*
The character needed to be pressed to jump to the match position,
after the whole search pattern has been given. It appears once the
first input has been entered, right next to the pair.
*LeapLabelSecondary*
If the number of matches exceeds the available target labels, the next
group of labeled targets are shown with a different color. Those can
be reached by pressing `<space>` (`special_keys.next_group`)
before the label character.
*LeapLabelSelected*
Labels that are selected for the action in |leap-multiselect| mode.
*LeapBackdrop*
In some cases it might be useful or even necessary to apply certain
settings on the rest of the area, like disabling all `gui` attributes,
or adding a uniform grey foreground color, to make Leap matches and
labels more distinguishable. This group is not set by default.
Example: `vim.api.nvim_set_hl(0, 'LeapBackdrop', { fg = 'grey' })`
In order to preserve your custom settings after changing the colorscheme, you
might want to wrap them in a function, and define an autocommand like below.
(Leap does the same, but with |:hi-default|, so it will not overwrite yours.)
>
vim.api.nvim_create_autocmd('ColorScheme', {
callback = function ()
vim.api.nvim_set_hl(0, 'LeapMatch', { <def. map> })
-- etc.
end
})
==============================================================================
EVENTS *leap-events*
The |User| event is triggered with the following patterns on entering/exiting
Leap (not in traversal mode):
*LeapEnter*
*LeapLeave*
Example: >
vim.api.nvim_create_autocmd('User', {
pattern = 'LeapEnter',
command = 'nohlsearch',
})
vim.api.nvim_create_autocmd('User', {
pattern = 'LeapLeave',
command = 'let &hlsearch=&hlsearch',
})
<
==============================================================================
EXTENDING LEAP *leap-extensions*
There is more to Leap than meets the eye. On a general level, you should think
of it as less of a motion plugin and more of an engine for selecting visible
targets on the screen (acquired by arbitrary means), and doing arbitrary things
with them.
There are lots of ways you can extend the plugin and bend it to your will, and
the combinations of them give you almost infinite possibilities.
Instead of using the provided `<Plug>` keys, you can also call the `leap()`
function directly:
leap({opts}) *leap.leap()*
Entry point for all |leap.nvim| actions.
Parameters ~
{opts} Optional parameters.
• opts: A table just like |leap.opts|, to override any default
setting for the specific call.
E.g.: `require('leap').leap { opts = { labels = {} } }`
• offset: Where to land with the cursor compared to the target
position (-1, 0, 1, 2).
• inclusive_op: A flag indicating whether an operation should
behave as |inclusive|.
• backward: Search backward instead of forward in the current
window.
• target_windows: A list of windows (as |winid|s) to be
searched.
*leap-custom-targets*
• targets: Either a list of targets, or a function returning
such a list. The advantage of the latter is that the function
will be evaluated after |LeapEnter| (that is, after setting
temporary editor options, etc.), so that you can even prompt
the user for input while already "in" Leap.
The elements of the list are tables of arbitrary structure,
with the only mandatory field being `pos` - a (1,1)-indexed
tuple; this is the position of the label, and also the jump
target, if there is no custom `action` provided. If you have
targets in multiple windows, you also need to provide a
`wininfo` field for each (|getwininfo()|). Targets can
represent anything with a position, like Tree-sitter nodes,
etc.
*leap-custom-action*
• action: A Lua function that will be executed by Leap in place
of the jump. (You could obviously implement some custom jump
logic here too.) Its only argument is either a target, or a
list of targets (in multiselect mode).
*leap-multiselect*
• multiselect: A flag allowing for selecting multiple targets
for `action`. In this mode, you can just start picking labels
one after the other. You can revert the most recent pick with
`<backspace>` (`special_keys.multi_revert`), and accept the
selection with `<enter>` (`special_keys.multi_accept`).
*leap.state* *leap-runtime-args*
Accessing the arguments passed to leap() ~
The arguments of the current call are always available at runtime, in the
`state.args` table.
Using |leap-events| together with the `args` table, you can customize practically
anything on a per-call basis. Keep in mind that you can even pass arbitrary
flags when calling |leap()|:
>
function my_custom_leap_func()
require'leap'.leap { my_custom_flag = true, ... }
end
vim.api.nvim_create_autocmd('User', {
pattern = 'LeapEnter',
callback = function ()
if require'leap'.state.args.my_custom_flag then
-- Implement some special logic here, that will only apply to
-- my_custom_leap_func() (e.g., change the style of the labels),
-- and clean up with an analogous `LeapLeave` autocommand.
end
end
})
<
==============================================================================
vim:tw=78:ts=8:ft=help:norl: