From eb94149a293131c79baa3bd86f3dc1078d38d472 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 13 Jul 2026 17:57:13 -0400 Subject: [PATCH] refactor(vimscript)!: drop ctxget(), ctxpush(), etc #40724 Problem: This functions are most likely never used in the wild. - They are redundant with `nvim_get_context` + `nvim_load_context`. - The "context" concept was never fully developed and hasn't gained traction. Solution: Drop these vimscript aliases, they are just extra cruft. --- runtime/doc/deprecated.txt | 4 + runtime/doc/news.txt | 8 + runtime/doc/repeat.txt | 4 - runtime/doc/usr_41.txt | 7 - runtime/doc/vimfn.txt | 50 -- runtime/lua/vim/_meta/vimfn.gen.lua | 39 -- src/nvim/context.c | 61 +-- src/nvim/eval.lua | 55 --- src/nvim/eval/funcs.c | 115 ----- src/nvim/memory.c | 1 - .../vimscript/ctx_functions_spec.lua | 444 ------------------ 11 files changed, 17 insertions(+), 771 deletions(-) delete mode 100644 test/functional/vimscript/ctx_functions_spec.lua diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index 158491d8a9..00f550e7da 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -44,6 +44,10 @@ LSP • vim.lsp.util.character_offset() Use to |vim.str_utfindex()| instead +VIMSCRIPT +• ctxget(), ctxpop(), ctxpush(), ctxset(), ctxsize(). Use + |nvim_get_context()| and |nvim_load_context()| instead. + ------------------------------------------------------------------------------ DEPRECATED IN 0.12 *deprecated-0.12* diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 85338c5eaa..4ee40a9218 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -132,6 +132,11 @@ TREESITTER • todo +VIMSCRIPT + +• Removed: ctxget(), ctxpop(), ctxpush(), ctxset(), ctxsize(). Use + |nvim_get_context()| and |nvim_load_context()| instead. + ============================================================================== NEW FEATURES *news-features* @@ -392,6 +397,9 @@ REMOVED FEATURES *news-removed* These deprecated features were removed. +• Vimscript functions ctxget(), ctxpop(), ctxpush(), ctxset(), ctxsize() + and the "context stack". Use |nvim_get_context()| and |nvim_load_context()| + with a script-owned list instead. • |vim.lsp.buf.declaration()|, |vim.lsp.buf.definition()|, |vim.lsp.buf.type_definition()|, and |vim.lsp.buf.implementation()| no longer accept the `reuse_win` option. diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index 02d74e9b12..e478973f62 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -917,9 +917,5 @@ Context objects are dictionaries with the following key-value pairs: - "funcs" (includes |script-local| functions as well): |List| of |:function| definitions. - *context-stack* -An initially-empty internal Context stack is maintained by the ctx-family -functions (see |ctx-functions|). - vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index e386f383b7..ed455b6200 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -1101,13 +1101,6 @@ Registers: *register-functions* reg_executing() return the name of the register being executed reg_recording() return the name of the register being recorded -Context Stack: *ctx-functions* - ctxget() return context at given index from top - ctxpop() pop and restore top context - ctxpush() push given context - ctxset() set context at given index from top - ctxsize() return context stack size - Various: *various-functions* mode() get current editing mode visualmode() last visual mode used diff --git a/runtime/doc/vimfn.txt b/runtime/doc/vimfn.txt index 32dc585afe..538cac7dac 100644 --- a/runtime/doc/vimfn.txt +++ b/runtime/doc/vimfn.txt @@ -1517,56 +1517,6 @@ count({comp}, {expr} [, {ic} [, {start}]]) *count()* *E70 Return: ~ (`integer`) -ctxget([{index}]) *ctxget()* - Returns a |Dictionary| representing the |context| at {index} - from the top of the |context-stack| (see |context-dict|). - If {index} is not given, it is assumed to be 0 (i.e.: top). - - Parameters: ~ - • {index} (`integer?`) - - Return: ~ - (`table`) - -ctxpop() *ctxpop()* - Pops and restores the |context| at the top of the - |context-stack|. - - Return: ~ - (`any`) - -ctxpush([{types}]) *ctxpush()* - Pushes the current editor state (|context|) on the - |context-stack|. - If {types} is given and is a |List| of |String|s, it specifies - which |context-types| to include in the pushed context. - Otherwise, all context types are included. - - Parameters: ~ - • {types} (`string[]?`) - - Return: ~ - (`any`) - -ctxset({context} [, {index}]) *ctxset()* - Sets the |context| at {index} from the top of the - |context-stack| to that represented by {context}. - {context} is a Dictionary with context data (|context-dict|). - If {index} is not given, it is assumed to be 0 (i.e.: top). - - Parameters: ~ - • {context} (`table`) - • {index} (`integer?`) - - Return: ~ - (`integer`) - -ctxsize() *ctxsize()* - Returns the size of the |context-stack|. - - Return: ~ - (`any`) - cursor({lnum}, {col} [, {off}]) *cursor()* cursor({list}) Lua: Prefer |nvim_win_set_cursor()| with `{lnum, col - 1}`; diff --git a/runtime/lua/vim/_meta/vimfn.gen.lua b/runtime/lua/vim/_meta/vimfn.gen.lua index ec0a4f6d71..5fa3910c0a 100644 --- a/runtime/lua/vim/_meta/vimfn.gen.lua +++ b/runtime/lua/vim/_meta/vimfn.gen.lua @@ -1333,45 +1333,6 @@ function vim.fn.cosh(expr) end --- @return integer function vim.fn.count(comp, expr, ic, start) end ---- Returns a |Dictionary| representing the |context| at {index} ---- from the top of the |context-stack| (see |context-dict|). ---- If {index} is not given, it is assumed to be 0 (i.e.: top). ---- ---- @param index? integer ---- @return table -function vim.fn.ctxget(index) end - ---- Pops and restores the |context| at the top of the ---- |context-stack|. ---- ---- @return any -function vim.fn.ctxpop() end - ---- Pushes the current editor state (|context|) on the ---- |context-stack|. ---- If {types} is given and is a |List| of |String|s, it specifies ---- which |context-types| to include in the pushed context. ---- Otherwise, all context types are included. ---- ---- @param types? string[] ---- @return any -function vim.fn.ctxpush(types) end - ---- Sets the |context| at {index} from the top of the ---- |context-stack| to that represented by {context}. ---- {context} is a Dictionary with context data (|context-dict|). ---- If {index} is not given, it is assumed to be 0 (i.e.: top). ---- ---- @param context table ---- @param index? integer ---- @return integer -function vim.fn.ctxset(context, index) end - ---- Returns the size of the |context-stack|. ---- ---- @return any -function vim.fn.ctxsize() end - --- @param lnum integer|string --- @param col? integer --- @param off? integer diff --git a/src/nvim/context.c b/src/nvim/context.c index 97ebce4086..270e5d6b66 100644 --- a/src/nvim/context.c +++ b/src/nvim/context.c @@ -52,41 +52,12 @@ int kCtxAll = (kCtxRegs | kCtxJumps | kCtxBufs | kCtxGVars | kCtxSFuncs | kCtxFuncs); -static ContextVec ctx_stack = KV_INITIAL_VALUE; - /// Nesting depth of ctx_switch() calls that changed curwin. static int _ctx_switch_depth = 0; /// curwin saved by the outermost curwin-changing ctx_switch() (0: none). static handle_T _ctx_saved_curwin = 0; -/// Clears and frees the context stack -void ctx_free_all(void) -{ - for (size_t i = 0; i < kv_size(ctx_stack); i++) { - ctx_free(&kv_A(ctx_stack, i)); - } - kv_destroy(ctx_stack); -} - -/// Returns the size of the context stack. -size_t ctx_size(void) - FUNC_ATTR_PURE -{ - return kv_size(ctx_stack); -} - -/// Returns pointer to Context object with given zero-based index from the top -/// of context stack or NULL if index is out of bounds. -Context *ctx_get(size_t index) - FUNC_ATTR_PURE -{ - if (index < kv_size(ctx_stack)) { - return &kv_Z(ctx_stack, index); - } - return NULL; -} - /// Free resources used by Context object. /// /// param[in] ctx pointer to Context object to free. @@ -102,18 +73,13 @@ void ctx_free(Context *ctx) /// Saves the editor state to a context. /// -/// If "context" is NULL, pushes context on context stack. /// Use "flags" to select particular types of context. /// -/// @param ctx Save to this context, or push on context stack if NULL. +/// @param ctx Save to this context. /// @param flags Flags, see ContextTypeFlags enum. void ctx_save(Context *ctx, const int flags) + FUNC_ATTR_NONNULL_ALL { - if (ctx == NULL) { - kv_push(ctx_stack, CONTEXT_INIT); - ctx = &kv_last(ctx_stack); - } - if (flags & kCtxRegs) { ctx->regs = shada_encode_regs(); } @@ -158,24 +124,13 @@ void ctx_save(Context *ctx, const int flags) /// Loads (restores) the editor state from a Context snapshot. /// -/// If "context" is NULL, pops context from context stack. /// Use "flags" to select particular types of context. /// -/// @param ctx Load from this context. Pop from context stack if NULL. +/// @param ctx Load from this context. /// @param flags Flags, see ContextTypeFlags enum. -/// -/// @return true on success, false otherwise (i.e.: empty context stack). -bool ctx_load(Context *ctx, const int flags) +void ctx_load(Context *ctx, const int flags) + FUNC_ATTR_NONNULL_ALL { - bool free_ctx = false; - if (ctx == NULL) { - if (ctx_stack.size == 0) { - return false; - } - ctx = &kv_pop(ctx_stack); - free_ctx = true; - } - OptVal op_shada = get_option_value(kOptShada, OPT_GLOBAL); set_option_value(kOptShada, STATIC_CSTR_AS_OPTVAL("!,'100,%"), OPT_GLOBAL); @@ -201,14 +156,8 @@ bool ctx_load(Context *ctx, const int flags) } } - if (free_ctx) { - ctx_free(ctx); - } - set_option_value(kOptShada, op_shada, OPT_GLOBAL); optval_free(op_shada); - - return true; } /// Convert readfile()-style array to String diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 89b731f8b4..c423f3b91d 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -1722,61 +1722,6 @@ M.funcs = { returns = 'integer', signature = 'count({comp}, {expr} [, {ic} [, {start}]])', }, - ctxget = { - args = { 0, 1 }, - desc = [=[ - Returns a |Dictionary| representing the |context| at {index} - from the top of the |context-stack| (see |context-dict|). - If {index} is not given, it is assumed to be 0 (i.e.: top). - ]=], - name = 'ctxget', - params = { { 'index', 'integer' } }, - returns = 'table', - signature = 'ctxget([{index}])', - }, - ctxpop = { - desc = [=[ - Pops and restores the |context| at the top of the - |context-stack|. - ]=], - name = 'ctxpop', - params = {}, - signature = 'ctxpop()', - }, - ctxpush = { - args = { 0, 1 }, - desc = [=[ - Pushes the current editor state (|context|) on the - |context-stack|. - If {types} is given and is a |List| of |String|s, it specifies - which |context-types| to include in the pushed context. - Otherwise, all context types are included. - ]=], - name = 'ctxpush', - params = { { 'types', 'string[]' } }, - signature = 'ctxpush([{types}])', - }, - ctxset = { - args = { 1, 2 }, - desc = [=[ - Sets the |context| at {index} from the top of the - |context-stack| to that represented by {context}. - {context} is a Dictionary with context data (|context-dict|). - If {index} is not given, it is assumed to be 0 (i.e.: top). - ]=], - name = 'ctxset', - params = { { 'context', 'table' }, { 'index', 'integer' } }, - returns = 'integer', - signature = 'ctxset({context} [, {index}])', - }, - ctxsize = { - desc = [=[ - Returns the size of the |context-stack|. - ]=], - name = 'ctxsize', - params = {}, - signature = 'ctxsize()', - }, cursor = { args = { 1, 3 }, base = 1, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 542b616285..e4d3a90741 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -844,121 +844,6 @@ static void f_copy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) var_item_copy(NULL, &argvars[0], rettv, false, 0); } -/// "ctxget([{index}])" function -static void f_ctxget(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - size_t index = 0; - if (argvars[0].v_type == VAR_NUMBER) { - index = (size_t)argvars[0].vval.v_number; - } else if (argvars[0].v_type != VAR_UNKNOWN) { - semsg(_(e_invarg2), "expected nothing or a Number as an argument"); - return; - } - - Context *ctx = ctx_get(index); - if (ctx == NULL) { - semsg(_(e_invargNval), "index", "out of bounds"); - return; - } - - Arena arena = ARENA_EMPTY; - Dict ctx_dict = ctx_to_dict(ctx, &arena); - Error err = ERROR_INIT; - object_to_vim(DICT_OBJ(ctx_dict), rettv, &err); - arena_mem_free(arena_finish(&arena)); - api_clear_error(&err); -} - -/// "ctxpop()" function -static void f_ctxpop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - if (!ctx_load(NULL, kCtxAll)) { - emsg(_("Context stack is empty")); - } -} - -/// "ctxpush([{types}])" function -static void f_ctxpush(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - int types = kCtxAll; - if (argvars[0].v_type == VAR_LIST) { - types = 0; - TV_LIST_ITER(argvars[0].vval.v_list, li, { - typval_T *tv_li = TV_LIST_ITEM_TV(li); - if (tv_li->v_type == VAR_STRING) { - if (strequal(tv_li->vval.v_string, "regs")) { - types |= kCtxRegs; - } else if (strequal(tv_li->vval.v_string, "jumps")) { - types |= kCtxJumps; - } else if (strequal(tv_li->vval.v_string, "bufs")) { - types |= kCtxBufs; - } else if (strequal(tv_li->vval.v_string, "gvars")) { - types |= kCtxGVars; - } else if (strequal(tv_li->vval.v_string, "sfuncs")) { - types |= kCtxSFuncs; - } else if (strequal(tv_li->vval.v_string, "funcs")) { - types |= kCtxFuncs; - } - } - }); - } else if (argvars[0].v_type != VAR_UNKNOWN) { - semsg(_(e_invarg2), "expected nothing or a List as an argument"); - return; - } - ctx_save(NULL, types); -} - -/// "ctxset({context}[, {index}])" function -static void f_ctxset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - if (argvars[0].v_type != VAR_DICT) { - semsg(_(e_invarg2), "expected dictionary as first argument"); - return; - } - - size_t index = 0; - if (argvars[1].v_type == VAR_NUMBER) { - index = (size_t)argvars[1].vval.v_number; - } else if (argvars[1].v_type != VAR_UNKNOWN) { - semsg(_(e_invarg2), "expected nothing or a Number as second argument"); - return; - } - - Context *ctx = ctx_get(index); - if (ctx == NULL) { - semsg(_(e_invargNval), "index", "out of bounds"); - return; - } - - const int save_did_emsg = did_emsg; - did_emsg = false; - - Arena arena = ARENA_EMPTY; - Dict dict = vim_to_object(&argvars[0], &arena, true).data.dict; - Context tmp = CONTEXT_INIT; - Error err = ERROR_INIT; - ctx_from_dict(dict, &tmp, &err); - - if (ERROR_SET(&err)) { - semsg("%s", err.msg); - ctx_free(&tmp); - } else { - ctx_free(ctx); - *ctx = tmp; - } - - arena_mem_free(arena_finish(&arena)); - api_clear_error(&err); - did_emsg = save_did_emsg; -} - -/// "ctxsize()" function -static void f_ctxsize(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = (varnumber_T)ctx_size(); -} - /// Set the cursor position. /// If "charcol" is true, then use the column number as a character offset. /// Otherwise use the column number as a byte offset. diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 2d6d8ac75e..36a1e2ec4b 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -983,7 +983,6 @@ void free_all_mem(void) channel_free_all_mem(); eval_clear(); api_extmark_free_all_mem(); - ctx_free_all(); map_destroy(int, &buffer_handles); map_destroy(int, &window_handles); diff --git a/test/functional/vimscript/ctx_functions_spec.lua b/test/functional/vimscript/ctx_functions_spec.lua deleted file mode 100644 index 6bda21b15f..0000000000 --- a/test/functional/vimscript/ctx_functions_spec.lua +++ /dev/null @@ -1,444 +0,0 @@ -local t = require('test.testutil') -local n = require('test.functional.testnvim')() - -local call = n.call -local clear = n.clear -local command = n.command -local eq = t.eq -local eval = n.eval -local feed = n.feed -local map = vim.tbl_map -local api = n.api -local parse_context = n.parse_context -local exec_capture = n.exec_capture -local source = n.source -local trim = vim.trim -local write_file = t.write_file -local pcall_err = t.pcall_err - -describe('context functions', function() - local fname1 = 'Xtest-functional-eval-ctx1' - local fname2 = 'Xtest-functional-eval-ctx2' - local outofbounds = 'Vim:E475: Invalid value for argument index: out of bounds' - - before_each(function() - clear() - write_file(fname1, '1\n2\n3') - write_file(fname2, 'a\nb\nc') - end) - - after_each(function() - os.remove(fname1) - os.remove(fname2) - end) - - describe('ctxpush/ctxpop', function() - it('saves and restores registers properly', function() - local regs = { '1', '2', '3', 'a' } - local vals = { '1', '2', '3', 'hjkl' } - feed('i123ddddddqahjklq') - eq( - vals, - map(function(r) - return trim(call('getreg', r)) - end, regs) - ) - call('ctxpush') - call('ctxpush', { 'regs' }) - - map(function(r) - call('setreg', r, {}) - end, regs) - eq( - { '', '', '', '' }, - map(function(r) - return trim(call('getreg', r)) - end, regs) - ) - - call('ctxpop') - eq( - vals, - map(function(r) - return trim(call('getreg', r)) - end, regs) - ) - - map(function(r) - call('setreg', r, {}) - end, regs) - eq( - { '', '', '', '' }, - map(function(r) - return trim(call('getreg', r)) - end, regs) - ) - - call('ctxpop') - eq( - vals, - map(function(r) - return trim(call('getreg', r)) - end, regs) - ) - end) - - it('saves and restores jumplist properly', function() - command('edit ' .. fname1) - feed('G') - feed('gg') - command('edit ' .. fname2) - local jumplist = call('getjumplist') - call('ctxpush') - call('ctxpush', { 'jumps' }) - - command('clearjumps') - eq({ {}, 0 }, call('getjumplist')) - - call('ctxpop') - eq(jumplist, call('getjumplist')) - - command('clearjumps') - eq({ {}, 0 }, call('getjumplist')) - - call('ctxpop') - eq(jumplist, call('getjumplist')) - end) - - it('saves and restores buffer list properly', function() - command('edit ' .. fname1) - command('edit ' .. fname2) - command('edit TEST') - local bufs = call('map', call('getbufinfo'), 'v:val.name') - call('ctxpush') - call('ctxpush', { 'bufs' }) - - command('%bwipeout') - eq({ '' }, call('map', call('getbufinfo'), 'v:val.name')) - - call('ctxpop') - eq({ '', unpack(bufs) }, call('map', call('getbufinfo'), 'v:val.name')) - - command('%bwipeout') - eq({ '' }, call('map', call('getbufinfo'), 'v:val.name')) - - call('ctxpop') - eq({ '', unpack(bufs) }, call('map', call('getbufinfo'), 'v:val.name')) - end) - - it('saves and restores global variables properly', function() - api.nvim_set_var('one', 1) - api.nvim_set_var('Two', 2) - api.nvim_set_var('THREE', 3) - eq({ 1, 2, 3 }, eval('[g:one, g:Two, g:THREE]')) - call('ctxpush') - call('ctxpush', { 'gvars' }) - - api.nvim_del_var('one') - api.nvim_del_var('Two') - api.nvim_del_var('THREE') - eq('Vim:E121: Undefined variable: g:one', pcall_err(eval, 'g:one')) - eq('Vim:E121: Undefined variable: g:Two', pcall_err(eval, 'g:Two')) - eq('Vim:E121: Undefined variable: g:THREE', pcall_err(eval, 'g:THREE')) - - call('ctxpop') - eq({ 1, 2, 3 }, eval('[g:one, g:Two, g:THREE]')) - - api.nvim_del_var('one') - api.nvim_del_var('Two') - api.nvim_del_var('THREE') - eq('Vim:E121: Undefined variable: g:one', pcall_err(eval, 'g:one')) - eq('Vim:E121: Undefined variable: g:Two', pcall_err(eval, 'g:Two')) - eq('Vim:E121: Undefined variable: g:THREE', pcall_err(eval, 'g:THREE')) - - call('ctxpop') - eq({ 1, 2, 3 }, eval('[g:one, g:Two, g:THREE]')) - end) - - it('saves and restores script functions properly', function() - source([[ - function s:greet(name) - echom 'Hello, '.a:name.'!' - endfunction - - function s:greet_all(name, ...) - echom 'Hello, '.a:name.'!' - for more in a:000 - echom 'Hello, '.more.'!' - endfor - endfunction - - function Greet(name) - call call('s:greet', [a:name]) - endfunction - - function GreetAll(name, ...) - call call('s:greet_all', extend([a:name], a:000)) - endfunction - - function SaveSFuncs() - call ctxpush(['sfuncs']) - endfunction - - function DeleteSFuncs() - delfunction s:greet - delfunction s:greet_all - endfunction - - function RestoreFuncs() - call ctxpop() - endfunction - - let g:sid = expand('') - ]]) - local sid = api.nvim_get_var('sid') - - eq('Hello, World!', exec_capture([[call Greet('World')]])) - eq( - 'Hello, World!' .. '\nHello, One!' .. '\nHello, Two!' .. '\nHello, Three!', - exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]]) - ) - - call('SaveSFuncs') - call('DeleteSFuncs') - - eq( - ('function Greet, line 1: Vim(call):E117: Unknown function: %sgreet'):format(sid), - pcall_err(command, [[call Greet('World')]]) - ) - eq( - ('function GreetAll, line 1: Vim(call):E117: Unknown function: %sgreet_all'):format(sid), - pcall_err(command, [[call GreetAll('World', 'One', 'Two', 'Three')]]) - ) - - call('RestoreFuncs') - - eq('Hello, World!', exec_capture([[call Greet('World')]])) - eq( - 'Hello, World!' .. '\nHello, One!' .. '\nHello, Two!' .. '\nHello, Three!', - exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]]) - ) - end) - - it('saves and restores functions properly', function() - source([[ - function Greet(name) - echom 'Hello, '.a:name.'!' - endfunction - - function GreetAll(name, ...) - echom 'Hello, '.a:name.'!' - for more in a:000 - echom 'Hello, '.more.'!' - endfor - endfunction - ]]) - - eq('Hello, World!', exec_capture([[call Greet('World')]])) - eq( - 'Hello, World!' .. '\nHello, One!' .. '\nHello, Two!' .. '\nHello, Three!', - exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]]) - ) - - call('ctxpush', { 'funcs' }) - command('delfunction Greet') - command('delfunction GreetAll') - - eq('Vim:E117: Unknown function: Greet', pcall_err(call, 'Greet', 'World')) - eq( - 'Vim:E117: Unknown function: GreetAll', - pcall_err(call, 'GreetAll', 'World', 'One', 'Two', 'Three') - ) - - call('ctxpop') - - eq('Hello, World!', exec_capture([[call Greet('World')]])) - eq( - 'Hello, World!' .. '\nHello, One!' .. '\nHello, Two!' .. '\nHello, Three!', - exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]]) - ) - end) - - it('errors out when context stack is empty', function() - local err = 'Vim:Context stack is empty' - eq(err, pcall_err(call, 'ctxpop')) - eq(err, pcall_err(call, 'ctxpop')) - call('ctxpush') - call('ctxpush') - call('ctxpop') - call('ctxpop') - eq(err, pcall_err(call, 'ctxpop')) - end) - end) - - describe('ctxsize()', function() - it('returns context stack size', function() - eq(0, call('ctxsize')) - call('ctxpush') - eq(1, call('ctxsize')) - call('ctxpush') - eq(2, call('ctxsize')) - call('ctxpush') - eq(3, call('ctxsize')) - call('ctxpop') - eq(2, call('ctxsize')) - call('ctxpop') - eq(1, call('ctxsize')) - call('ctxpop') - eq(0, call('ctxsize')) - end) - end) - - describe('ctxget()', function() - it('errors out when index is out of bounds', function() - eq(outofbounds, pcall_err(call, 'ctxget')) - call('ctxpush') - eq(outofbounds, pcall_err(call, 'ctxget', 1)) - call('ctxpop') - eq(outofbounds, pcall_err(call, 'ctxget', 0)) - end) - - it('returns context dict at index in context stack', function() - feed('i123ddddddqahjklq') - command('edit! ' .. fname1) - feed('G') - feed('gg') - command('edit ' .. fname2) - api.nvim_set_var('one', 1) - api.nvim_set_var('Two', 2) - api.nvim_set_var('THREE', 3) - - local with_regs = { - ['regs'] = { - { ['rt'] = 1, ['rc'] = { '1' }, ['n'] = 49, ['ru'] = true }, - { ['rt'] = 1, ['rc'] = { '2' }, ['n'] = 50 }, - { ['rt'] = 1, ['rc'] = { '3' }, ['n'] = 51 }, - { ['rc'] = { 'hjkl' }, ['n'] = 97 }, - }, - } - - local with_jumps = { - ['jumps'] = eval((([[ - filter(map(add( - getjumplist()[0], { 'bufnr': bufnr('%'), 'lnum': getcurpos()[1] }), - 'filter( - { "f": expand("#".v:val.bufnr.":p:gs?\\?/?"), "l": v:val.lnum }, - { k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)') - ]]):gsub('\n', ''))), - } - - local with_bufs = { - ['bufs'] = eval([[ - filter(map(getbufinfo(), '{ "f": v:val.name }'), '!empty(v:val.f)') - ]]), - } - - local with_gvars = { - ['gvars'] = { { 'one', 1 }, { 'Two', 2 }, { 'THREE', 3 } }, - } - - local with_all = { - ['regs'] = with_regs['regs'], - ['jumps'] = with_jumps['jumps'], - ['bufs'] = with_bufs['bufs'], - ['gvars'] = with_gvars['gvars'], - } - - call('ctxpush') - eq(with_all, parse_context(call('ctxget'))) - eq(with_all, parse_context(call('ctxget', 0))) - - call('ctxpush', { 'gvars' }) - eq(with_gvars, parse_context(call('ctxget'))) - eq(with_gvars, parse_context(call('ctxget', 0))) - eq(with_all, parse_context(call('ctxget', 1))) - - call('ctxpush', { 'bufs' }) - eq(with_bufs, parse_context(call('ctxget'))) - eq(with_bufs, parse_context(call('ctxget', 0))) - eq(with_gvars, parse_context(call('ctxget', 1))) - eq(with_all, parse_context(call('ctxget', 2))) - - call('ctxpush', { 'jumps' }) - eq(with_jumps, parse_context(call('ctxget'))) - eq(with_jumps, parse_context(call('ctxget', 0))) - eq(with_bufs, parse_context(call('ctxget', 1))) - eq(with_gvars, parse_context(call('ctxget', 2))) - eq(with_all, parse_context(call('ctxget', 3))) - - call('ctxpush', { 'regs' }) - eq(with_regs, parse_context(call('ctxget'))) - eq(with_regs, parse_context(call('ctxget', 0))) - eq(with_jumps, parse_context(call('ctxget', 1))) - eq(with_bufs, parse_context(call('ctxget', 2))) - eq(with_gvars, parse_context(call('ctxget', 3))) - eq(with_all, parse_context(call('ctxget', 4))) - - call('ctxpop') - eq(with_jumps, parse_context(call('ctxget'))) - eq(with_jumps, parse_context(call('ctxget', 0))) - eq(with_bufs, parse_context(call('ctxget', 1))) - eq(with_gvars, parse_context(call('ctxget', 2))) - eq(with_all, parse_context(call('ctxget', 3))) - - call('ctxpop') - eq(with_bufs, parse_context(call('ctxget'))) - eq(with_bufs, parse_context(call('ctxget', 0))) - eq(with_gvars, parse_context(call('ctxget', 1))) - eq(with_all, parse_context(call('ctxget', 2))) - - call('ctxpop') - eq(with_gvars, parse_context(call('ctxget'))) - eq(with_gvars, parse_context(call('ctxget', 0))) - eq(with_all, parse_context(call('ctxget', 1))) - - call('ctxpop') - eq(with_all, parse_context(call('ctxget'))) - eq(with_all, parse_context(call('ctxget', 0))) - end) - end) - - describe('ctxset()', function() - it('errors out when index is out of bounds', function() - eq(outofbounds, pcall_err(call, 'ctxset', { dummy = 1 })) - call('ctxpush') - eq(outofbounds, pcall_err(call, 'ctxset', { dummy = 1 }, 1)) - call('ctxpop') - eq(outofbounds, pcall_err(call, 'ctxset', { dummy = 1 }, 0)) - end) - - it('errors when context dict is invalid', function() - call('ctxpush') - eq( - 'Vim:E474: Failed to convert list to msgpack string buffer', - pcall_err(call, 'ctxset', { regs = { {} }, jumps = { {} } }) - ) - end) - - it('sets context dict at index in context stack', function() - api.nvim_set_var('one', 1) - api.nvim_set_var('Two', 2) - api.nvim_set_var('THREE', 3) - call('ctxpush') - local ctx1 = call('ctxget') - api.nvim_set_var('one', 'a') - api.nvim_set_var('Two', 'b') - api.nvim_set_var('THREE', 'c') - call('ctxpush') - call('ctxpush') - local ctx2 = call('ctxget') - - eq({ 'a', 'b', 'c' }, eval('[g:one, g:Two, g:THREE]')) - call('ctxset', ctx1) - call('ctxset', ctx2, 2) - call('ctxpop') - eq({ 1, 2, 3 }, eval('[g:one, g:Two, g:THREE]')) - call('ctxpop') - eq({ 'a', 'b', 'c' }, eval('[g:one, g:Two, g:THREE]')) - api.nvim_set_var('one', 1.5) - eq({ 1.5, 'b', 'c' }, eval('[g:one, g:Two, g:THREE]')) - call('ctxpop') - eq({ 'a', 'b', 'c' }, eval('[g:one, g:Two, g:THREE]')) - end) - end) -end)