397 lines
18 KiB
Plaintext
397 lines
18 KiB
Plaintext
|
==============================================================================
|
||
|
------------------------------------------------------------------------------
|
||
|
*mini.indentscope*
|
||
|
*MiniIndentscope*
|
||
|
Visualize and operate on 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 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.
|
||
|
* `MiniIndentscopePrefix` - space before symbol. By default made so as to
|
||
|
appear as nothing is displayed.
|
||
|
|
||
|
To change any highlight group, modify it directly with |:highlight|.
|
||
|
|
||
|
# Disabling~
|
||
|
|
||
|
To disable autodrawing, set `g:miniindentscope_disable` (globally) or
|
||
|
`b:miniindentscope_disable` (for a buffer) to `v: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)` Module config table. See |MiniIndentscope.config|.
|
||
|
|
||
|
Usage~
|
||
|
`require('mini.indentscope').setup({})` (replace `{}` with your `config` table)
|
||
|
|
||
|
------------------------------------------------------------------------------
|
||
|
*MiniIndentscope.config*
|
||
|
`MiniIndentscope.config`
|
||
|
Module config
|
||
|
|
||
|
Default values:
|
||
|
>
|
||
|
MiniIndentscope.config = {
|
||
|
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>,
|
||
|
},
|
||
|
|
||
|
-- 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 presense 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)` Input line number (starts from 1). Can be modified to a
|
||
|
neighbor if `try_as_border` is `true`. Default: cursor line.
|
||
|
{col} `(number)` 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)` 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 withing
|
||
|
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.auto_draw()*
|
||
|
`MiniIndentscope.auto_draw`({opts})
|
||
|
Auto draw scope indicator based on movement events
|
||
|
|
||
|
Designed to be used with |autocmd|. No need to use it directly, everything
|
||
|
is setup in |MiniIndentscope.setup|.
|
||
|
|
||
|
Parameters~
|
||
|
{opts} `(table)` Options.
|
||
|
|
||
|
------------------------------------------------------------------------------
|
||
|
*MiniIndentscope.draw()*
|
||
|
`MiniIndentscope.draw`({scope}, {opts})
|
||
|
Draw scope manually
|
||
|
|
||
|
Scope is visualized as a vertical line withing 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)` Scope. Default: output of |MiniIndentscope.get_scope|
|
||
|
with default arguments.
|
||
|
{opts} `(table)` Options. Currently supported:
|
||
|
- <animation_fun> - animation function for drawing. See
|
||
|
|MiniIndentscope-drawing| and |MiniIndentscope.gen_animation()|.
|
||
|
|
||
|
------------------------------------------------------------------------------
|
||
|
*MiniIndentscope.undraw()*
|
||
|
`MiniIndentscope.undraw`()
|
||
|
Undraw currently visible scope manually
|
||
|
|
||
|
------------------------------------------------------------------------------
|
||
|
*MiniIndentscope.gen_animation()*
|
||
|
`MiniIndentscope.gen_animation`({easing}, {opts})
|
||
|
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.
|
||
|
|
||
|
Supported easing types:
|
||
|
- `'none'` - show indicator immediately. Equivalent to animation function
|
||
|
always returning 0.
|
||
|
- `'linear'` - linear progression.
|
||
|
- Quadratic progression:
|
||
|
- `'quadraticIn'` - accelerating from zero speed.
|
||
|
- `'quadraticOut'` - decelerating to zero speed.
|
||
|
- `'quadraticInOut'` - accelerating halfway, decelerating after.
|
||
|
- Cubic progression:
|
||
|
- `'cubicIn'` - accelerating from zero speed.
|
||
|
- `'cubicOut'` - decelerating to zero speed.
|
||
|
- `'cubicInOut'` - accelerating halfway, decelerating after.
|
||
|
- Quartic progression:
|
||
|
- `'quarticIn'` - accelerating from zero speed.
|
||
|
- `'quarticOut'` - decelerating to zero speed.
|
||
|
- `'quarticInOut'` - accelerating halfway, decelerating after.
|
||
|
- Exponential progression:
|
||
|
- `'exponentialIn'` - accelerating from zero speed.
|
||
|
- `'exponentialOut'` - decelerating to zero speed.
|
||
|
- `'exponentialInOut'` - accelerating halfway, decelerating after.
|
||
|
|
||
|
Customization of duration and other general behavior of output animation
|
||
|
function is done through `opts` argument.
|
||
|
|
||
|
Parameters~
|
||
|
{easing} `(string)` One of supported easing types.
|
||
|
{opts} `(table)` Options that control progression. Possible keys:
|
||
|
- <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|).
|
||
|
|
||
|
Examples~
|
||
|
- Don't use animation: `gen_animation('none')`
|
||
|
- Use quadratic "out" easing with total duration of 1000 ms:
|
||
|
`gen_animation('quadraticOut', { duration = 1000, unit = 'total' })`
|
||
|
|
||
|
See also~
|
||
|
|MiniIndentscope-drawing| for more information about how drawing is done.
|
||
|
|
||
|
------------------------------------------------------------------------------
|
||
|
*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)` Whether to move to border or withing scope's body.
|
||
|
If particular border is absent, body is used.
|
||
|
{scope} `(table)` 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)` 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)` 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:
|