-
Notifications
You must be signed in to change notification settings - Fork 180
/
mini-indentscope.txt
469 lines (383 loc) · 21.3 KB
/
mini-indentscope.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
*mini.indentscope* Visualize and work with indent scope
*MiniIndentscope*
MIT License Copyright (c) 2022 Evgeni Chasnovski
==============================================================================
Indent scope (or just "scope") is a maximum set of consecutive lines which
contains certain reference line (cursor line by default) and every member
has indent not less than certain reference indent ("indent at cursor" by
default: minimum between cursor column and indent of cursor line).
Features:
- Visualize scope with animated vertical line. It is very fast and done
automatically in a non-blocking way (other operations can be performed,
like moving cursor). You can customize debounce delay and animation rule.
- Customization of scope computation options can be done on global level
(in |MiniIndentscope.config|), for a certain buffer (using
`vim.b.miniindentscope_config` buffer variable), or within a call (using
`opts` variable in |MiniIndentscope.get_scope|).
- Customizable notion of a border: which adjacent lines with strictly lower
indent are recognized as such. This is useful for a certain filetypes
(for example, Python or plain text).
- Customizable way of line to be considered "border first". This is useful
if you want to place cursor on function header and get scope of its body.
- There are textobjects and motions to operate on scope. Support |count|
and dot-repeat (in operator pending mode).
# Setup ~
This module needs a setup with `require('mini.indentscope').setup({})`
(replace `{}` with your `config` table). It will create global Lua table
`MiniIndentscope` which you can use for scripting or manually (with `:lua
MiniIndentscope.*`).
See |MiniIndentscope.config| for available config settings.
You can override runtime config settings locally to buffer inside
`vim.b.miniindentscope_config` which should have same structure as
`MiniIndentscope.config`. See |mini.nvim-buffer-local-config| for more
details.
# Comparisons ~
- 'lukas-reineke/indent-blankline.nvim':
- Its main functionality is about showing static guides of indent levels.
- Implementation of 'mini.indentscope' is similar to
'indent-blankline.nvim' (using |extmarks| on first column to be shown
even on blank lines). They can be used simultaneously, but it will
lead to one of the visualizations being on top (hiding) of another.
# Highlight groups ~
* `MiniIndentscopeSymbol` - symbol showing on every line of scope if its
indent is multiple of 'shiftwidth'.
* `MiniIndentscopeSymbolOff` - symbol showing on every line of scope if its
indent is not multiple of 'shiftwidth'.
Default: links to `MiniIndentscopeSymbol`.
To change any highlight group, modify it directly with |:highlight|.
# Disabling ~
To disable autodrawing, set `vim.g.miniindentscope_disable` (globally) or
`vim.b.miniindentscope_disable` (for a buffer) to `true`. Considering high
number of different scenarios and customization intentions, writing exact
rules for disabling module's functionality is left to user. See
|mini.nvim-disabling-recipes| for common recipes.
------------------------------------------------------------------------------
*MiniIndentscope-drawing*
Drawing of scope indicator
Draw of scope indicator is done as iterative animation. It has the
following design:
- Draw indicator on origin line (where cursor is at) immediately. Indicator
is visualized as `MiniIndentscope.config.symbol` placed to the right of
scope's border indent. This creates a line from top to bottom scope edges.
- Draw upward and downward concurrently per one line. Progression by one
line in both direction is considered to be one step of animation.
- Before each step wait certain amount of time, which is decided by
"animation function". It takes next and total step numbers (both are one
or bigger) and returns number of milliseconds to wait before drawing next
step. Comparing to a more popular "easing functions" in animation (input:
duration since animation start; output: percent of animation done), it is
a discrete inverse version of its derivative. Such interface proved to be
more appropriate for kind of task at hand.
Special cases ~
- When scope to be drawn intersects (same indent, ranges overlap) currently
visible one (at process or finished drawing), drawing is done immediately
without animation. With most common example being typing new text, this
feels more natural.
- Scope for the whole buffer is not drawn as it is isually redundant.
Technically, it can be thought as drawn at column 0 (because border
indent is -1) which is not visible.
------------------------------------------------------------------------------
*MiniIndentscope.setup()*
`MiniIndentscope.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniIndentscope.config|.
Usage ~
>lua
require('mini.indentscope').setup() -- use default config
-- OR
require('mini.indentscope').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniIndentscope.config*
`MiniIndentscope.config`
Module config
Default values:
>lua
MiniIndentscope.config = {
-- Draw options
draw = {
-- Delay (in ms) between event and start of drawing scope indicator
delay = 100,
-- Animation rule for scope's first drawing. A function which, given
-- next and total step numbers, returns wait time (in ms). See
-- |MiniIndentscope.gen_animation| for builtin options. To disable
-- animation, use `require('mini.indentscope').gen_animation.none()`.
animation = --<function: implements constant 20ms between steps>,
-- Symbol priority. Increase to display on top of more symbols.
priority = 2,
},
-- Module mappings. Use `''` (empty string) to disable one.
mappings = {
-- Textobjects
object_scope = 'ii',
object_scope_with_border = 'ai',
-- Motions (jump to respective border line; if not present - body line)
goto_top = '[i',
goto_bottom = ']i',
},
-- Options which control scope computation
options = {
-- Type of scope's border: which line(s) with smaller indent to
-- categorize as border. Can be one of: 'both', 'top', 'bottom', 'none'.
border = 'both',
-- Whether to use cursor column when computing reference indent.
-- Useful to see incremental scopes with horizontal cursor movements.
indent_at_cursor = true,
-- Whether to first check input line to be a border of adjacent scope.
-- Use it if you want to place cursor on function header to get scope of
-- its body.
try_as_border = false,
},
-- Which character to use for drawing scope indicator
symbol = '╎',
}
<
# Options ~
- Options can be supplied globally (from this `config`), locally to buffer
(via `options` field of `vim.b.miniindentscope_config` buffer variable),
or locally to call (as argument to |MiniIndentscope.get_scope()|).
- Option `border` controls which line(s) with smaller indent to categorize
as border. This matters for textobjects and motions.
It also controls how empty lines are treated: they are included in scope
only if followed by a border. Another way of looking at it is that indent
of blank line is computed based on value of `border` option.
Here is an illustration of how `border` works in presence of empty lines: >
|both|bottom|top|none|
1|function foo() | 0 | 0 | 0 | 0 |
2| | 4 | 0 | 4 | 0 |
3| print('Hello world') | 4 | 4 | 4 | 4 |
4| | 4 | 4 | 2 | 2 |
5| end | 2 | 2 | 2 | 2 |
<
Numbers inside a table are indent values of a line computed with certain
value of `border`. So, for example, a scope with reference line 3 and
right-most column has body range depending on value of `border` option:
- `border` is "both": range is 2-4, border is 1 and 5 with indent 2.
- `border` is "top": range is 2-3, border is 1 with indent 0.
- `border` is "bottom": range is 3-4, border is 5 with indent 0.
- `border` is "none": range is 3-3, border is empty with indent `nil`.
- Option `indent_at_cursor` controls if cursor position should affect
computation of scope. If `true`, reference indent is a minimum of
reference line's indent and cursor column. In main example, here how
scope's body range differs depending on cursor column and `indent_at_cursor`
value (assuming cursor is on line 3 and it is whole buffer): >
Column\Option true|false
1 and 2 2-5 | 2-4
3 and more 2-4 | 2-4
<
- Option `try_as_border` controls how to act when input line can be
recognized as a border of some neighbor indent scope. In main example,
when input line is 1 and can be recognized as border for inner scope,
value `try_as_border = true` means that inner scope will be returned.
Similar, for input line 5 inner scope will be returned if it is
recognized as border.
------------------------------------------------------------------------------
*MiniIndentscope.get_scope()*
`MiniIndentscope.get_scope`({line}, {col}, {opts})
Compute indent scope
Indent scope (or just "scope") is a maximum set of consecutive lines which
contains certain reference line (cursor line by default) and every member
has indent not less than certain reference indent ("indent at column" by
default). Here "indent at column" means minimum between input column value
and indent of reference line. When using cursor column, this allows for a
useful interactive view of nested indent scopes by making horizontal
movements within line.
Options controlling actual computation is taken from these places in order:
- Argument `opts`. Use it to ensure independence from other sources.
- Buffer local variable `vim.b.miniindentscope_config` (`options` field).
Useful to define local behavior (for example, for a certain filetype).
- Global options from |MiniIndentscope.config|.
Algorithm overview ~
- Compute reference "indent at column". Reference line is an input `line`
which might be modified to one of its neighbors if `try_as_border` option
is `true`: if it can be viewed as border of some neighbor scope, it will.
- Process upwards and downwards from reference line to search for line with
indent strictly less than reference one. This is like casting rays up and
down from reference line and reference indent until meeting "a wall"
(character to the right of indent or buffer edge). Latest line before
meeting is a respective end of scope body. It always exists because
reference line is a such one.
- Based on top and bottom lines with strictly lower indent, construct
scopes's border. The way it is computed is decided based on `border`
option (see |MiniIndentscope.config| for more information).
- Compute border indent as maximum indent of border lines (or reference
indent minus one in case of no border). This is used during drawing
visual indicator.
Indent computation ~
For every line indent is intended to be computed unambiguously:
- For "normal" lines indent is an output of |indent()|.
- Indent is `-1` for imaginary lines 0 and past last line.
- For blank and empty lines indent is computed based on previous
(|prevnonblank()|) and next (|nextnonblank()|) non-blank lines. The way
it is computed is decided based on `border` in order to not include blank
lines at edge of scope's body if there is no border there. See
|MiniIndentscope.config| for a details example.
Parameters ~
{line} `(number|nil)` Input line number (starts from 1). Can be modified to a
neighbor if `try_as_border` is `true`. Default: cursor line.
{col} `(number|nil)` Column number (starts from 1). Default: if
`indent_at_cursor` option is `true` - cursor column from `curswant` of
|getcurpos()| (allows for more natural behavior on empty lines);
`math.huge` otherwise in order to not incorporate cursor in computation.
{opts} `(table|nil)` Options to override global or buffer local ones (see
|MiniIndentscope.config|).
Return ~
`(table)` Table with scope information:
- <body> - table with <top> (top line of scope, inclusive), <bottom>
(bottom line of scope, inclusive), and <indent> (minimum indent within
scope) keys. Line numbers start at 1.
- <border> - table with <top> (line of top border, might be `nil`),
<bottom> (line of bottom border, might be `nil`), and <indent> (indent
of border) keys. Line numbers start at 1.
- <buf_id> - identifier of current buffer.
- <reference> - table with <line> (reference line), <column> (reference
column), and <indent> ("indent at column") keys.
------------------------------------------------------------------------------
*MiniIndentscope.draw()*
`MiniIndentscope.draw`({scope}, {opts})
Draw scope manually
Scope is visualized as a vertical line within scope's body range at column
equal to border indent plus one (or body indent if border is absent).
Numbering starts from one.
Parameters ~
{scope} `(table|nil)` Scope. Default: output of |MiniIndentscope.get_scope|
with default arguments.
{opts} `(table|nil)` Options. Currently supported:
- <animation_fun> - animation function for drawing. See
|MiniIndentscope-drawing| and |MiniIndentscope.gen_animation|.
- <priority> - priority number for visualization. See `priority` option
for |nvim_buf_set_extmark()|.
------------------------------------------------------------------------------
*MiniIndentscope.undraw()*
`MiniIndentscope.undraw`()
Undraw currently visible scope manually
------------------------------------------------------------------------------
*MiniIndentscope.gen_animation*
`MiniIndentscope.gen_animation`
Generate builtin animation function
This is a builtin source to generate animation function for usage in
`MiniIndentscope.config.draw.animation`. Most of them are variations of
common easing functions, which provide certain type of progression for
revealing scope visual indicator.
Each field corresponds to one family of progression which can be customized
further by supplying appropriate arguments.
Examples ~
- Don't use animation: `MiniIndentscope.gen_animation.none()`
- Use quadratic "out" easing with total duration of 1000 ms: >lua
gen_animation.quadratic({ easing = 'out', duration = 1000, unit = 'total' })
<
See also ~
|MiniIndentscope-drawing| for more information about how drawing is done.
------------------------------------------------------------------------------
*MiniIndentscope.gen_animation.none()*
`MiniIndentscope.gen_animation.none`()
Generate no animation
Show indicator immediately. Same as animation function always returning 0.
------------------------------------------------------------------------------
*MiniIndentscope.gen_animation.linear()*
`MiniIndentscope.gen_animation.linear`({opts})
Generate linear progression
Parameters ~
{opts} `(table|nil)` Options that control progression. Possible keys:
- <easing> `(string)` - a subtype of progression. One of "in"
(accelerating from zero speed), "out" (decelerating to zero speed),
"in-out" (default; accelerating halfway, decelerating after).
- <duration> `(number)` - duration (in ms) of a unit. Default: 20.
- <unit> `(string)` - which unit's duration `opts.duration` controls. One
of "step" (default; ensures average duration of step to be `opts.duration`)
or "total" (ensures fixed total duration regardless of scope's range).
Return ~
`(function)` Animation function (see |MiniIndentscope-drawing|).
------------------------------------------------------------------------------
*MiniIndentscope.gen_animation.quadratic()*
`MiniIndentscope.gen_animation.quadratic`({opts})
Generate quadratic progression
Parameters ~
{opts} `(table|nil)` Options that control progression. Possible keys:
- <easing> `(string)` - a subtype of progression. One of "in"
(accelerating from zero speed), "out" (decelerating to zero speed),
"in-out" (default; accelerating halfway, decelerating after).
- <duration> `(number)` - duration (in ms) of a unit. Default: 20.
- <unit> `(string)` - which unit's duration `opts.duration` controls. One
of "step" (default; ensures average duration of step to be `opts.duration`)
or "total" (ensures fixed total duration regardless of scope's range).
Return ~
`(function)` Animation function (see |MiniIndentscope-drawing|).
------------------------------------------------------------------------------
*MiniIndentscope.gen_animation.cubic()*
`MiniIndentscope.gen_animation.cubic`({opts})
Generate cubic progression
Parameters ~
{opts} `(table|nil)` Options that control progression. Possible keys:
- <easing> `(string)` - a subtype of progression. One of "in"
(accelerating from zero speed), "out" (decelerating to zero speed),
"in-out" (default; accelerating halfway, decelerating after).
- <duration> `(number)` - duration (in ms) of a unit. Default: 20.
- <unit> `(string)` - which unit's duration `opts.duration` controls. One
of "step" (default; ensures average duration of step to be `opts.duration`)
or "total" (ensures fixed total duration regardless of scope's range).
Return ~
`(function)` Animation function (see |MiniIndentscope-drawing|).
------------------------------------------------------------------------------
*MiniIndentscope.gen_animation.quartic()*
`MiniIndentscope.gen_animation.quartic`({opts})
Generate quartic progression
Parameters ~
{opts} `(table|nil)` Options that control progression. Possible keys:
- <easing> `(string)` - a subtype of progression. One of "in"
(accelerating from zero speed), "out" (decelerating to zero speed),
"in-out" (default; accelerating halfway, decelerating after).
- <duration> `(number)` - duration (in ms) of a unit. Default: 20.
- <unit> `(string)` - which unit's duration `opts.duration` controls. One
of "step" (default; ensures average duration of step to be `opts.duration`)
or "total" (ensures fixed total duration regardless of scope's range).
Return ~
`(function)` Animation function (see |MiniIndentscope-drawing|).
------------------------------------------------------------------------------
*MiniIndentscope.gen_animation.exponential()*
`MiniIndentscope.gen_animation.exponential`({opts})
Generate exponential progression
Parameters ~
{opts} `(table|nil)` Options that control progression. Possible keys:
- <easing> `(string)` - a subtype of progression. One of "in"
(accelerating from zero speed), "out" (decelerating to zero speed),
"in-out" (default; accelerating halfway, decelerating after).
- <duration> `(number)` - duration (in ms) of a unit. Default: 20.
- <unit> `(string)` - which unit's duration `opts.duration` controls. One
of "step" (default; ensures average duration of step to be `opts.duration`)
or "total" (ensures fixed total duration regardless of scope's range).
Return ~
`(function)` Animation function (see |MiniIndentscope-drawing|).
------------------------------------------------------------------------------
*MiniIndentscope.move_cursor()*
`MiniIndentscope.move_cursor`({side}, {use_border}, {scope})
Move cursor within scope
Cursor is placed on a first non-blank character of target line.
Parameters ~
{side} `(string)` One of "top" or "bottom".
{use_border} `(boolean|nil)` Whether to move to border or within scope's body.
If particular border is absent, body is used.
{scope} `(table|nil)` Scope to use. Default: output of |MiniIndentscope.get_scope()|.
------------------------------------------------------------------------------
*MiniIndentscope.operator()*
`MiniIndentscope.operator`({side}, {add_to_jumplist})
Function for motion mappings
Move to a certain side of border. Respects |count| and dot-repeat (in
operator-pending mode). Doesn't move cursor for scope that is not shown
(drawing indent less that zero).
Parameters ~
{side} `(string)` One of "top" or "bottom".
{add_to_jumplist} `(boolean|nil)` Whether to add movement to jump list. It is
`true` only for Normal mode mappings.
------------------------------------------------------------------------------
*MiniIndentscope.textobject()*
`MiniIndentscope.textobject`({use_border})
Function for textobject mappings
Respects |count| and dot-repeat (in operator-pending mode). Doesn't work
for scope that is not shown (drawing indent less that zero).
Parameters ~
{use_border} `(boolean|nil)` Whether to include border in textobject. When
`true` and `try_as_border` option is `false`, allows "chaining" calls for
incremental selection.
vim:tw=78:ts=8:noet:ft=help:norl: