From 40527a4684f03e80f63965145e4e25dd2b50093d Mon Sep 17 00:00:00 2001 From: Riley Bruins Date: Wed, 11 Dec 2024 23:44:35 -0800 Subject: [PATCH] feat(api): add/del folds, get fold information --- runtime/doc/api.txt | 40 +++++++++ runtime/lua/vim/_meta/api.lua | 36 ++++++++ runtime/lua/vim/_meta/api_keysets.lua | 3 + src/nvim/api/keysets_defs.h | 5 ++ src/nvim/api/window.c | 113 ++++++++++++++++++++++++++ src/nvim/fold.c | 45 +--------- src/nvim/fold.h | 45 ++++++++++ 7 files changed, 243 insertions(+), 44 deletions(-) diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index cb3b2a3f77..b59898f5e1 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -2889,6 +2889,20 @@ nvim__ns_set({ns_id}, {opts}) *nvim__ns_set()* ============================================================================== Window Functions *api-window* + *nvim_win_add_fold()* +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 |fold-manual| and |fold-marker|. + + 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_call({window}, {fun}) *nvim_win_call()* Calls a function with window as temporary current window. @@ -2919,6 +2933,21 @@ nvim_win_close({window}, {force}) *nvim_win_close()* unwritten changes can be closed. The buffer will become hidden, even if 'hidden' is not set. + *nvim_win_del_fold()* +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 |fold-manual| and |fold-marker|. + + Parameters: ~ + • {window} Window handle, or 0 for current window. + • {start} Start row of fold. + • {end} End row of fold. + • {opts} Optional parameters: + • recursive: Delete folds recursively + nvim_win_del_var({window}, {name}) *nvim_win_del_var()* Removes a window-scoped (w:) variable @@ -2949,6 +2978,17 @@ nvim_win_get_cursor({window}) *nvim_win_get_cursor()* See also: ~ • |getcurpos()| +nvim_win_get_folds({window}, {opts}) *nvim_win_get_folds()* + Get fold information from the window. + + 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 + nvim_win_get_height({window}) *nvim_win_get_height()* Gets the window height diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index b2385197bd..2db06a95cc 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -2343,6 +2343,18 @@ function vim.api.nvim_tabpage_set_var(tabpage, name, value) end --- @param win integer Window handle, must already belong to {tabpage} function vim.api.nvim_tabpage_set_win(tabpage, win) end +--- Add a fold to the window from {start} to {end}. +--- +--- All row arguments are 0-indexed, inclusive. +--- +--- Only supported for `fold-manual` and `fold-marker`. +--- +--- @param window integer Window handle, or 0 for current window. +--- @param start integer Start row of fold. +--- @param end_ integer End row of fold. +--- @param opts vim.api.keyset.empty Optional parameters. Reserved for future use. +function vim.api.nvim_win_add_fold(window, start, end_, opts) end + --- Calls a function with window as temporary current window. --- --- @@ -2362,6 +2374,19 @@ function vim.api.nvim_win_call(window, fun) end --- hidden, even if 'hidden' is not set. function vim.api.nvim_win_close(window, force) end +--- Delete a fold from the window from {start} to {end}. +--- +--- All row arguments are 0-indexed, inclusive. +--- +--- Only supported for `fold-manual` and `fold-marker`. +--- +--- @param window integer Window handle, or 0 for current window. +--- @param start integer Start row of fold. +--- @param end_ integer End row of fold. +--- @param opts vim.api.keyset.win_del_fold Optional parameters: +--- - recursive: Delete folds recursively +function vim.api.nvim_win_del_fold(window, start, end_, opts) end + --- Removes a window-scoped (w:) variable --- --- @param window integer Window handle, or 0 for current window @@ -2394,6 +2419,17 @@ function vim.api.nvim_win_get_config(window) end --- @return integer[] # (row, col) tuple function vim.api.nvim_win_get_cursor(window) end +--- Get fold information from the window. +--- +--- All row arguments are 0-indexed, inclusive. +--- +--- @param window integer Window handle, or 0 for current window. +--- @param opts vim.api.keyset.empty Optional parameters: +--- - start_row: get folds from this row +--- - end_row: get folds up to this row +--- @return any[] +function vim.api.nvim_win_get_folds(window, opts) end + --- Gets the window height --- --- @param window integer Window handle, or 0 for current window diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua index e11dddb2d3..e46fdfaed8 100644 --- a/runtime/lua/vim/_meta/api_keysets.lua +++ b/runtime/lua/vim/_meta/api_keysets.lua @@ -308,6 +308,9 @@ error('Cannot require a meta file') --- @field fixed? boolean --- @field hide? boolean +--- @class vim.api.keyset.win_del_fold +--- @field recursive? boolean + --- @class vim.api.keyset.win_text_height --- @field start_row? integer --- @field end_row? integer diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index 48f5f7246c..a1af495f23 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -227,6 +227,11 @@ typedef struct { Integer end_vcol; } Dict(win_text_height); +typedef struct { + OptionalKeys is_set__win_del_fold_; + Boolean recursive; +} Dict(win_del_fold); + typedef struct { OptionalKeys is_set__clear_autocmds_; Buffer buffer; diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index ee7729ce81..8fc2a47d7b 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -4,6 +4,7 @@ #include #include "nvim/api/keysets_defs.h" +#include "klib/kvec.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/dispatch.h" #include "nvim/api/private/helpers.h" @@ -16,6 +17,7 @@ #include "nvim/errors.h" #include "nvim/eval/window.h" #include "nvim/ex_docmd.h" +#include "nvim/fold.h" #include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/lua/executor.h" @@ -582,3 +584,114 @@ Dict nvim_win_text_height(Window window, Dict(win_text_height) *opts, Arena *are PUT_C(rv, "fill", INTEGER_OBJ(fill)); return rv; } + +/// Add a fold to the window from {start} to {end}. +/// +/// All row arguments are 0-indexed, inclusive. +/// +/// Only supported for |fold-manual| and |fold-marker|. +/// +/// @param window Window handle, or 0 for current window. +/// @param start Start row of fold. +/// @param end End row of fold. +/// @param opts Optional parameters. Reserved for future use. +void nvim_win_add_fold(Window window, Integer start, Integer end, Dict(empty) *opts, Error *err) + FUNC_API_SINCE(11) +{ + if (!foldManualAllowed(true)) { + return; + } + win_T *win = find_window_by_handle(window, err); + if (!win) { + return; + } + + curwin->w_p_fen = true; + // Rows are zero-indexed. + pos_T start_pos = { start + 1, 1, 0 }; + pos_T end_pos = { end + 1, 1, 0 }; + + foldCreate(win, start_pos, end_pos); +} + +/// Delete a fold from the window from {start} to {end}. +/// +/// All row arguments are 0-indexed, inclusive. +/// +/// Only supported for |fold-manual| and |fold-marker|. +/// +/// @param window Window handle, or 0 for current window. +/// @param start Start row of fold. +/// @param end End row of fold. +/// @param opts Optional parameters: +/// - recursive: Delete folds recursively +void nvim_win_del_fold(Window window, Integer start, Integer end, Dict(win_del_fold) *opts, Error *err) + FUNC_API_SINCE(11) +{ + if (!foldManualAllowed(false)) { + return; + } + win_T *win = find_window_by_handle(window, err); + if (!win) { + return; + } + + // Rows are zero-indexed. + deleteFold(win, start + 1, end + 1, opts->recursive, false); +} + +/// Get fold information from the window. +/// +/// All row arguments are 0-indexed, inclusive. +/// +/// @param window Window handle, or 0 for current window. +/// @param opts Optional parameters: +/// - start_row: get folds from this row +/// - end_row: get folds up to this row +Array nvim_win_get_folds(Window window, Dict(empty) *opts, Arena *arena, Error *err) + FUNC_API_SINCE(11) +{ + win_T *win = find_window_by_handle(window, err); + if (!win) { + goto cleanup; + } + + garray_T *gap = &win->w_folds; + + return folds_to_fold_dict(gap, arena, win, 0); + +cleanup: + return (Array)ARRAY_DICT_INIT; +} + +static Array folds_to_fold_dict(garray_T *folds, Arena *arena, win_T *wp, int parent_start) { + fold_T *fp = (fold_T *)folds->ga_data; + int fold_count = folds->ga_len; + Array rv = arena_array(arena, fold_count); + for (int i = 0; i < fold_count; i++) { + fold_T fold = fp[i]; + // Nested fold positions are relative to the parent + int top = fold.fd_top + parent_start; + Dict fold_dict = arena_dict(arena, 4); + char *state; + switch (fold.fd_flags) { + case FD_CLOSED: + state = "closed"; + break; + case FD_OPEN: + state = "open"; + break; + case FD_LEVEL: + // BUG: Still does not update for e.g. open nested folds after "zM" + state = foldLevelWin(wp, top) >= wp->w_p_fdl ? "closed" : "open"; + break; + } + PUT_C(fold_dict, "state", CSTR_TO_ARENA_OBJ(arena, state)); + PUT_C(fold_dict, "start_row", INTEGER_OBJ(top - 1)); + PUT_C(fold_dict, "end_row", INTEGER_OBJ(top + fold.fd_len - 2)); + Array inner_folds = folds_to_fold_dict(&fold.fd_nested, arena, wp, top); + PUT_C(fold_dict, "children", ARRAY_OBJ(inner_folds)); + ADD_C(rv, DICT_OBJ(fold_dict)); + } + return rv; +} diff --git a/src/nvim/fold.c b/src/nvim/fold.c index c9699cb161..f757a133f9 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -55,49 +55,6 @@ #include "nvim/undo.h" #include "nvim/vim_defs.h" -// local declarations. {{{1 -// typedef fold_T {{{2 - -// The toplevel folds for each window are stored in the w_folds growarray. -// Each toplevel fold can contain an array of second level folds in the -// fd_nested growarray. -// The info stored in both growarrays is the same: An array of fold_T. - -typedef struct { - linenr_T fd_top; // first line of fold; for nested fold - // relative to parent - linenr_T fd_len; // number of lines in the fold - garray_T fd_nested; // array of nested folds - char fd_flags; // see below - TriState fd_small; // kTrue, kFalse, or kNone: fold smaller than - // 'foldminlines'; kNone applies to nested - // folds too -} fold_T; - -enum { - FD_OPEN = 0, // fold is open (nested ones can be closed) - FD_CLOSED = 1, // fold is closed - FD_LEVEL = 2, // depends on 'foldlevel' (nested folds too) -}; - -#define MAX_LEVEL 20 // maximum fold depth - -// Define "fline_T", passed to get fold level for a line. {{{2 -typedef struct { - win_T *wp; // window - linenr_T lnum; // current line number - linenr_T off; // offset between lnum and real line number - linenr_T lnum_save; // line nr used by foldUpdateIEMSRecurse() - int lvl; // current level (-1 for undefined) - int lvl_next; // level used for next line - int start; // number of folds that are forced to start at - // this line. - int end; // level of fold that is forced to end below - // this line - int had_end; // level of fold that is forced to end above - // this line (copy of "end" of prev. line) -} fline_T; - // Flag is set when redrawing is needed. static bool fold_changed; @@ -1086,7 +1043,7 @@ static bool foldFind(const garray_T *gap, linenr_T lnum, fold_T **fpp) // foldLevelWin() {{{2 /// @return fold level at line number "lnum" in window "wp". -static int foldLevelWin(win_T *wp, linenr_T lnum) +int foldLevelWin(win_T *wp, linenr_T lnum) { fold_T *fp; linenr_T lnum_rel = lnum; diff --git a/src/nvim/fold.h b/src/nvim/fold.h index f9044d247f..3d69d76f77 100644 --- a/src/nvim/fold.h +++ b/src/nvim/fold.h @@ -12,6 +12,51 @@ EXTERN int disable_fold_update INIT( = 0); +// local declarations. {{{1 +// typedef fold_T {{{2 + +// The toplevel folds for each window are stored in the w_folds growarray. +// Each toplevel fold can contain an array of second level folds in the +// fd_nested growarray. +// The info stored in both growarrays is the same: An array of fold_T. + +typedef struct { + linenr_T fd_top; // first line of fold; for nested fold + // relative to parent + linenr_T fd_len; // number of lines in the fold + garray_T fd_nested; // array of nested folds + char fd_flags; // see below + TriState fd_small; // kTrue, kFalse, or kNone: fold smaller than + // 'foldminlines'; kNone applies to nested + // folds too +} fold_T; + +enum { + FD_OPEN = 0, // fold is open (nested ones can be closed) + FD_CLOSED = 1, // fold is closed + FD_LEVEL = 2, // depends on 'foldlevel' (nested folds too) +}; + +#define MAX_LEVEL 20 // maximum fold depth + +// Define "fline_T", passed to get fold level for a line. {{{2 +typedef struct { + win_T *wp; // window + linenr_T lnum; // current line number + linenr_T off; // offset between lnum and real line number + linenr_T lnum_save; // line nr used by foldUpdateIEMSRecurse() + int lvl; // current level (-1 for undefined) + int lvl_next; // level used for next line + int start; // number of folds that are forced to start at + // this line. + int end; // level of fold that is forced to end below + // this line + int had_end; // level of fold that is forced to end above + // this line (copy of "end" of prev. line) +} fline_T; + +int foldLevelWin(win_T *wp, linenr_T lnum); + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "fold.h.generated.h" #endif