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.
This commit is contained in:
Justin M. Keyes
2026-07-13 17:57:13 -04:00
committed by GitHub
parent 06ded1330c
commit eb94149a29
11 changed files with 17 additions and 771 deletions

View File

@@ -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*

View File

@@ -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.

View File

@@ -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:

View File

@@ -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

View File

@@ -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}`;

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -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.

View File

@@ -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);

View File

@@ -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('i1<cr>2<cr>3<c-[>ddddddqahjklq')
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('<SID>')
]])
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('i1<cr>2<cr>3<c-[>ddddddqahjklq')
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)