*mini.bracketed* Go forward/backward with square brackets

MIT License Copyright (c) 2023 Evgeni Chasnovski

------------------------------------------------------------------------------
                                                                 *MiniBracketed*
Features:
- Configurable Lua functions to go forward/backward to a certain target.
  Each function can be customized with:
    - Direction. One of "forward", "backward", "first" (forward starting
      from first one), "last" (backward starting from last one).
    - Number of times to go.
    - Whether to wrap on edges (going forward on last one goes to first).
    - Some other target specific options.

- Mappings using square brackets. They are created using configurable
  target suffix and can be selectively disabled.

  Each mapping supports |[count]|. Mappings are created in Normal mode; for
  targets which move cursor in current buffer also Visual and
  Operator-pending (with dot-repeat) modes are supported.

  Using `lower-suffix` and `upper-suffix` (lower and upper case suffix) for
  a single target the following mappings are created:
    - `[` + `upper-suffix` : go first.
    - `[` + `lower-suffix` : go backward.
    - `]` + `lower-suffix` : go forward.
    - `]` + `upper-suffix` : go last.

- Supported targets (for more information see help for corresponding Lua
  function):

  `Target`                           `Mappings`         `Lua function`
  Buffer ......................... `[B` `[b` `]b` `]B` .... |MiniBracketed.buffer()|
  Comment block .................. `[C` `[c` `]c` `]C` .... |MiniBracketed.comment()|
  Conflict marker ................ `[X` `[x` `]x` `]X` .... |MiniBracketed.conflict()|
  Diagnostic ..................... `[D` `[d` `]d` `]D` .... |MiniBracketed.diagnostic()|
  File on disk ................... `[F` `[f` `]f` `]F` .... |MiniBracketed.file()|
  Indent change .................. `[I` `[i` `]i` `]I` .... |MiniBracketed.indent()|
  Jump inside current buffer ..... `[J` `[j` `]j` `]J` .... |MiniBracketed.jump()|
  Location from |location-list| .... `[L` `[l` `]l` `]L` .... |MiniBracketed.location()|
  Old files ...................... `[O` `[o` `]o` `]O` .... |MiniBracketed.oldfile()|
  Quickfix entry from |Quickfix| ... `[Q` `[q` `]q` `]Q` .... |MiniBracketed.quickfix()|
  Tree-sitter node and parents ... `[T` `[t` `]t` `]T` .... |MiniBracketed.treesitter()|
  Undo from linear history ....... `[U` `[u` `]u` `]U` .... |MiniBracketed.undo()|
  Window in current tab .......... `[W` `[w` `]w` `]W` .... |MiniBracketed.window()|
  Yank entry over put region ..... `[Y` `[y` `]y` `]Y` .... |MiniBracketed.yank()|

Notes:
- The `undo` target remaps |u| and |CTRL-R| keys to register undo state
  after undo and redo respectively. If this conflicts with your setup,
  either disable `undo` target or make your remaps after calling
  |MiniBracketed.setup()|. To use `undo` target, remap your undo/redo keys
  to call |MiniBracketed.register_undo_state()| after the action.

# Setup ~

This module needs a setup with `require('mini.bracketed').setup({})` (replace
`{}` with your `config` table). It will create global Lua table `MiniBracketed`
which you can use for scripting or manually (with `:lua MiniBracketed.*`).

See |MiniBracketed.config| for available config settings.

You can override runtime config settings (like target options) locally
to buffer inside `vim.b.minibracketed_config` which should have same structure
as `MiniBracketed.config`. See |mini.nvim-buffer-local-config| for more details.

# Comparisons ~

