mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
eval: context: add ctx-family functions
This commit is contained in:
parent
a80f691a6a
commit
691deca2e8
107
src/nvim/eval.c
107
src/nvim/eval.c
@ -26,6 +26,7 @@
|
||||
#include "nvim/buffer.h"
|
||||
#include "nvim/channel.h"
|
||||
#include "nvim/charset.h"
|
||||
#include "nvim/context.h"
|
||||
#include "nvim/cursor.h"
|
||||
#include "nvim/diff.h"
|
||||
#include "nvim/edit.h"
|
||||
@ -8014,6 +8015,112 @@ static void f_cscope_connection(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||
(char_u *)prepend);
|
||||
}
|
||||
|
||||
/// "ctxget([{index}])" function
|
||||
static void f_ctxget(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||
{
|
||||
size_t index = 0;
|
||||
if (argvars[0].v_type == VAR_NUMBER) {
|
||||
index = argvars[0].vval.v_number;
|
||||
} else if (argvars[0].v_type != VAR_UNKNOWN) {
|
||||
EMSG2(_(e_invarg2), "expected nothing or a Number as an argument");
|
||||
return;
|
||||
}
|
||||
|
||||
Context *ctx = ctx_get(index);
|
||||
if (ctx == NULL) {
|
||||
EMSG3(_(e_invargNval), "index", "out of bounds");
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary ctx_dict = ctx_to_dict(ctx);
|
||||
Error err = ERROR_INIT;
|
||||
object_to_vim(DICTIONARY_OBJ(ctx_dict), rettv, &err);
|
||||
api_free_dictionary(ctx_dict);
|
||||
api_clear_error(&err);
|
||||
}
|
||||
|
||||
/// "ctxpop()" function
|
||||
static void f_ctxpop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||
{
|
||||
if (!ctx_restore(NULL, kCtxAll)) {
|
||||
EMSG(_("Context stack is empty"));
|
||||
}
|
||||
}
|
||||
|
||||
/// "ctxpush([{types}])" function
|
||||
static void f_ctxpush(typval_T *argvars, typval_T *rettv, FunPtr 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((char *)tv_li->vval.v_string, "regs")) {
|
||||
types |= kCtxRegs;
|
||||
} else if (strequal((char *)tv_li->vval.v_string, "jumps")) {
|
||||
types |= kCtxJumps;
|
||||
} else if (strequal((char *)tv_li->vval.v_string, "buflist")) {
|
||||
types |= kCtxBuflist;
|
||||
} else if (strequal((char *)tv_li->vval.v_string, "gvars")) {
|
||||
types |= kCtxGVars;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (argvars[0].v_type != VAR_UNKNOWN) {
|
||||
EMSG2(_(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, FunPtr fptr)
|
||||
{
|
||||
if (argvars[0].v_type != VAR_DICT) {
|
||||
EMSG2(_(e_invarg2), "expected dictionary as first argument");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t index = 0;
|
||||
if (argvars[1].v_type == VAR_NUMBER) {
|
||||
index = argvars[1].vval.v_number;
|
||||
} else if (argvars[1].v_type != VAR_UNKNOWN) {
|
||||
EMSG2(_(e_invarg2), "expected nothing or a Number as second argument");
|
||||
return;
|
||||
}
|
||||
|
||||
Context *ctx = ctx_get(index);
|
||||
if (ctx == NULL) {
|
||||
EMSG3(_(e_invargNval), "index", "out of bounds");
|
||||
return;
|
||||
}
|
||||
|
||||
int save_did_emsg = did_emsg;
|
||||
did_emsg = false;
|
||||
|
||||
Dictionary dict = vim_to_object(&argvars[0]).data.dictionary;
|
||||
Context tmp = CONTEXT_INIT;
|
||||
ctx_from_dict(dict, &tmp);
|
||||
|
||||
if (did_emsg) {
|
||||
ctx_free(&tmp);
|
||||
} else {
|
||||
ctx_free(ctx);
|
||||
*ctx = tmp;
|
||||
}
|
||||
|
||||
api_free_dictionary(dict);
|
||||
did_emsg = save_did_emsg;
|
||||
}
|
||||
|
||||
/// "ctxsize()" function
|
||||
static void f_ctxsize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||
{
|
||||
rettv->v_type = VAR_NUMBER;
|
||||
rettv->vval.v_number = ctx_size();
|
||||
}
|
||||
|
||||
/// "cursor(lnum, col)" function, or
|
||||
/// "cursor(list)"
|
||||
///
|
||||
|
@ -74,6 +74,11 @@ return {
|
||||
cosh={args=1, func="float_op_wrapper", data="&cosh"},
|
||||
count={args={2, 4}},
|
||||
cscope_connection={args={0, 3}},
|
||||
ctxget={args={0, 1}},
|
||||
ctxpop={},
|
||||
ctxpush={args={0, 1}},
|
||||
ctxset={args={1, 2}},
|
||||
ctxsize={},
|
||||
cursor={args={1, 3}},
|
||||
deepcopy={args={1, 2}},
|
||||
delete={args={1,2}},
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "nvim/main.h"
|
||||
#include "nvim/buffer.h"
|
||||
#include "nvim/charset.h"
|
||||
#include "nvim/context.h"
|
||||
#include "nvim/diff.h"
|
||||
#include "nvim/eval.h"
|
||||
#include "nvim/ex_cmds.h"
|
||||
@ -672,6 +673,8 @@ void getout(int exitval)
|
||||
garbage_collect(false);
|
||||
}
|
||||
|
||||
free_ctx_stack();
|
||||
|
||||
mch_exit(exitval);
|
||||
}
|
||||
|
||||
|
303
test/functional/eval/ctx_functions_spec.lua
Normal file
303
test/functional/eval/ctx_functions_spec.lua
Normal file
@ -0,0 +1,303 @@
|
||||
local helpers = require('test.functional.helpers')(after_each)
|
||||
|
||||
local call = helpers.call
|
||||
local clear = helpers.clear
|
||||
local command = helpers.command
|
||||
local eq = helpers.eq
|
||||
local eval = helpers.eval
|
||||
local expect_err = helpers.expect_err
|
||||
local feed = helpers.feed
|
||||
local map = helpers.map
|
||||
local nvim = helpers.nvim
|
||||
local parse_context = helpers.parse_context
|
||||
local trim = helpers.trim
|
||||
local write_file = helpers.write_file
|
||||
|
||||
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 buflist = call('map', call('getbufinfo'), 'v:val.name')
|
||||
call('ctxpush')
|
||||
call('ctxpush', {'buflist'})
|
||||
|
||||
command('%bwipeout')
|
||||
eq({''}, call('map', call('getbufinfo'), 'v:val.name'))
|
||||
|
||||
call('ctxpop')
|
||||
eq({'', unpack(buflist)}, call('map', call('getbufinfo'), 'v:val.name'))
|
||||
|
||||
command('%bwipeout')
|
||||
eq({''}, call('map', call('getbufinfo'), 'v:val.name'))
|
||||
|
||||
call('ctxpop')
|
||||
eq({'', unpack(buflist)}, call('map', call('getbufinfo'), 'v:val.name'))
|
||||
end)
|
||||
|
||||
it('saves and restores global variables properly', function()
|
||||
nvim('set_var', 'one', 1)
|
||||
nvim('set_var', 'Two', 2)
|
||||
nvim('set_var', 'THREE', 3)
|
||||
eq({1, 2 ,3}, eval('[g:one, g:Two, g:THREE]'))
|
||||
call('ctxpush')
|
||||
call('ctxpush', {'gvars'})
|
||||
|
||||
nvim('del_var', 'one')
|
||||
nvim('del_var', 'Two')
|
||||
nvim('del_var', 'THREE')
|
||||
expect_err('E121: Undefined variable: g:one', eval, 'g:one')
|
||||
expect_err('E121: Undefined variable: g:Two', eval, 'g:Two')
|
||||
expect_err('E121: Undefined variable: g:THREE', eval, 'g:THREE')
|
||||
|
||||
call('ctxpop')
|
||||
eq({1, 2 ,3}, eval('[g:one, g:Two, g:THREE]'))
|
||||
|
||||
nvim('del_var', 'one')
|
||||
nvim('del_var', 'Two')
|
||||
nvim('del_var', 'THREE')
|
||||
expect_err('E121: Undefined variable: g:one', eval, 'g:one')
|
||||
expect_err('E121: Undefined variable: g:Two', eval, 'g:Two')
|
||||
expect_err('E121: Undefined variable: g:THREE', eval, 'g:THREE')
|
||||
|
||||
call('ctxpop')
|
||||
eq({1, 2 ,3}, eval('[g:one, g:Two, g:THREE]'))
|
||||
end)
|
||||
|
||||
it('errors out when context stack is empty', function()
|
||||
local err = 'Vim:Context stack is empty'
|
||||
expect_err(err, call, 'ctxpop')
|
||||
expect_err(err, call, 'ctxpop')
|
||||
call('ctxpush')
|
||||
call('ctxpush')
|
||||
call('ctxpop')
|
||||
call('ctxpop')
|
||||
expect_err(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()
|
||||
expect_err(outofbounds, call, 'ctxget')
|
||||
call('ctxpush')
|
||||
expect_err(outofbounds, call, 'ctxget', 1)
|
||||
call('ctxpop')
|
||||
expect_err(outofbounds, call, 'ctxget', 0)
|
||||
end)
|
||||
|
||||
it('returns context dictionary at index in context stack', function()
|
||||
feed('i1<cr>2<cr>3<c-[>ddddddqahjklq')
|
||||
command('edit! '..fname1)
|
||||
feed('G')
|
||||
feed('gg')
|
||||
command('edit '..fname2)
|
||||
nvim('set_var', 'one', 1)
|
||||
nvim('set_var', 'Two', 2)
|
||||
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"), "l": v:val.lnum },
|
||||
{ k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)')
|
||||
]]):gsub('\n', ''))
|
||||
}
|
||||
|
||||
local with_buflist = {
|
||||
['buflist'] = 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'],
|
||||
['buflist'] = with_buflist['buflist'],
|
||||
['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', {'buflist'})
|
||||
eq(with_buflist, parse_context(call('ctxget')))
|
||||
eq(with_buflist, 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_buflist, 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_buflist, 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_buflist, 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_buflist, parse_context(call('ctxget')))
|
||||
eq(with_buflist, 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()
|
||||
expect_err(outofbounds, call, 'ctxset', {dummy = 1})
|
||||
call('ctxpush')
|
||||
expect_err(outofbounds, call, 'ctxset', {dummy = 1}, 1)
|
||||
call('ctxpop')
|
||||
expect_err(outofbounds, call, 'ctxset', {dummy = 1}, 0)
|
||||
end)
|
||||
|
||||
it('sets context dictionary at index in context stack', function()
|
||||
nvim('set_var', 'one', 1)
|
||||
nvim('set_var', 'Two', 2)
|
||||
nvim('set_var', 'THREE', 3)
|
||||
call('ctxpush')
|
||||
local ctx1 = call('ctxget')
|
||||
nvim('set_var', 'one', 'a')
|
||||
nvim('set_var', 'Two', 'b')
|
||||
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]'))
|
||||
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)
|
Loading…
Reference in New Issue
Block a user