Repository: neovim
Issue: API: folds
Labels: enhancement,api,folds

### Problem

- Plugins cannot inspect folds
- Custom folds require `foldexpr` which is inefficient as it requires evaluating this expression on every buffer line.

### Solution

API for plugins to set, inspect folds and get fold events.

### Notes

- Folds are window local and so are most of the folding related options.
- Fold state (whether a fold is open/closed) is also local to the window.
- ~Extmarks are buffer local.~ Can now be window local with `nvim__ns_set`.
- Folds can be nested

### Proposal

- Add folding support to extmarks
  - Add a new method to `'foldmethod'` named `extmark` so extmark folds cannot be mixed with other kinds of folds.
  - When folds of calculated, we scan the extmarks for all the folds and store them in `win->w_folds`.
  - The trigger for updating is the same as other foldmethods.
- Add `nvim_win_add_fold` and `nvim_win_del_fold`. These new API functions are to be used instead of the `zf`/`zd` operators.
  - _Should these functions work for all fold methods or just `manual`/`marker` like `zf` does?_

  ```
  nvim_win_add_fold({window}, {start}, {end}, {opts})
      Add a fold to the window from {start} to {end}.

      All row arguments are 0-indexed, inclusive.

      Only supported for `foldmethod=manåual`

      Parameters: ~
          {window}  Window handle, or 0 for current window
          {start}   Start row of fold.
          {end}     End row of fold.
          {opts}    Optional parameters. Reserved for future use.

  nvim_win_del_fold({window}, {start}, {end}, {opts})
      Delete a fold from the window from {start} to {end}.

      All row arguments are 0-indexed, inclusive.

      Only supported for `foldmethod=manual`

      Parameters: ~
          {window}  Window handle, or 0 for current window
          {start}   Start row of fold.
          {end}     End row of fold.
          {opts}    Optional parameters. Reserved for future use.

  ```
- Add `nvim_win_get_folds` for getting folds for any foldmethod.
  ```
  nvim_win_get_folds({window}, {opts})

      All row arguments are 0-indexed, inclusive.

      Parameters: ~
          {window}  Window handle, or 0 for current window
          {opts}    Optional parameters:
                    - start_row: get folds from this row
                    - end_row: get folds up to this row
      Returns:

      Array of dictionaries for each fold. Each elements has the keys:
      - state: string, "open" | "closed"
      - start_row: integer, start row of fold
      - end_row: integer, end row of fold
      - [FUTURE] start_col: integer
      - [FUTURE] end_col: integer
      - children: array of folds ????? SHOULD WE HAVE THIS?

  ```

- Add `nvim_win_toggle_folds` change the fold state of all folds in a region.
  ```
  nvim_win_toggle_folds({window}, {opts})

      Parameters: ~
          {window}  Window handle, or 0 for current window
          {opts}    Optional parameters:
                    - start_row: get folds from this row
                    - end_row: get folds up to this row
                    - action: "toggle", "open", "close". Default is "toggle".
                      Note: for "toggle", only the first fold state is considered.
                      E.g. if the first fold in the region is open, then all folds
                      in the region will be closed.
    ```
- Add `nvim_win_attach` with an `on_fold` callback for detecting changes to folds.
  ```
  nvim_win_attach({window}, {opts})
      Activates window-update events on a channel, or as Lua
      callbacks.

      Parameters: ~
          {window}  Window handle, or 0 for current window
          {opts}    Optional parameters:
                    - on_fold: Lua callback invoked on change.
                      Args:
                      - the string "fold"
                      - window handle
                      - first line where folds where updated
                      - last line where folds where updated
                      - String "add | del | update | open | close"

  ```


### Future Enhancements

- Column-aware folds (aka inline folds)

### Questions

- For nested folds, do we represent them as a flat list of locations or a nested structure?

- ~Should we assign ID's and namespaces to folds?~
  - EDIT: No as ID's/namespaces for folds will be achievable using extmarks which will be the preferred method for applying folds.

### Related

- Fold events: #8538
- Window events: #8539