- [tpope/vim-unimpaired](https://github.com/tpope/vim-unimpaired):
    - Supports buffer, conflict, file, location, and quickfix targets mostly
      via built-in commands (like |:bprevious|, etc.) without configuration.
    - Supports files from argument list and tags. This module does not.
    - Doesn't support most other this module's targets (comment, indent, ...).
- |mini.indentscope|:
    - Target |MiniBracketed.indent()| target can go to "first" and "last"
      indent change. It also can go not only to line with smaller indent,
      but also bigger or different one.
    - Mappings from 'mini.indentscope' have more flexibility in computation of
      indent scope, like how to treat empty lines near border or whether to
      compute indent at cursor.

# Disabling ~

To disable, set `vim.g.minibracketed_disable` (globally) or
`vim.b.minibracketed_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.

------------------------------------------------------------------------------
                                                         *MiniBracketed.setup()*
                        `MiniBracketed.setup`({config})
Module setup

Parameters ~
{config} `(table|nil)` Module config table. See |MiniBracketed.config|.

Usage ~
>lua
  require('mini.bracketed').setup() -- use default config
  -- OR
  require('mini.bracketed').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
                                                          *MiniBracketed.config*
                             `MiniBracketed.config`
Defaults ~
>lua
  MiniBracketed.config = {
    -- First-level elements are tables describing behavior of a target:
    --
    -- - <suffix> - single character suffix. Used after `[` / `]` in mappings.
    --   For example, with `b` creates `[B`, `[b`, `]b`, `]B` mappings.
    --   Supply empty string `''` to not create mappings.
    --
    -- - <options> - table overriding target options.
    --
    -- See `:h MiniBracketed.config` for more info.

    buffer     = { suffix = 'b', options = {} },
    comment    = { suffix = 'c', options = {} },
    conflict   = { suffix = 'x', options = {} },
    diagnostic = { suffix = 'd', options = {} },
    file       = { suffix = 'f', options = {} },
    indent     = { suffix = 'i', options = {} },
    jump       = { suffix = 'j', options = {} },
    location   = { suffix = 'l', options = {} },
    oldfile    = { suffix = 'o', options = {} },
    quickfix   = { suffix = 'q', options = {} },
    treesitter = { suffix = 't', options = {} },
    undo       = { suffix = 'u', options = {} },
    window     = { suffix = 'w', options = {} },
    yank       = { suffix = 'y', options = {} },
  }
<
Options ~

Each entry configures target with the same name and can have data configuring
mapping suffix and target options.

Example of configuration: >lua

  require('mini.bracketed').setup({
    -- Map [N, [n, ]n, ]N for conflict marker like in 'tpope/vim-unimpaired'
    conflict = { suffix = 'n' },

    -- Make diagnostic advance only by errors
    diagnostic = { options = { severity = vim.diagnostic.severity.ERROR } },

    -- Disable creation of mappings for `indent` target (for example,
    -- in favor of ones from |mini.indentscope|)
    indent = { suffix = '' },

    -- Disable mappings for `window` target in favor of custom ones
    window = { suffix = '' },
  })

  -- Create custom `window` mappings
  local map = vim.keymap.set
  map('n', '<Leader>wH', "<Cmd>lua MiniBracketed.window('first')<CR>")
  map('n', '<Leader>wh', "<Cmd>lua MiniBracketed.window('backward')<CR>")
  map('n', '<Leader>wl', "<Cmd>lua MiniBracketed.window('forward')<CR>")
  map('n', '<Leader>wL', "<Cmd>lua MiniBracketed.window('last')<CR>")
<
## Suffix ~

The `suffix` key is used to create target mappings.

Supply empty string to disable mapping creation for that particular target.
To create a completely different mapping (like with |<Leader>|) use target
function manually.

Using `lower-suffix` and `upper-suffix` (lower and upper case suffix) for
a single target the following mappings are created:
- `[` + `upper-suffix` : go first.
- `[` + `lower-suffix` : go backward.
- `]` + `lower-suffix` : go forward.
- `]` + `upper-suffix` : go last.

When supplied with a non-letter, only forward/backward mappings are created.

## Options ~

The `options` key is directly forwarded as `opts` to corresponding Lua function.

------------------------------------------------------------------------------
                                                        *MiniBracketed.buffer()*
                  `MiniBracketed.buffer`({direction}, {opts})
Listed buffer

Go to next/previous listed buffer. Order by their number (see |bufnr()|).

Direction "forward" increases number, "backward" - decreases.

Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
  - <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
  - <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.

------------------------------------------------------------------------------
                                                       *MiniBracketed.comment()*
                  `MiniBracketed.comment`({direction}, {opts})
Comment block

Go to next/previous comment block. Only linewise comments using
'commentsring' are recognized.

Direction "forward" increases line number, "backward" - decreases.

Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
  - <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
  - <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.
  - <add_to_jumplist> (`boolean`) - Whether to add current position to jumplist.
    Default: `false`.
  - <block_side> `(string)` - which side of comment block to use. One of
    "near" (default; use nearest side), "start" (use first line), "end"
    (use last line), "both" (use both first and last lines).

------------------------------------------------------------------------------
                                                      *MiniBracketed.conflict()*
                 `MiniBracketed.conflict`({direction}, {opts})
Git conflict marker

Go to next/previous lines containing Git conflict marker. That is, if it
starts with "<<<<<<< ", ">>>>>>> ", or is "=======".

Direction "forward" increases line number, "backward" - decreases.

Notes:
- Using this target in Operator-pending mode allows the following approach
  at resolving merge conflicts:
    - Place cursor on `=======` line.
    - Execute one of these: `d]x[xdd` (choose upper part) or
      `d[x]xdd` (choose lower part).

Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
  - <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
  - <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.
  - <add_to_jumplist> (`boolean`) - Whether to add current position to jumplist.
    Default: `false`.

------------------------------------------------------------------------------
                                                    *MiniBracketed.diagnostic()*
                `MiniBracketed.diagnostic`({direction}, {opts})
Diagnostic

Go to next/previous diagnostic. This is mostly similar to built-in
|vim.diagnostic.jump()| (on Neovim<0.11 it is |vim.diagnostic.goto_next()| and
|vim.diagnostic.goto_prev()|) which has an interface and behavior
consistent with other methods of the module.

Direction "forward" increases line number, "backward" - decreases.

Notes:
- Using `severity` option, this target can be used in mappings like "go to
  next/previous error" (), etc. Using code similar to this: >lua

  local severity_error = vim.diagnostic.severity.ERROR
  -- Use these inside custom mappings
  MiniBracketed.diagnostic('forward', { severity = severity_error })
  MiniBracketed.diagnostic('backward', { severity = severity_error })
<
Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
  - <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
  - <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.
  - <float> `(boolean|table)` - control floating window after movement.
    For available values see |vim.diagnostic.goto_next()|.
  - <severity> `(string|table)` - which severity to use.
    For available values see |diagnostic-severity|.

------------------------------------------------------------------------------
                                                          *MiniBracketed.file()*
                   `MiniBracketed.file`({direction}, {opts})
File on disk

Go to next/previous file on disk alphabetically. Files are taken from
directory of file in current buffer (or current working directory if buffer
doesn't contain a readable file). Only first-level files are used, i.e. it
doesn't go inside subdirectories.

Direction "forward" goes forward alphabetically, "backward" - backward.

Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
  - <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
  - <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.

------------------------------------------------------------------------------
                                                        *MiniBracketed.indent()*
                  `MiniBracketed.indent`({direction}, {opts})
Indent change

Go to next/previous line with different indent (see |indent()|).
Can be used to go to lines with smaller, bigger, or different indent.

Notes:
- Directions "first" and "last" work differently from most other targets
  for performance reasons. They are essentially "backward" and "forward"
  with very big `n_times` option.
- For similar reasons, `wrap` is not supported.
- Blank line inherit indent from near non-blank line in direction of movement.

Direction "forward" increases line number, "backward" - decreases.

Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
  - <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
  - <add_to_jumplist> (`boolean`) - Whether to add current position to jumplist.
    Default: `false`.
  - <change_type> `(string)` - which type of indent change to use.
    One of "less" (default; smaller indent), "more" (bigger indent),
    "diff" (different indent).

------------------------------------------------------------------------------
                                                          *MiniBracketed.jump()*
                   `MiniBracketed.jump`({direction}, {opts})
Jump inside current buffer

Go to next/previous jump from |jumplist| which is inside current buffer.

Notes:
- There are no Visual mode mappings due to implementation problems.

Direction "forward" increases jump number, "backward" - decreases.

Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
  - <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
  - <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.

------------------------------------------------------------------------------
                                                      *MiniBracketed.location()*
                 `MiniBracketed.location`({direction}, {opts})
Location from location list

Go to next/previous location from |location-list|. This is similar to
|:lfirst|, |:lprevious|, |:lnext|, and |:llast| but with support of
wrapping around edges and |[count]| for "first"/"last" direction.

Direction "forward" increases location number, "backward" - decreases.

Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
  - <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
  - <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.

------------------------------------------------------------------------------
                                                       *MiniBracketed.oldfile()*
                  `MiniBracketed.oldfile`({direction}, {opts})
Old files from previous and current sessions

Go to older/newer readable file either from previous session (see |v:oldfiles|)
or the current one (updated automatically after |MiniBracketed.setup()| call).

Direction "forward" goes to more recent files, "backward" - to older.

Notes:
- In current session it tracks only normal buffers (see |'buftype'|) for
  some readable file.
- No new file is tracked when advancing this target. Only after buffer
  change is done not through this target (like with |MiniBracketed.buffer()|),
  it updates recency of last advanced and new buffers.

Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
  - <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
  - <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.

------------------------------------------------------------------------------
                                                      *MiniBracketed.quickfix()*
                 `MiniBracketed.quickfix`({direction}, {opts})
Quickfix from quickfix list

Go to next/previous entry from |quickfix| list. This is similar to
|:cfirst|, |:cprevious|, |:cnext|, and |:clast| but with support of
wrapping around edges and |[count]| for "first"/"last" direction.

Direction "forward" increases location number, "backward" - decreases.

Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
  - <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
  - <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.

------------------------------------------------------------------------------
                                                    *MiniBracketed.treesitter()*
                `MiniBracketed.treesitter`({direction}, {opts})
Tree-sitter node

Go to end/start of current tree-sitter node and its parents (except root).

Notes:
- Requires loaded tree-sitter parser in the current buffer.
- Directions "first" and "last" work differently from most other targets
  for performance reasons. They are essentially "backward" and "forward"
  with very big `n_times` option.
- For similar reasons, `wrap` is not supported.

Direction "forward" moves cursor forward to node's end, "backward" - backward
to node's start.

Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
  - <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
  - <add_to_jumplist> (`boolean`) - Whether to add current position to jumplist.
    Default: `false`.

------------------------------------------------------------------------------
                                                          *MiniBracketed.undo()*
                   `MiniBracketed.undo`({direction}, {opts})
Undo along a tracked linear history

In a nutshell:
- Keys |u| and |CTRL-R| (although remapped) can be used as usual, but every
  their execution new state is recorded in this module's linear undo history.
- Advancing this target goes along linear undo history revealing undo states
  **in order they actually appeared**.
- One big difference with built-in methods is that tracked linear history
  can repeat undo states (not consecutively, though).

Neovim's default way of managing undo history is through branches (see
|undo-branches|). Basically it means that if you undo several changes and then
make new ones, it creates new undo branch while usually (see |'undolevels'|)
saving previous buffer states in another branch. While there are commands
to navigate by time of undo state creation (like |:earlier| and |:later|),
there is no intuitive way to cycle through them. Existing |g-| and |g+|
cycle through undo states **based on their creation time**, which often
gets confusing really guickly in extensively edited buffer.

This `undo()` target provides a way to cycle through linear undo history
**in order states actually appeared**. It does so by registering any new undo
states plus every time |MiniBracketed.register_undo_state()| is called. To have
more "out of the box" experience, |u| and |CTRL-R| are remapped to call it after
they perform their undo/redo.

Example:

To show more clearly the difference between advancing this target and using
built-in functionality, here is an example:

- Create undo history in a new buffer (|:new|):
    - Enter `one two three` text.
    - Delete first word with `daw` and undo the change with `u`.
    - Delete second word with `daw` and undo the change with `u`.
    - Delete third word with `daw` and undo the change with `u`.

- Now try one of the following (each one after performing previous steps in
  separate new buffer):
    - Press `u`. It goes back to empty buffer. Press `<C-R>` twice and it
      goes to the latest change (`one two`). No way to get to other states
      (like `two three` or `one three`) with these two keys.

    - Press `g-`. It goes to an empty buffer. Press `g+` 4 times. It cycles
      through all available undo states **in order they were created**.

    - Finally, press `[u`. It goes back to `one two` - state which was
      **previously visited** by the user. Another `[u` restores `one two three`.
      Use `]U` to go to latest visited undo state.

Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
  - <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
  - <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.

------------------------------------------------------------------------------
                                           *MiniBracketed.register_undo_state()*
                     `MiniBracketed.register_undo_state`()
Register state for undo target

Use this function to add current undo state to this module's linear undo
history. It is used in |MiniBracketed.setup()| to remap |u| and |CTRL-R| keys
to add their new state to linear undo history.

------------------------------------------------------------------------------
                                                        *MiniBracketed.window()*
                  `MiniBracketed.window`({direction}, {opts})
Normal window

Go to next/previous normal window. Order by their number (see |winnr()|).

Direction "forward" increases window number, "backward" - decreases.

Only normal (non-floating) windows are used.

Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
  - <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
  - <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.

------------------------------------------------------------------------------
                                                          *MiniBracketed.yank()*
                   `MiniBracketed.yank`({direction}, {opts})
Replace "latest put region" with yank history entry

After |MiniBracketed.setup()| is called, on every yank/delete/change operation
(technically, every trigger of |TextYankPost| event) the object of operation
is added to yank history. Advancing this target will replace the region of
latest put operation with entry from yank history.

By default works best if called **right after** text paste (like with |p| or |P|).

To better detect "latest put region", use |MiniBracketed.register_put_region()|
as described later.

Direction "forward" goes to newer yank history entry, "backward" - to older.

Example:

- Type `one two three`.
- Yank each word with `yiw`.
- Create new line and press `p`. This should paste `three`.
- Type `[y`. This should replace latest `three` with `two`.

# Latest put region ~

"Latest put region" is (in order of decreasing priority):
- The one from latest advance of this target.
- The one registered by user with |MiniBracketed.register_put_region()|.
- The one taken from |'[| and |']| marks.

For users there are these approaches to manage which region will be used:
- Do nothing. In this case region between `[` / `]` marks will always be used
  for first `yank` advance.
  Although doable, this has several drawbacks: it will use latest yanked or
  changed region or the entire buffer if marks are not set.
  If remember to advance this target only after recent put operation, this
  should work as expected.

- Remap common put operations to use |MiniBracketed.register_put_region()|.
  After that, only regions from mapped put operations will be used for first
  advance. Example of custom mappings (note use of |:map-expression|): >lua

    local put_keys = { 'p', 'P' }
    for _, lhs in ipairs(put_keys) do
      local rhs = 'v:lua.MiniBracketed.register_put_region("' .. lhs .. '")'
      vim.keymap.set({ 'n', 'x' }, lhs, rhs, { expr = true })
    end
<
Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
  - <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
  - <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.
  - <operators> `(table)` - array of operator names ("c", "d", or "y") for
    which yank entry should be used to advance. For example, use `{ "y" }`
    to advance only by entries actually resulted from yank operation with |y|.
    Default: `{ 'c', 'd', 'y' }`.

------------------------------------------------------------------------------
                                           *MiniBracketed.register_put_region()*
                 `MiniBracketed.register_put_region`({put_key})
Register "latest put region"

This function should be called after put register becomes relevant
(|v:register| is appropriately set) but before put operation takes place
(|'[| and |']| marks become relevant).

Designed to be used in a user-facing expression mapping (see |:map-expression|).
For mapping examples see |MiniBracketed.yank()|.

Parameters ~
{put_key} `(string)` Put keys to be remapped.

Return ~
`(string)` Returns `put_key` for a better usage inside expression mappings.

------------------------------------------------------------------------------
                                                       *MiniBracketed.advance()*
            `MiniBracketed.advance`({iterator}, {direction}, {opts})
Advance iterator

This is the main function which performs any forward/backward/first/last
advance in this module. Its basic idea is to take iterator (object containing
information about current state and how to go to next/previous one) and go
in certain direction until needed/allowed.

Notes:
- Directions "first" and "last" are convenience wrappers for "forward" and
  "backward" with pre-setting initial state to `start_edge` and `end_edge`.
- Iterators `next()` and `prev()` methods should be able to handle `nil` as input.
- This function only returns new state and doesn't modify `iterator.state`.

Parameters ~
{iterator} `(table)` Table:
  - Methods:
      - <next> - given state, return state in forward direction (no wrap).
      - <prev> - given state, return state in backward direction (no wrap).
  - Fields:
      - <state> - object describing current state.
      - <start_edge> (optional) - object with `forward(start_edge)` describing
        first state. If `nil`, can't wrap forward or use direction "first".
      - <end_edge> (optional) - object with `backward(end_edge)` describing
        last state. If `nil`, can't wrap backward or use direction "last".
{direction} `(string)` Direction. One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options with the following keys:
  - <n_times> `(number)` - number of times to go in input direction.
    Default: `v:count1`.
  - <wrap> `(boolean)` - whether to wrap around edges when `next()` or
    `prev()` return `nil`. Default: `true`.

Return ~
`(any)` Result state. If `nil`, could not reach any valid result state.


 vim:tw=78:ts=8:noet:ft=help:norl: