1
0
forked from aniani/vim
Files
vim/src/evalfunc.c
Shougo Matsushita 19b718828d patch 9.1.0142: getregion() can be improved
Problem:  getregion() can be improved (after v9.1.120)
Solution: change getregion() implementation to use pos as lists and
          one optional {opt} dictionary (Shougo Matsushita)

Note: The following is a breaking change!

Currently, the getregion() function (included as of patch v9.1.120) takes
3 arguments: the first 2 arguments are strings, describing a position,
arg3 is the type string.

However, that is slightly inflexible, there is no way to specify
additional arguments. So let's instead change the function signature to:

getregion(pos1, pos2 [, {Dict}]) where both pos1 and pos2 are lists.
This is slightly cleaner, and gives us the flexibility to specify
additional arguments as key/value pairs to the optional Dict arg.

Now it supports the "type" key to specify the selection type
(characterwise, blockwise or linewise) and now in addition one can also
define the selection type, independently of what the 'selection' option
actually is.

Technically, this is a breaking change, but since the getregion()
Vimscript function is still quite new, this should be fine.

closes: #14090

Co-authored-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Shougo Matsushita <Shougo.Matsu@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-28 22:49:03 +01:00

11606 lines
284 KiB
C

/* vi:set ts=8 sts=4 sw=4 noet:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
* See README.txt for an overview of the Vim source code.
*/
/*
* evalfunc.c: Builtin functions
*/
#define USING_FLOAT_STUFF
#include "vim.h"
#if defined(FEAT_EVAL) || defined(PROTO)
#ifdef VMS
# include <float.h>
#endif
static void f_and(typval_T *argvars, typval_T *rettv);
#ifdef FEAT_BEVAL
static void f_balloon_gettext(typval_T *argvars, typval_T *rettv);
static void f_balloon_show(typval_T *argvars, typval_T *rettv);
# if defined(FEAT_BEVAL_TERM)
static void f_balloon_split(typval_T *argvars, typval_T *rettv);
# endif
#endif
static void f_byte2line(typval_T *argvars, typval_T *rettv);
static void f_call(typval_T *argvars, typval_T *rettv);
static void f_changenr(typval_T *argvars, typval_T *rettv);
static void f_char2nr(typval_T *argvars, typval_T *rettv);
static void f_charcol(typval_T *argvars, typval_T *rettv);
static void f_col(typval_T *argvars, typval_T *rettv);
static void f_confirm(typval_T *argvars, typval_T *rettv);
static void f_copy(typval_T *argvars, typval_T *rettv);
static void f_cursor(typval_T *argsvars, typval_T *rettv);
#ifdef MSWIN
static void f_debugbreak(typval_T *argvars, typval_T *rettv);
#endif
static void f_deepcopy(typval_T *argvars, typval_T *rettv);
static void f_did_filetype(typval_T *argvars, typval_T *rettv);
static void f_echoraw(typval_T *argvars, typval_T *rettv);
static void f_empty(typval_T *argvars, typval_T *rettv);
static void f_environ(typval_T *argvars, typval_T *rettv);
static void f_err_teapot(typval_T *argvars, typval_T *rettv);
static void f_escape(typval_T *argvars, typval_T *rettv);
static void f_eval(typval_T *argvars, typval_T *rettv);
static void f_eventhandler(typval_T *argvars, typval_T *rettv);
static void f_execute(typval_T *argvars, typval_T *rettv);
static void f_exists_compiled(typval_T *argvars, typval_T *rettv);
static void f_expand(typval_T *argvars, typval_T *rettv);
static void f_expandcmd(typval_T *argvars, typval_T *rettv);
static void f_feedkeys(typval_T *argvars, typval_T *rettv);
static void f_fnameescape(typval_T *argvars, typval_T *rettv);
static void f_foreground(typval_T *argvars, typval_T *rettv);
static void f_funcref(typval_T *argvars, typval_T *rettv);
static void f_function(typval_T *argvars, typval_T *rettv);
static void f_garbagecollect(typval_T *argvars, typval_T *rettv);
static void f_get(typval_T *argvars, typval_T *rettv);
static void f_getchangelist(typval_T *argvars, typval_T *rettv);
static void f_getcharpos(typval_T *argvars, typval_T *rettv);
static void f_getcharsearch(typval_T *argvars, typval_T *rettv);
static void f_getcurpos(typval_T *argvars, typval_T *rettv);
static void f_getcursorcharpos(typval_T *argvars, typval_T *rettv);
static void f_getenv(typval_T *argvars, typval_T *rettv);
static void f_getfontname(typval_T *argvars, typval_T *rettv);
static void f_getjumplist(typval_T *argvars, typval_T *rettv);
static void f_getpid(typval_T *argvars, typval_T *rettv);
static void f_getpos(typval_T *argvars, typval_T *rettv);
static void f_getreg(typval_T *argvars, typval_T *rettv);
static void f_getreginfo(typval_T *argvars, typval_T *rettv);
static void f_getregion(typval_T *argvars, typval_T *rettv);
static void f_getregtype(typval_T *argvars, typval_T *rettv);
static void f_gettagstack(typval_T *argvars, typval_T *rettv);
static void f_gettext(typval_T *argvars, typval_T *rettv);
static void f_haslocaldir(typval_T *argvars, typval_T *rettv);
static void f_hlID(typval_T *argvars, typval_T *rettv);
static void f_hlexists(typval_T *argvars, typval_T *rettv);
static void f_hostname(typval_T *argvars, typval_T *rettv);
static void f_index(typval_T *argvars, typval_T *rettv);
static void f_indexof(typval_T *argvars, typval_T *rettv);
static void f_input(typval_T *argvars, typval_T *rettv);
static void f_inputdialog(typval_T *argvars, typval_T *rettv);
static void f_inputlist(typval_T *argvars, typval_T *rettv);
static void f_inputrestore(typval_T *argvars, typval_T *rettv);
static void f_inputsave(typval_T *argvars, typval_T *rettv);
static void f_inputsecret(typval_T *argvars, typval_T *rettv);
static void f_interrupt(typval_T *argvars, typval_T *rettv);
static void f_invert(typval_T *argvars, typval_T *rettv);
static void f_islocked(typval_T *argvars, typval_T *rettv);
static void f_keytrans(typval_T *argvars, typval_T *rettv);
static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv);
static void f_libcall(typval_T *argvars, typval_T *rettv);
static void f_libcallnr(typval_T *argvars, typval_T *rettv);
static void f_line(typval_T *argvars, typval_T *rettv);
static void f_line2byte(typval_T *argvars, typval_T *rettv);
#ifdef FEAT_LUA
static void f_luaeval(typval_T *argvars, typval_T *rettv);
#endif
static void f_match(typval_T *argvars, typval_T *rettv);
static void f_matchbufline(typval_T *argvars, typval_T *rettv);
static void f_matchend(typval_T *argvars, typval_T *rettv);
static void f_matchlist(typval_T *argvars, typval_T *rettv);
static void f_matchstr(typval_T *argvars, typval_T *rettv);
static void f_matchstrlist(typval_T *argvars, typval_T *rettv);
static void f_matchstrpos(typval_T *argvars, typval_T *rettv);
static void f_max(typval_T *argvars, typval_T *rettv);
static void f_min(typval_T *argvars, typval_T *rettv);
#ifdef FEAT_MZSCHEME
static void f_mzeval(typval_T *argvars, typval_T *rettv);
#endif
static void f_nextnonblank(typval_T *argvars, typval_T *rettv);
static void f_nr2char(typval_T *argvars, typval_T *rettv);
static void f_or(typval_T *argvars, typval_T *rettv);
#ifdef FEAT_PERL
static void f_perleval(typval_T *argvars, typval_T *rettv);
#endif
static void f_prevnonblank(typval_T *argvars, typval_T *rettv);
static void f_printf(typval_T *argvars, typval_T *rettv);
static void f_pum_getpos(typval_T *argvars, typval_T *rettv);
static void f_pumvisible(typval_T *argvars, typval_T *rettv);
#ifdef FEAT_PYTHON3
static void f_py3eval(typval_T *argvars, typval_T *rettv);
#endif
#ifdef FEAT_PYTHON
static void f_pyeval(typval_T *argvars, typval_T *rettv);
#endif
#if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3)
static void f_pyxeval(typval_T *argvars, typval_T *rettv);
#endif
static void f_test_srand_seed(typval_T *argvars, typval_T *rettv);
static void f_rand(typval_T *argvars, typval_T *rettv);
static void f_range(typval_T *argvars, typval_T *rettv);
static void f_reg_executing(typval_T *argvars, typval_T *rettv);
static void f_reg_recording(typval_T *argvars, typval_T *rettv);
static void f_rename(typval_T *argvars, typval_T *rettv);
static void f_repeat(typval_T *argvars, typval_T *rettv);
#ifdef FEAT_RUBY
static void f_rubyeval(typval_T *argvars, typval_T *rettv);
#endif
static void f_screenattr(typval_T *argvars, typval_T *rettv);
static void f_screenchar(typval_T *argvars, typval_T *rettv);
static void f_screenchars(typval_T *argvars, typval_T *rettv);
static void f_screencol(typval_T *argvars, typval_T *rettv);
static void f_screenrow(typval_T *argvars, typval_T *rettv);
static void f_screenstring(typval_T *argvars, typval_T *rettv);
static void f_search(typval_T *argvars, typval_T *rettv);
static void f_searchdecl(typval_T *argvars, typval_T *rettv);
static void f_searchpair(typval_T *argvars, typval_T *rettv);
static void f_searchpairpos(typval_T *argvars, typval_T *rettv);
static void f_searchpos(typval_T *argvars, typval_T *rettv);
static void f_setcharpos(typval_T *argvars, typval_T *rettv);
static void f_setcharsearch(typval_T *argvars, typval_T *rettv);
static void f_setcursorcharpos(typval_T *argvars, typval_T *rettv);
static void f_setenv(typval_T *argvars, typval_T *rettv);
static void f_setfperm(typval_T *argvars, typval_T *rettv);
static void f_setpos(typval_T *argvars, typval_T *rettv);
static void f_setreg(typval_T *argvars, typval_T *rettv);
static void f_settagstack(typval_T *argvars, typval_T *rettv);
#ifdef FEAT_CRYPT
static void f_sha256(typval_T *argvars, typval_T *rettv);
#endif
static void f_shellescape(typval_T *argvars, typval_T *rettv);
static void f_shiftwidth(typval_T *argvars, typval_T *rettv);
static void f_soundfold(typval_T *argvars, typval_T *rettv);
static void f_spellbadword(typval_T *argvars, typval_T *rettv);
static void f_spellsuggest(typval_T *argvars, typval_T *rettv);
static void f_split(typval_T *argvars, typval_T *rettv);
static void f_srand(typval_T *argvars, typval_T *rettv);
static void f_submatch(typval_T *argvars, typval_T *rettv);
static void f_substitute(typval_T *argvars, typval_T *rettv);
static void f_swapfilelist(typval_T *argvars, typval_T *rettv);
static void f_swapinfo(typval_T *argvars, typval_T *rettv);
static void f_swapname(typval_T *argvars, typval_T *rettv);
static void f_synID(typval_T *argvars, typval_T *rettv);
static void f_synIDattr(typval_T *argvars, typval_T *rettv);
static void f_synIDtrans(typval_T *argvars, typval_T *rettv);
static void f_synstack(typval_T *argvars, typval_T *rettv);
static void f_synconcealed(typval_T *argvars, typval_T *rettv);
static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv);
static void f_taglist(typval_T *argvars, typval_T *rettv);
static void f_tagfiles(typval_T *argvars, typval_T *rettv);
static void f_type(typval_T *argvars, typval_T *rettv);
static void f_virtcol(typval_T *argvars, typval_T *rettv);
static void f_visualmode(typval_T *argvars, typval_T *rettv);
static void f_wildmenumode(typval_T *argvars, typval_T *rettv);
static void f_windowsversion(typval_T *argvars, typval_T *rettv);
static void f_wordcount(typval_T *argvars, typval_T *rettv);
static void f_xor(typval_T *argvars, typval_T *rettv);
/*
* Functions that check the argument type of a builtin function.
* Each function returns FAIL and gives an error message if the type is wrong.
*/
// Context passed to an arg_ function.
typedef struct {
int arg_count; // actual argument count
type2_T *arg_types; // list of argument types
int arg_idx; // current argument index (first arg is zero)
cctx_T *arg_cctx;
} argcontext_T;
// A function to check one argument type. The first argument is the type to
// check. If needed, other argument types can be obtained with the context.
// E.g. if "arg_idx" is 1, then (type - 1) is the first argument type.
// NOTE: Use "arg_any", not NULL, in funcentry_T.f_argcheck array
// to accept an argument of any type.
typedef int (*argcheck_T)(type_T *, type_T *, argcontext_T *);
/*
* Call need_type() to check an argument type.
*/
static int
check_arg_type(
type_T *expected,
type_T *actual,
argcontext_T *context)
{
return need_type(actual, expected, FALSE,
context->arg_idx - context->arg_count, context->arg_idx + 1,
context->arg_cctx, FALSE, FALSE);
}
/*
* Call need_type() to check an argument type and that it is modifiable
*/
static int
check_arg_type_mod(
type_T *expected,
type_T *actual,
argcontext_T *context)
{
if (need_type(actual, expected, FALSE,
context->arg_idx - context->arg_count, context->arg_idx + 1,
context->arg_cctx, FALSE, FALSE) == FAIL)
return FAIL;
return arg_type_modifiable(actual, context->arg_idx + 1);
}
/*
* Give an error if "type" is a constant.
*/
int
arg_type_modifiable(type_T *type, int arg_idx)
{
char *tofree;
if ((type->tt_flags & TTFLAG_CONST) == 0)
return OK;
semsg(_(e_argument_nr_trying_to_modify_const_str),
arg_idx, type_name(type, &tofree));
vim_free(tofree);
return FAIL;
}
/*
* Return OK for any type unconditionally.
*/
static int
arg_any(type_T *type UNUSED,
type_T *decl_type UNUSED,
argcontext_T *context UNUSED)
{
return OK;
}
/*
* Check "type" is a float or a number.
*/
static int
arg_float_or_nr(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_FLOAT
|| type->tt_type == VAR_NUMBER
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_number, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" is a number.
*/
static int
arg_number(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
return check_arg_type(&t_number, type, context);
}
/*
* Check "type" is an object.
*/
static int
arg_object(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_OBJECT
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_object, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" is a dict of 'any'.
*/
static int
arg_dict_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
return check_arg_type(&t_dict_any, type, context);
}
/*
* Check "type" is a list of 'any'.
*/
static int
arg_list_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
return check_arg_type(&t_list_any, type, context);
}
/*
* Check "type" is a list of 'any' and modifiable
*/
static int
arg_list_any_mod(
type_T *type,
type_T *decl_type UNUSED,
argcontext_T *context)
{
return check_arg_type_mod(&t_list_any, type, context);
}
/*
* Check "type" is a list of numbers.
*/
static int
arg_list_number(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
return check_arg_type(&t_list_number, type, context);
}
/*
* Check "type" is a list of strings.
*/
static int
arg_list_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
return check_arg_type(&t_list_string, type, context);
}
/*
* Check "type" is a string.
*/
static int
arg_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
return check_arg_type(&t_string, type, context);
}
/*
* Check "type" is a blob
*/
static int
arg_blob(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
return check_arg_type(&t_blob, type, context);
}
/*
* Check "type" is a bool or number 0 or 1.
*/
static int
arg_bool(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
return check_arg_type(&t_bool, type, context);
}
/*
* Check "type" is a list of 'any' or a blob.
*/
static int
arg_list_or_blob(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_LIST
|| type->tt_type == VAR_BLOB
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_list_any, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" is a modifiable list of 'any' or a blob.
*/
static int
arg_list_or_blob_mod(
type_T *type,
type_T *decl_type,
argcontext_T *context)
{
if (arg_list_or_blob(type, decl_type, context) == FAIL)
return FAIL;
return arg_type_modifiable(type, context->arg_idx + 1);
}
/*
* Check "type" is a string or a number
*/
static int
arg_string_or_nr(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_STRING
|| type->tt_type == VAR_NUMBER
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_string, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" is a buffer (string or a number)
*/
static int
arg_buffer(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_STRING
|| type->tt_type == VAR_NUMBER
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_string, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" is a buffer or a dict of any
*/
static int
arg_buffer_or_dict_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_STRING
|| type->tt_type == VAR_NUMBER
|| type->tt_type == VAR_DICT
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_string, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" is a line (string or a number)
*/
static int
arg_lnum(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_STRING
|| type->tt_type == VAR_NUMBER
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_string, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" is a string or a list of strings.
*/
static int
arg_string_or_list_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_STRING
|| type_any_or_unknown(type))
return OK;
if (type->tt_type != VAR_LIST)
{
arg_type_mismatch(&t_string, type, context->arg_idx + 1);
return FAIL;
}
if (type->tt_member->tt_type == VAR_ANY
|| type->tt_member->tt_type == VAR_STRING)
return OK;
arg_type_mismatch(&t_list_string, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" is a string or a list of 'any'
*/
static int
arg_string_or_list_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_STRING
|| type->tt_type == VAR_LIST
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_string, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" is a string or a dict of 'any'
*/
static int
arg_string_or_dict_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_STRING
|| type->tt_type == VAR_DICT
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_string, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" is a string or a blob
*/
static int
arg_string_or_blob(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_STRING
|| type->tt_type == VAR_BLOB
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_string, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" is a list of 'any' or a dict of 'any'.
*/
static int
arg_list_or_dict(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_LIST
|| type->tt_type == VAR_DICT
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_list_any, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" is a list of 'any' or a dict of 'any'. And modifiable.
*/
static int
arg_list_or_dict_mod(
type_T *type,
type_T *decl_type,
argcontext_T *context)
{
if (arg_list_or_dict(type, decl_type, context) == FAIL)
return FAIL;
return arg_type_modifiable(type, context->arg_idx + 1);
}
/*
* Check "type" is a list of 'any' or a dict of 'any' or a blob.
* Also check if "type" is modifiable.
*/
static int
arg_list_or_dict_or_blob_mod(
type_T *type,
type_T *decl_type UNUSED,
argcontext_T *context)
{
if (type->tt_type == VAR_LIST
|| type->tt_type == VAR_DICT
|| type->tt_type == VAR_BLOB
|| type_any_or_unknown(type))
return arg_type_modifiable(type, context->arg_idx + 1);
arg_type_mismatch(&t_list_any, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" is a list of 'any' or a dict of 'any' or a blob or a string.
*/
static int
arg_list_or_dict_or_blob_or_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_LIST
|| type->tt_type == VAR_DICT
|| type->tt_type == VAR_BLOB
|| type->tt_type == VAR_STRING
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_list_any, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" is a list of 'any' or a dict of 'any' or a blob or a string.
* Also check the value is modifiable.
*/
static int
arg_list_or_dict_or_blob_or_string_mod(
type_T *type,
type_T *decl_type,
argcontext_T *context)
{
if (arg_list_or_dict_or_blob_or_string(type, decl_type, context) == FAIL)
return FAIL;
return arg_type_modifiable(type, context->arg_idx + 1);
}
/*
* Check second argument of map(), filter(), foreach().
*/
static int
check_map_filter_arg2(type_T *type, argcontext_T *context,
filtermap_T filtermap)
{
type_T *expected_member = NULL;
type_T *(args[2]);
type_T t_func_exp = {VAR_FUNC, 2, 0, 0, NULL, NULL, args};
if (context->arg_types[0].type_curr->tt_type == VAR_LIST
|| context->arg_types[0].type_curr->tt_type == VAR_DICT)
{
// Use the declared type if possible, so that an error is given if
// a declared list changes type, but not if a constant list changes
// type.
if (context->arg_types[0].type_decl->tt_type == VAR_LIST
|| context->arg_types[0].type_decl->tt_type == VAR_DICT)
expected_member = context->arg_types[0].type_decl->tt_member;
else
expected_member = context->arg_types[0].type_curr->tt_member;
}
else if (context->arg_types[0].type_curr->tt_type == VAR_STRING)
expected_member = &t_string;
else if (context->arg_types[0].type_curr->tt_type == VAR_BLOB)
expected_member = &t_number;
args[0] = NULL;
args[1] = &t_unknown;
if (type->tt_argcount != -1)
{
if (!(type->tt_argcount == 2 || (type->tt_argcount == 1
&& (type->tt_flags & TTFLAG_VARARGS))))
{
emsg(_(e_invalid_number_of_arguments));
return FAIL;
}
if (type->tt_flags & TTFLAG_VARARGS)
// check the argument types at runtime
t_func_exp.tt_argcount = -1;
else
{
if (context->arg_types[0].type_curr->tt_type == VAR_STRING
|| context->arg_types[0].type_curr->tt_type == VAR_BLOB
|| context->arg_types[0].type_curr->tt_type == VAR_LIST)
args[0] = &t_number;
else if (context->arg_types[0].type_decl->tt_type == VAR_DICT)
args[0] = &t_string;
if (args[0] != NULL)
args[1] = expected_member;
}
}
if (!type_any_or_unknown(type->tt_member) || args[0] != NULL)
{
where_T where = WHERE_INIT;
if (filtermap == FILTERMAP_MAP)
t_func_exp.tt_member = expected_member == NULL
|| type_any_or_unknown(type->tt_member)
? &t_any : expected_member;
else if (filtermap == FILTERMAP_FILTER)
t_func_exp.tt_member = &t_bool;
else // filtermap == FILTERMAP_FOREACH
t_func_exp.tt_member = &t_unknown;
if (args[0] == NULL)
args[0] = &t_unknown;
if (type->tt_argcount == -1)
t_func_exp.tt_argcount = -1;
where.wt_index = 2;
where.wt_kind = WT_ARGUMENT;
return check_type(&t_func_exp, type, TRUE, where);
}
return OK;
}
/*
* Check second argument of filter(): func must return a bool.
*/
static int
arg_filter_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_STRING
|| type->tt_type == VAR_PARTIAL
|| type_any_or_unknown(type))
return OK;
if (type->tt_type == VAR_FUNC)
return check_map_filter_arg2(type, context, FILTERMAP_FILTER);
semsg(_(e_string_or_function_required_for_argument_nr), 2);
return FAIL;
}
/*
* Check second argument of map(), the function.
*/
static int
arg_map_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_STRING
|| type->tt_type == VAR_PARTIAL
|| type_any_or_unknown(type))
return OK;
if (type->tt_type == VAR_FUNC)
return check_map_filter_arg2(type, context, FILTERMAP_MAP);
semsg(_(e_string_or_function_required_for_argument_nr), 2);
return FAIL;
}
/*
* Check second argument of foreach(), the function.
*/
static int
arg_foreach_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_STRING
|| type->tt_type == VAR_PARTIAL
|| type_any_or_unknown(type))
return OK;
if (type->tt_type == VAR_FUNC)
return check_map_filter_arg2(type, context, FILTERMAP_FOREACH);
semsg(_(e_string_or_function_required_for_argument_nr), 2);
return FAIL;
}
/*
* Check second argument of sort() and uniq(), the "how" argument.
*/
static int
arg_sort_how(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_STRING
|| type->tt_type == VAR_PARTIAL
|| type_any_or_unknown(type))
return OK;
if (type->tt_type == VAR_FUNC)
{
type_T *(args[2]);
type_T t_func_exp = {VAR_FUNC, 2, 0, 0, &t_number, NULL, args};
if (context->arg_types[0].type_curr->tt_type == VAR_LIST)
args[0] = context->arg_types[0].type_curr->tt_member;
else
args[0] = &t_unknown;
if (!type_any_or_unknown(type->tt_member) || args[0] != &t_unknown)
{
where_T where = WHERE_INIT;
args[1] = args[0];
if (type->tt_argcount == -1)
t_func_exp.tt_argcount = -1;
where.wt_index = 2;
where.wt_kind = WT_ARGUMENT;
return check_type(&t_func_exp, type, TRUE, where);
}
return OK;
}
semsg(_(e_string_or_function_required_for_argument_nr), 2);
return FAIL;
}
/*
* Check an expression argument, can be a string, funcref or partial.
* Also accept a bool, a constant resulting from compiling a string argument.
* Also accept a number, one and zero are accepted.
*/
static int
arg_string_or_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_STRING
|| type->tt_type == VAR_PARTIAL
|| type->tt_type == VAR_FUNC
|| type->tt_type == VAR_BOOL
|| type->tt_type == VAR_NUMBER
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_func_any, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check varargs' "type" are class.
*/
static int
varargs_class(type_T *type UNUSED,
type_T *decl_type UNUSED,
argcontext_T *context)
{
for (int i = context->arg_idx; i < context->arg_count; ++i)
{
type2_T *types = &context->arg_types[i];
if (types->type_curr->tt_type != VAR_CLASS)
{
semsg(_(e_class_or_typealias_required_for_argument_nr), i + 1);
return FAIL;
}
}
return OK;
}
/*
* Check "type" is a list of 'any' or a blob or a string.
*/
static int
arg_string_list_or_blob(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_LIST
|| type->tt_type == VAR_BLOB
|| type->tt_type == VAR_STRING
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_list_any, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" is a modifiable list of 'any' or a blob or a string.
*/
static int
arg_string_list_or_blob_mod(type_T *type, type_T *decl_type, argcontext_T *context)
{
if (arg_string_list_or_blob(type, decl_type, context) == FAIL)
return FAIL;
return arg_type_modifiable(type, context->arg_idx + 1);
}
/*
* Check "type" is a job.
*/
static int
arg_job(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
return check_arg_type(&t_job, type, context);
}
/*
* Check "type" is a channel or a job.
*/
static int
arg_chan_or_job(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_CHANNEL
|| type->tt_type == VAR_JOB
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_channel, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" can be used as the type_decl of the previous argument.
* Must not be used for the first argcheck_T entry.
*/
static int
arg_same_as_prev(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
type_T *prev_type = context->arg_types[context->arg_idx - 1].type_decl;
return check_arg_type(prev_type, type, context);
}
/*
* Check "type" is the same basic type as the previous argument, checks list or
* dict vs other type, but not member type.
* Must not be used for the first argcheck_T entry.
*/
static int
arg_same_struct_as_prev(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
type_T *prev_type = context->arg_types[context->arg_idx - 1].type_curr;
if (prev_type->tt_type != context->arg_types[context->arg_idx].type_curr->tt_type)
return check_arg_type(prev_type, type, context);
return OK;
}
/*
* Check "type" is an item of the list or blob of the previous arg.
* Must not be used for the first argcheck_T entry.
*/
static int
arg_item_of_prev(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
type_T *prev_type = context->arg_types[context->arg_idx - 1].type_curr;
type_T *expected;
if (prev_type->tt_type == VAR_LIST)
expected = prev_type->tt_member;
else if (prev_type->tt_type == VAR_BLOB)
expected = &t_number;
else
// probably VAR_ANY, can't check
return OK;
return check_arg_type(expected, type, context);
}
/*
* Check "type" is a string or a number or a list
*/
static int
arg_str_or_nr_or_list(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_STRING
|| type->tt_type == VAR_NUMBER
|| type->tt_type == VAR_LIST
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_string, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" is a dict of 'any' or a string
*/
static int
arg_dict_any_or_string(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_DICT
|| type->tt_type == VAR_STRING
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_string, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" which is the third argument of extend() (number or string or
* any)
*/
static int
arg_extend3(type_T *type, type_T *decl_type, argcontext_T *context)
{
type_T *first_type = context->arg_types[context->arg_idx - 2].type_curr;
if (first_type->tt_type == VAR_LIST)
return arg_number(type, decl_type, context);
if (first_type->tt_type == VAR_DICT)
return arg_string(type, decl_type, context);
return OK;
}
/*
* Check "type" which is the first argument of get() (blob or list or dict or
* funcref)
*/
static int
arg_get1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_BLOB
|| type->tt_type == VAR_LIST
|| type->tt_type == VAR_DICT
|| type->tt_type == VAR_FUNC
|| type->tt_type == VAR_PARTIAL
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_list_any, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" which is the first argument of len() (number or string or
* blob or list or dict)
*/
static int
arg_len1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_STRING
|| type->tt_type == VAR_NUMBER
|| type->tt_type == VAR_BLOB
|| type->tt_type == VAR_LIST
|| type->tt_type == VAR_DICT
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_list_any, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" which is the second argument of remove() (number or string or
* any)
*/
static int
arg_remove2(type_T *type, type_T *decl_type, argcontext_T *context)
{
type_T *first_type = context->arg_types[context->arg_idx - 1].type_curr;
if (first_type->tt_type == VAR_LIST || first_type->tt_type == VAR_BLOB)
return arg_number(type, decl_type, context);
if (first_type->tt_type == VAR_DICT)
return arg_string_or_nr(type, decl_type, context);
return OK;
}
/*
* Check "type" which is the first argument of repeat() (string or number or
* list or any)
*/
static int
arg_repeat1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_STRING
|| type->tt_type == VAR_NUMBER
|| type->tt_type == VAR_BLOB
|| type->tt_type == VAR_LIST
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_string, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" which is the first argument of slice() (list or blob or string
* or any)
*/
static int
arg_slice1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_LIST
|| type->tt_type == VAR_BLOB
|| type->tt_type == VAR_STRING
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_list_any, type, context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" which is the first argument of count() (string or list or dict
* or any)
*/
static int
arg_string_or_list_or_dict(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_STRING
|| type->tt_type == VAR_LIST
|| type->tt_type == VAR_DICT
|| type_any_or_unknown(type))
return OK;
semsg(_(e_string_list_or_dict_required_for_argument_nr),
context->arg_idx + 1);
return FAIL;
}
/*
* Check "type" which is the first argument of cursor() (number or string or
* list or any)
*/
static int
arg_cursor1(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_NUMBER
|| type->tt_type == VAR_STRING
|| type->tt_type == VAR_LIST
|| type_any_or_unknown(type))
return OK;
arg_type_mismatch(&t_number, type, context->arg_idx + 1);
return FAIL;
}
/*
* Lists of functions that check the argument types of a builtin function.
*/
static argcheck_T arg1_blob[] = {arg_blob};
static argcheck_T arg1_bool[] = {arg_bool};
static argcheck_T arg1_buffer[] = {arg_buffer};
static argcheck_T arg1_buffer_or_dict_any[] = {arg_buffer_or_dict_any};
static argcheck_T arg1_chan_or_job[] = {arg_chan_or_job};
static argcheck_T arg1_dict_any[] = {arg_dict_any};
static argcheck_T arg1_dict_or_string[] = {arg_dict_any_or_string};
static argcheck_T arg1_float_or_nr[] = {arg_float_or_nr};
static argcheck_T arg1_job[] = {arg_job};
static argcheck_T arg1_list_any[] = {arg_list_any};
static argcheck_T arg1_list_number[] = {arg_list_number};
static argcheck_T arg1_string_or_list_or_blob_mod[] = {arg_string_list_or_blob_mod};
static argcheck_T arg1_list_or_dict[] = {arg_list_or_dict};
static argcheck_T arg1_list_string[] = {arg_list_string};
static argcheck_T arg1_string_or_list_or_dict[] = {arg_string_or_list_or_dict};
static argcheck_T arg1_lnum[] = {arg_lnum};
static argcheck_T arg1_number[] = {arg_number};
static argcheck_T arg1_string[] = {arg_string};
static argcheck_T arg1_string_or_list_any[] = {arg_string_or_list_any};
static argcheck_T arg1_string_or_list_string[] = {arg_string_or_list_string};
static argcheck_T arg1_string_or_nr[] = {arg_string_or_nr};
static argcheck_T arg2_any_buffer[] = {arg_any, arg_buffer};
static argcheck_T arg2_buffer_any[] = {arg_buffer, arg_any};
static argcheck_T arg2_buffer_bool[] = {arg_buffer, arg_bool};
static argcheck_T arg2_buffer_list_any[] = {arg_buffer, arg_list_any};
static argcheck_T arg2_buffer_lnum[] = {arg_buffer, arg_lnum};
static argcheck_T arg2_buffer_number[] = {arg_buffer, arg_number};
static argcheck_T arg2_buffer_string[] = {arg_buffer, arg_string};
static argcheck_T arg2_chan_or_job_dict[] = {arg_chan_or_job, arg_dict_any};
static argcheck_T arg2_chan_or_job_string[] = {arg_chan_or_job, arg_string};
static argcheck_T arg2_dict_any_list_any[] = {arg_dict_any, arg_list_any};
static argcheck_T arg2_dict_any_string_or_nr[] = {arg_dict_any, arg_string_or_nr};
static argcheck_T arg2_dict_string[] = {arg_dict_any, arg_string};
static argcheck_T arg2_float_or_nr[] = {arg_float_or_nr, arg_float_or_nr};
static argcheck_T arg2_job_dict[] = {arg_job, arg_dict_any};
static argcheck_T arg2_job_string_or_number[] = {arg_job, arg_string_or_nr};
static argcheck_T arg2_list_any_number[] = {arg_list_any, arg_number};
static argcheck_T arg2_list_any_string[] = {arg_list_any, arg_string};
static argcheck_T arg2_list_number[] = {arg_list_number, arg_list_number};
static argcheck_T arg2_list_number_bool[] = {arg_list_number, arg_bool};
static argcheck_T arg2_listblobmod_item[] = {arg_list_or_blob_mod, arg_item_of_prev};
static argcheck_T arg2_lnum[] = {arg_lnum, arg_lnum};
static argcheck_T arg2_lnum_number[] = {arg_lnum, arg_number};
static argcheck_T arg2_number[] = {arg_number, arg_number};
static argcheck_T arg2_number_any[] = {arg_number, arg_any};
static argcheck_T arg2_number_bool[] = {arg_number, arg_bool};
static argcheck_T arg2_number_dict_any[] = {arg_number, arg_dict_any};
static argcheck_T arg2_number_list[] = {arg_number, arg_list_any};
static argcheck_T arg2_number_string[] = {arg_number, arg_string};
static argcheck_T arg2_number_string_or_list[] = {arg_number, arg_string_or_list_any};
static argcheck_T arg2_str_or_nr_or_list_dict[] = {arg_str_or_nr_or_list, arg_dict_any};
static argcheck_T arg2_string[] = {arg_string, arg_string};
static argcheck_T arg2_string_any[] = {arg_string, arg_any};
static argcheck_T arg2_string_bool[] = {arg_string, arg_bool};
static argcheck_T arg2_string_chan_or_job[] = {arg_string, arg_chan_or_job};
static argcheck_T arg2_string_dict[] = {arg_string, arg_dict_any};
static argcheck_T arg2_string_list_number[] = {arg_string, arg_list_number};
static argcheck_T arg2_string_number[] = {arg_string, arg_number};
static argcheck_T arg2_string_or_list_dict[] = {arg_string_or_list_any, arg_dict_any};
static argcheck_T arg2_string_or_list_number[] = {arg_string_or_list_any, arg_number};
static argcheck_T arg2_string_string_or_number[] = {arg_string, arg_string_or_nr};
static argcheck_T arg3_any_list_dict[] = {arg_any, arg_list_any, arg_dict_any};
static argcheck_T arg3_buffer_lnum_lnum[] = {arg_buffer, arg_lnum, arg_lnum};
static argcheck_T arg3_buffer_number_number[] = {arg_buffer, arg_number, arg_number};
static argcheck_T arg3_buffer_string_any[] = {arg_buffer, arg_string, arg_any};
static argcheck_T arg3_buffer_string_dict[] = {arg_buffer, arg_string, arg_dict_any};
static argcheck_T arg3_dict_number_number[] = {arg_dict_any, arg_number, arg_number};
static argcheck_T arg3_diff[] = {arg_list_string, arg_list_string, arg_dict_any};
static argcheck_T arg3_list_string_dict[] = {arg_list_any, arg_string, arg_dict_any};
static argcheck_T arg3_list_list_dict[] = {arg_list_any, arg_list_any, arg_dict_any};
static argcheck_T arg3_lnum_number_bool[] = {arg_lnum, arg_number, arg_bool};
static argcheck_T arg3_number[] = {arg_number, arg_number, arg_number};
static argcheck_T arg3_number_any_dict[] = {arg_number, arg_any, arg_dict_any};
static argcheck_T arg3_number_number_dict[] = {arg_number, arg_number, arg_dict_any};
static argcheck_T arg3_number_string_any[] = {arg_number, arg_string, arg_any};
static argcheck_T arg3_number_string_buffer[] = {arg_number, arg_string, arg_buffer};
static argcheck_T arg3_number_string_string[] = {arg_number, arg_string, arg_string};
static argcheck_T arg3_string[] = {arg_string, arg_string, arg_string};
static argcheck_T arg3_string_any_dict[] = {arg_string, arg_any, arg_dict_any};
static argcheck_T arg3_string_any_string[] = {arg_string, arg_any, arg_string};
static argcheck_T arg3_string_bool_bool[] = {arg_string, arg_bool, arg_bool};
static argcheck_T arg3_string_number_bool[] = {arg_string, arg_number, arg_bool};
static argcheck_T arg3_string_number_number[] = {arg_string, arg_number, arg_number};
static argcheck_T arg3_string_or_dict_bool_dict[] = {arg_string_or_dict_any, arg_bool, arg_dict_any};
static argcheck_T arg3_string_or_list_bool_number[] = {arg_string_or_list_any, arg_bool, arg_number};
static argcheck_T arg3_string_string_bool[] = {arg_string, arg_string, arg_bool};
static argcheck_T arg3_string_string_dict[] = {arg_string, arg_string, arg_dict_any};
static argcheck_T arg3_string_string_number[] = {arg_string, arg_string, arg_number};
static argcheck_T arg4_number_number_string_any[] = {arg_number, arg_number, arg_string, arg_any};
static argcheck_T arg4_string_string_any_string[] = {arg_string, arg_string, arg_any, arg_string};
static argcheck_T arg4_string_string_number_string[] = {arg_string, arg_string, arg_number, arg_string};
static argcheck_T arg4_string_number_bool_bool[] = {arg_string, arg_number, arg_bool, arg_bool};
/* Function specific argument types (not covered by the above) */
static argcheck_T arg15_assert_fails[] = {arg_string_or_nr, arg_string_or_list_any, arg_any, arg_number, arg_string};
static argcheck_T arg34_assert_inrange[] = {arg_float_or_nr, arg_float_or_nr, arg_float_or_nr, arg_string};
static argcheck_T arg4_browse[] = {arg_bool, arg_string, arg_string, arg_string};
static argcheck_T arg23_chanexpr[] = {arg_chan_or_job, arg_any, arg_dict_any};
static argcheck_T arg23_chanraw[] = {arg_chan_or_job, arg_string_or_blob, arg_dict_any};
static argcheck_T arg24_count[] = {arg_string_or_list_or_dict, arg_any, arg_bool, arg_number};
static argcheck_T arg13_cursor[] = {arg_cursor1, arg_number, arg_number};
static argcheck_T arg12_deepcopy[] = {arg_any, arg_bool};
static argcheck_T arg12_execute[] = {arg_string_or_list_string, arg_string};
static argcheck_T arg23_extend[] = {arg_list_or_dict_mod, arg_same_as_prev, arg_extend3};
static argcheck_T arg23_extendnew[] = {arg_list_or_dict, arg_same_struct_as_prev, arg_extend3};
static argcheck_T arg23_get[] = {arg_get1, arg_string_or_nr, arg_any};
static argcheck_T arg14_glob[] = {arg_string, arg_bool, arg_bool, arg_bool};
static argcheck_T arg25_globpath[] = {arg_string, arg_string, arg_bool, arg_bool, arg_bool};
static argcheck_T arg24_index[] = {arg_list_or_blob, arg_item_of_prev, arg_number, arg_bool};
static argcheck_T arg23_index[] = {arg_list_or_blob, arg_filter_func, arg_dict_any};
static argcheck_T arg23_insert[] = {arg_list_or_blob, arg_item_of_prev, arg_number};
static argcheck_T arg1_len[] = {arg_len1};
static argcheck_T arg3_libcall[] = {arg_string, arg_string, arg_string_or_nr};
static argcheck_T arg14_maparg[] = {arg_string, arg_string, arg_bool, arg_bool};
static argcheck_T arg2_filter[] = {arg_list_or_dict_or_blob_or_string_mod, arg_filter_func};
static argcheck_T arg2_foreach[] = {arg_list_or_dict_or_blob_or_string, arg_foreach_func};
static argcheck_T arg2_instanceof[] = {arg_object, varargs_class, NULL };
static argcheck_T arg2_map[] = {arg_list_or_dict_or_blob_or_string_mod, arg_map_func};
static argcheck_T arg2_mapnew[] = {arg_list_or_dict_or_blob_or_string, arg_any};
static argcheck_T arg25_matchadd[] = {arg_string, arg_string, arg_number, arg_number, arg_dict_any};
static argcheck_T arg25_matchaddpos[] = {arg_string, arg_list_any, arg_number, arg_number, arg_dict_any};
static argcheck_T arg23_matchstrlist[] = {arg_list_string, arg_string, arg_dict_any};
static argcheck_T arg45_matchbufline[] = {arg_buffer, arg_string, arg_lnum, arg_lnum, arg_dict_any};
static argcheck_T arg119_printf[] = {arg_string_or_nr, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any, arg_any};
static argcheck_T arg23_reduce[] = {arg_string_list_or_blob, arg_any, arg_any};
static argcheck_T arg24_remote_expr[] = {arg_string, arg_string, arg_string, arg_number};
static argcheck_T arg23_remove[] = {arg_list_or_dict_or_blob_mod, arg_remove2, arg_number};
static argcheck_T arg2_repeat[] = {arg_repeat1, arg_number};
static argcheck_T arg15_search[] = {arg_string, arg_string, arg_number, arg_number, arg_string_or_func};
static argcheck_T arg37_searchpair[] = {arg_string, arg_string, arg_string, arg_string, arg_string_or_func, arg_number, arg_number};
static argcheck_T arg3_setbufline[] = {arg_buffer, arg_lnum, arg_str_or_nr_or_list};
static argcheck_T arg2_setline[] = {arg_lnum, arg_any};
static argcheck_T arg24_setloclist[] = {arg_number, arg_list_any, arg_string, arg_dict_any};
static argcheck_T arg13_setqflist[] = {arg_list_any, arg_string, arg_dict_any};
static argcheck_T arg23_settagstack[] = {arg_number, arg_dict_any, arg_string};
static argcheck_T arg02_sign_getplaced[] = {arg_buffer, arg_dict_any};
static argcheck_T arg45_sign_place[] = {arg_number, arg_string, arg_string, arg_buffer, arg_dict_any};
static argcheck_T arg23_slice[] = {arg_slice1, arg_number, arg_number};
static argcheck_T arg13_sortuniq[] = {arg_list_any_mod, arg_sort_how, arg_dict_any};
static argcheck_T arg24_strpart[] = {arg_string, arg_number, arg_number, arg_bool};
static argcheck_T arg12_system[] = {arg_string, arg_str_or_nr_or_list};
static argcheck_T arg23_win_execute[] = {arg_number, arg_string_or_list_string, arg_string};
static argcheck_T arg23_writefile[] = {arg_list_or_blob, arg_string, arg_string};
static argcheck_T arg24_match_func[] = {arg_string_or_list_any, arg_string, arg_number, arg_number};
// Can be used by functions called through "f_retfunc" to create new types.
static garray_T *current_type_gap = NULL;
/*
* Functions that return the return type of a builtin function.
* Note that "argtypes" is NULL if "argcount" is zero.
*/
static type_T *
ret_void(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type UNUSED)
{
return &t_void;
}
static type_T *
ret_any(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type UNUSED)
{
return &t_any;
}
static type_T *
ret_bool(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type UNUSED)
{
return &t_bool;
}
static type_T *
ret_number_bool(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type UNUSED)
{
return &t_number_bool;
}
static type_T *
ret_number(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type UNUSED)
{
return &t_number;
}
static type_T *
ret_float(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type UNUSED)
{
return &t_float;
}
static type_T *
ret_string(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type UNUSED)
{
return &t_string;
}
static type_T *
ret_list_any(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type UNUSED)
{
return &t_list_any;
}
static type_T *
ret_list_number(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type)
{
*decl_type = &t_list_any;
return &t_list_number;
}
static type_T *
ret_list_string(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type)
{
*decl_type = &t_list_any;
return &t_list_string;
}
static type_T *
ret_list_dict_any(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type)
{
*decl_type = &t_list_any;
return &t_list_dict_any;
}
static type_T *
ret_list_items(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type)
{
*decl_type = &t_list_any;
return &t_list_list_any;
}
static type_T *
ret_list_string_items(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type)
{
*decl_type = &t_list_any;
return &t_list_list_string;
}
static type_T *
ret_dict_any(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type UNUSED)
{
return &t_dict_any;
}
static type_T *
ret_job_info(int argcount,
type2_T *argtypes UNUSED,
type_T **decl_type)
{
if (argcount == 0)
{
*decl_type = &t_list_any;
return &t_list_job;
}
return &t_dict_any;
}
static type_T *
ret_dict_number(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type UNUSED)
{
return &t_dict_number;
}
static type_T *
ret_dict_string(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type UNUSED)
{
return &t_dict_string;
}
static type_T *
ret_blob(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type UNUSED)
{
return &t_blob;
}
static type_T *
ret_func_any(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type UNUSED)
{
return &t_func_any;
}
static type_T *
ret_func_unknown(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type UNUSED)
{
return &t_func_unknown;
}
static type_T *
ret_channel(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type UNUSED)
{
return &t_channel;
}
static type_T *
ret_job(int argcount UNUSED,
type2_T *argtypes UNUSED,
type_T **decl_type UNUSED)
{
return &t_job;
}
static type_T *
ret_first_arg(int argcount,
type2_T *argtypes,
type_T **decl_type)
{
if (argcount > 0)
{
*decl_type = argtypes[0].type_decl;
return argtypes[0].type_curr;
}
return &t_void;
}
static type_T *
ret_slice(int argcount,
type2_T *argtypes,
type_T **decl_type)
{
if (argcount > 0)
{
if (argtypes[0].type_decl != NULL)
{
switch (argtypes[0].type_decl->tt_type)
{
case VAR_STRING: *decl_type = &t_string; break;
case VAR_BLOB: *decl_type = &t_blob; break;
case VAR_LIST: *decl_type = &t_list_any; break;
default: break;
}
}
return argtypes[0].type_curr;
}
return &t_void;
}
static type_T *
ret_copy(int argcount,
type2_T *argtypes,
type_T **decl_type)
{
if (argcount > 0)
{
if (argtypes[0].type_decl != NULL)
{
if (argtypes[0].type_decl->tt_type == VAR_LIST)
*decl_type = &t_list_any;
else if (argtypes[0].type_decl->tt_type == VAR_DICT)
*decl_type = &t_dict_any;
else
*decl_type = argtypes[0].type_decl;
}
if (argtypes[0].type_curr != NULL)
{
if (argtypes[0].type_curr->tt_type == VAR_LIST)
return &t_list_any;
else if (argtypes[0].type_curr->tt_type == VAR_DICT)
return &t_dict_any;
}
return argtypes[0].type_curr;
}
return &t_void;
}
static type_T *
ret_extend(int argcount,
type2_T *argtypes,
type_T **decl_type)
{
if (argcount > 0)
{
*decl_type = argtypes[0].type_decl;
// if the second argument has a different current type then the current
// type is "any"
if (argcount > 1 && !equal_type(argtypes[0].type_curr,
argtypes[1].type_curr, 0))
{
if (argtypes[0].type_curr->tt_type == VAR_LIST)
return &t_list_any;
if (argtypes[0].type_curr->tt_type == VAR_DICT)
return &t_dict_any;
}
return argtypes[0].type_curr;
}
return &t_void;
}
static type_T *
ret_repeat(int argcount,
type2_T *argtypes,
type_T **decl_type UNUSED)
{
if (argcount == 0)
return &t_any;
if (argtypes[0].type_curr == &t_number)
return &t_string;
return argtypes[0].type_curr;
}
// for map(): returns first argument but item type may differ
static type_T *
ret_first_cont(int argcount,
type2_T *argtypes,
type_T **decl_type UNUSED)
{
if (argcount > 0)
{
if (argtypes[0].type_curr->tt_type == VAR_LIST)
return &t_list_any;
if (argtypes[0].type_curr->tt_type == VAR_DICT)
return &t_dict_any;
if (argtypes[0].type_curr->tt_type == VAR_BLOB)
return argtypes[0].type_curr;
}
return &t_any;
}
// for getline()
static type_T *
ret_getline(int argcount,
type2_T *argtypes UNUSED,
type_T **decl_type UNUSED)
{
if (argcount == 1)
return &t_string;
*decl_type = &t_list_any;
return &t_list_string;
}
// for finddir()
static type_T *
ret_finddir(int argcount,
type2_T *argtypes UNUSED,
type_T **decl_type UNUSED)
{
if (argcount < 3)
return &t_string;
// Depending on the count would be a string or a list of strings.
return &t_any;
}
// for values(): list of member of first argument
static type_T *
ret_list_member(int argcount,
type2_T *argtypes,
type_T **decl_type)
{
if (argcount > 0)
{
type_T *t = argtypes[0].type_decl;
if (current_type_gap != NULL
&& (t->tt_type == VAR_DICT || t->tt_type == VAR_LIST))
t = get_list_type(t->tt_member, current_type_gap);
else
t = &t_list_any;
*decl_type = t;
t = argtypes[0].type_curr;
if (current_type_gap != NULL
&& (t->tt_type == VAR_DICT || t->tt_type == VAR_LIST))
return get_list_type(t->tt_member, current_type_gap);
}
return &t_list_any;
}
/*
* Used for getqflist(): returns list if there is no argument, dict if there is
* one.
*/
static type_T *
ret_list_or_dict_0(int argcount,
type2_T *argtypes UNUSED,
type_T **decl_type)
{
if (argcount > 0)
return &t_dict_any;
*decl_type = &t_list_any;
return &t_list_dict_any;
}
/*
* Used for getloclist(): returns list if there is one argument, dict if there
* are two.
*/
static type_T *
ret_list_or_dict_1(int argcount,
type2_T *argtypes UNUSED,
type_T **decl_type)
{
if (argcount > 1)
return &t_dict_any;
*decl_type = &t_list_any;
return &t_list_dict_any;
}
static type_T *
ret_argv(int argcount,
type2_T *argtypes UNUSED,
type_T **decl_type)
{
// argv() returns list of strings
if (argcount == 0)
{
*decl_type = &t_list_any;
return &t_list_string;
}
// argv(0) returns a string, but argv(-1] returns a list
return &t_any;
}
static type_T *
ret_remove(int argcount,
type2_T *argtypes,
type_T **decl_type)
{
if (argcount > 0)
{
if (argtypes[0].type_curr->tt_type == VAR_LIST
|| argtypes[0].type_curr->tt_type == VAR_DICT)
{
if (argcount == 3)
{
*decl_type = argtypes[0].type_decl;
return argtypes[0].type_curr;
}
if (argtypes[0].type_curr->tt_type
== argtypes[0].type_decl->tt_type)
*decl_type = argtypes[0].type_decl->tt_member;
return argtypes[0].type_curr->tt_member;
}
if (argtypes[0].type_curr->tt_type == VAR_BLOB)
return &t_number;
}
return &t_any;
}
static type_T *
ret_getreg(int argcount,
type2_T *argtypes UNUSED,
type_T **decl_type)
{
// Assume that if the third argument is passed it's non-zero
if (argcount == 3)
{
*decl_type = &t_list_any;
return &t_list_string;
}
return &t_string;
}
static type_T *
ret_virtcol(int argcount,
type2_T *argtypes UNUSED,
type_T **decl_type)
{
// Assume that if the second argument is passed it's non-zero
if (argcount > 1)
{
*decl_type = &t_list_any;
return &t_list_number;
}
return &t_number;
}
static type_T *
ret_maparg(int argcount,
type2_T *argtypes UNUSED,
type_T **decl_type UNUSED)
{
// Assume that if the fourth argument is passed it's non-zero
if (argcount == 4)
return &t_dict_any;
return &t_string;
}
/*
* Array with names and number of arguments of all internal functions
* MUST BE KEPT SORTED IN strcmp() ORDER FOR BINARY SEARCH!
*
* The builtin function may be varargs. In that case
* - f_max_argc == VARGS
* - For varargs, f_argcheck must be NULL terminated. The last non-null
* entry in f_argcheck should validate all the remaining args.
*/
typedef struct
{
char *f_name; // function name
char f_min_argc; // minimal number of arguments
char f_max_argc; // maximal number of arguments
char f_argtype; // for method: FEARG_ values; bits FE_
argcheck_T *f_argcheck; // list of functions to check argument types;
// use "arg_any" (not NULL) to accept an
// argument of any type
type_T *(*f_retfunc)(int argcount, type2_T *argtypes,
type_T **decl_type);
// return type function
void (*f_func)(typval_T *args, typval_T *rvar);
// implementation of function
} funcentry_T;
// Set f_max_argc to VARGS for varargs.
#define VARGS CHAR_MAX
// values for f_argtype; zero means it cannot be used as a method
#define FEARG_1 0x01 // base is the first argument
#define FEARG_2 0x02 // base is the second argument
#define FEARG_3 0x03 // base is the third argument
#define FEARG_4 0x04 // base is the fourth argument
#define FEARG_MASK 0x0F // bits in f_argtype used as argument index
#define FE_X 0x10 // builtin accepts a non-value (class, typealias)
#if defined(HAVE_MATH_H)
# define MATH_FUNC(name) name
#else
# define MATH_FUNC(name) NULL
#endif
#ifdef FEAT_TIMERS
# define TIMER_FUNC(name) name
#else
# define TIMER_FUNC(name) NULL
#endif
#ifdef FEAT_JOB_CHANNEL
# define JOB_FUNC(name) name
#else
# define JOB_FUNC(name) NULL
#endif
#ifdef FEAT_PROP_POPUP
# define PROP_FUNC(name) name
#else
# define PROP_FUNC(name) NULL
#endif
#ifdef FEAT_SIGNS
# define SIGN_FUNC(name) name
#else
# define SIGN_FUNC(name) NULL
#endif
#ifdef FEAT_SOUND
# define SOUND_FUNC(name) name
#else
# define SOUND_FUNC(name) NULL
#endif
#ifdef FEAT_TERMINAL
# define TERM_FUNC(name) name
#else
# define TERM_FUNC(name) NULL
#endif
static funcentry_T global_functions[] =
{
{"abs", 1, 1, FEARG_1, arg1_float_or_nr,
ret_any, f_abs},
{"acos", 1, 1, FEARG_1, arg1_float_or_nr,
ret_float, f_acos},
{"add", 2, 2, FEARG_1, arg2_listblobmod_item,
ret_first_arg, f_add},
{"and", 2, 2, FEARG_1, arg2_number,
ret_number, f_and},
{"append", 2, 2, FEARG_2, arg2_setline,
ret_number_bool, f_append},
{"appendbufline", 3, 3, FEARG_3, arg3_setbufline,
ret_number_bool, f_appendbufline},
{"argc", 0, 1, 0, arg1_number,
ret_number, f_argc},
{"argidx", 0, 0, 0, NULL,
ret_number, f_argidx},
{"arglistid", 0, 2, 0, arg2_number,
ret_number, f_arglistid},
{"argv", 0, 2, 0, arg2_number,
ret_argv, f_argv},
{"asin", 1, 1, FEARG_1, arg1_float_or_nr,
ret_float, f_asin},
{"assert_beeps", 1, 1, FEARG_1, arg1_string,
ret_number_bool, f_assert_beeps},
{"assert_equal", 2, 3, FEARG_2, NULL,
ret_number_bool, f_assert_equal},
{"assert_equalfile", 2, 3, FEARG_1, arg3_string,
ret_number_bool, f_assert_equalfile},
{"assert_exception", 1, 2, 0, arg2_string,
ret_number_bool, f_assert_exception},
{"assert_fails", 1, 5, FEARG_1, arg15_assert_fails,
ret_number_bool, f_assert_fails},
{"assert_false", 1, 2, FEARG_1, NULL,
ret_number_bool, f_assert_false},
{"assert_inrange", 3, 4, FEARG_3, arg34_assert_inrange,
ret_number_bool, f_assert_inrange},
{"assert_match", 2, 3, FEARG_2, arg3_string,
ret_number_bool, f_assert_match},
{"assert_nobeep", 1, 1, FEARG_1, arg1_string,
ret_number_bool, f_assert_nobeep},
{"assert_notequal", 2, 3, FEARG_2, NULL,
ret_number_bool, f_assert_notequal},
{"assert_notmatch", 2, 3, FEARG_2, arg3_string,
ret_number_bool, f_assert_notmatch},
{"assert_report", 1, 1, FEARG_1, arg1_string,
ret_number_bool, f_assert_report},
{"assert_true", 1, 2, FEARG_1, NULL,
ret_number_bool, f_assert_true},
{"atan", 1, 1, FEARG_1, arg1_float_or_nr,
ret_float, f_atan},
{"atan2", 2, 2, FEARG_1, arg2_float_or_nr,
ret_float, f_atan2},
{"autocmd_add", 1, 1, FEARG_1, arg1_list_any,
ret_number_bool, f_autocmd_add},
{"autocmd_delete", 1, 1, FEARG_1, arg1_list_any,
ret_number_bool, f_autocmd_delete},
{"autocmd_get", 0, 1, FEARG_1, arg1_dict_any,
ret_list_dict_any, f_autocmd_get},
{"balloon_gettext", 0, 0, 0, NULL,
ret_string,
#ifdef FEAT_BEVAL
f_balloon_gettext
#else
NULL
#endif
},
{"balloon_show", 1, 1, FEARG_1, arg1_string_or_list_any,
ret_void,
#ifdef FEAT_BEVAL
f_balloon_show
#else
NULL
#endif
},
{"balloon_split", 1, 1, FEARG_1, arg1_string,
ret_list_string,
#if defined(FEAT_BEVAL_TERM)
f_balloon_split
#else
NULL
#endif
},
{"blob2list", 1, 1, FEARG_1, arg1_blob,
ret_list_number, f_blob2list},
{"browse", 4, 4, 0, arg4_browse,
ret_string, f_browse},
{"browsedir", 2, 2, 0, arg2_string,
ret_string, f_browsedir},
{"bufadd", 1, 1, FEARG_1, arg1_string,
ret_number, f_bufadd},
{"bufexists", 1, 1, FEARG_1, arg1_buffer,
ret_number_bool, f_bufexists},
{"buffer_exists", 1, 1, FEARG_1, arg1_buffer, // obsolete
ret_number_bool, f_bufexists},
{"buffer_name", 0, 1, FEARG_1, arg1_buffer, // obsolete
ret_string, f_bufname},
{"buffer_number", 0, 1, FEARG_1, arg1_buffer, // obsolete
ret_number, f_bufnr},
{"buflisted", 1, 1, FEARG_1, arg1_buffer,
ret_number_bool, f_buflisted},
{"bufload", 1, 1, FEARG_1, arg1_buffer,
ret_void, f_bufload},
{"bufloaded", 1, 1, FEARG_1, arg1_buffer,
ret_number_bool, f_bufloaded},
{"bufname", 0, 1, FEARG_1, arg1_buffer,
ret_string, f_bufname},
{"bufnr", 0, 2, FEARG_1, arg2_buffer_bool,
ret_number, f_bufnr},
{"bufwinid", 1, 1, FEARG_1, arg1_buffer,
ret_number, f_bufwinid},
{"bufwinnr", 1, 1, FEARG_1, arg1_buffer,
ret_number, f_bufwinnr},
{"byte2line", 1, 1, FEARG_1, arg1_number,
ret_number, f_byte2line},
{"byteidx", 2, 3, FEARG_1, arg3_string_number_bool,
ret_number, f_byteidx},
{"byteidxcomp", 2, 3, FEARG_1, arg3_string_number_bool,
ret_number, f_byteidxcomp},
{"call", 2, 3, FEARG_1, arg3_any_list_dict,
ret_any, f_call},
{"ceil", 1, 1, FEARG_1, arg1_float_or_nr,
ret_float, f_ceil},
{"ch_canread", 1, 1, FEARG_1, arg1_chan_or_job,
ret_number_bool, JOB_FUNC(f_ch_canread)},
{"ch_close", 1, 1, FEARG_1, arg1_chan_or_job,
ret_void, JOB_FUNC(f_ch_close)},
{"ch_close_in", 1, 1, FEARG_1, arg1_chan_or_job,
ret_void, JOB_FUNC(f_ch_close_in)},
{"ch_evalexpr", 2, 3, FEARG_1, arg23_chanexpr,
ret_any, JOB_FUNC(f_ch_evalexpr)},
{"ch_evalraw", 2, 3, FEARG_1, arg23_chanraw,
ret_any, JOB_FUNC(f_ch_evalraw)},
{"ch_getbufnr", 2, 2, FEARG_1, arg2_chan_or_job_string,
ret_number, JOB_FUNC(f_ch_getbufnr)},
{"ch_getjob", 1, 1, FEARG_1, arg1_chan_or_job,
ret_job, JOB_FUNC(f_ch_getjob)},
{"ch_info", 1, 1, FEARG_1, arg1_chan_or_job,
ret_dict_any, JOB_FUNC(f_ch_info)},
{"ch_log", 1, 2, FEARG_1, arg2_string_chan_or_job,
ret_void, f_ch_log},
{"ch_logfile", 1, 2, FEARG_1, arg2_string,
ret_void, f_ch_logfile},
{"ch_open", 1, 2, FEARG_1, arg2_string_dict,
ret_channel, JOB_FUNC(f_ch_open)},
{"ch_read", 1, 2, FEARG_1, arg2_chan_or_job_dict,
ret_string, JOB_FUNC(f_ch_read)},
{"ch_readblob", 1, 2, FEARG_1, arg2_chan_or_job_dict,
ret_blob, JOB_FUNC(f_ch_readblob)},
{"ch_readraw", 1, 2, FEARG_1, arg2_chan_or_job_dict,
ret_string, JOB_FUNC(f_ch_readraw)},
{"ch_sendexpr", 2, 3, FEARG_1, arg23_chanexpr,
ret_any, JOB_FUNC(f_ch_sendexpr)},
{"ch_sendraw", 2, 3, FEARG_1, arg23_chanraw,
ret_void, JOB_FUNC(f_ch_sendraw)},
{"ch_setoptions", 2, 2, FEARG_1, arg2_chan_or_job_dict,
ret_void, JOB_FUNC(f_ch_setoptions)},
{"ch_status", 1, 2, FEARG_1, arg2_chan_or_job_dict,
ret_string, JOB_FUNC(f_ch_status)},
{"changenr", 0, 0, 0, NULL,
ret_number, f_changenr},
{"char2nr", 1, 2, FEARG_1, arg2_string_bool,
ret_number, f_char2nr},
{"charclass", 1, 1, FEARG_1, arg1_string,
ret_number, f_charclass},
{"charcol", 1, 2, FEARG_1, arg2_string_or_list_number,
ret_number, f_charcol},
{"charidx", 2, 4, FEARG_1, arg4_string_number_bool_bool,
ret_number, f_charidx},
{"chdir", 1, 1, FEARG_1, arg1_string,
ret_string, f_chdir},
{"cindent", 1, 1, FEARG_1, arg1_lnum,
ret_number, f_cindent},
{"clearmatches", 0, 1, FEARG_1, arg1_number,
ret_void, f_clearmatches},
{"col", 1, 2, FEARG_1, arg2_string_or_list_number,
ret_number, f_col},
{"complete", 2, 2, FEARG_2, arg2_number_list,
ret_void, f_complete},
{"complete_add", 1, 1, FEARG_1, arg1_dict_or_string,
ret_number, f_complete_add},
{"complete_check", 0, 0, 0, NULL,
ret_number_bool, f_complete_check},
{"complete_info", 0, 1, FEARG_1, arg1_list_string,
ret_dict_any, f_complete_info},
{"confirm", 1, 4, FEARG_1, arg4_string_string_number_string,
ret_number, f_confirm},
{"copy", 1, 1, FEARG_1, NULL,
ret_copy, f_copy},
{"cos", 1, 1, FEARG_1, arg1_float_or_nr,
ret_float, f_cos},
{"cosh", 1, 1, FEARG_1, arg1_float_or_nr,
ret_float, f_cosh},
{"count", 2, 4, FEARG_1, arg24_count,
ret_number, f_count},
{"cscope_connection",0,3, 0, arg3_number_string_string,
ret_number, f_cscope_connection},
{"cursor", 1, 3, FEARG_1, arg13_cursor,
ret_number, f_cursor},
{"debugbreak", 1, 1, FEARG_1, arg1_number,
ret_number,
#ifdef MSWIN
f_debugbreak
#else
NULL
#endif
},
{"deepcopy", 1, 2, FEARG_1, arg12_deepcopy,
ret_copy, f_deepcopy},
{"delete", 1, 2, FEARG_1, arg2_string,
ret_number_bool, f_delete},
{"deletebufline", 2, 3, FEARG_1, arg3_buffer_lnum_lnum,
ret_number_bool, f_deletebufline},
{"did_filetype", 0, 0, 0, NULL,
ret_number_bool, f_did_filetype},
{"diff", 2, 3, FEARG_1, arg3_diff,
ret_any, f_diff},
{"diff_filler", 1, 1, FEARG_1, arg1_lnum,
ret_number, f_diff_filler},
{"diff_hlID", 2, 2, FEARG_1, arg2_lnum_number,
ret_number, f_diff_hlID},
{"digraph_get", 1, 1, FEARG_1, arg1_string,
ret_string, f_digraph_get},
{"digraph_getlist",0, 1, FEARG_1, arg1_bool,
ret_list_string_items, f_digraph_getlist},
{"digraph_set", 2, 2, FEARG_1, arg2_string,
ret_bool, f_digraph_set},
{"digraph_setlist",1, 1, FEARG_1, arg1_list_string,
ret_bool, f_digraph_setlist},
{"echoraw", 1, 1, FEARG_1, arg1_string,
ret_void, f_echoraw},
{"empty", 1, 1, FEARG_1, NULL,
ret_number_bool, f_empty},
{"environ", 0, 0, 0, NULL,
ret_dict_string, f_environ},
{"err_teapot", 0, 1, 0, NULL,
ret_number_bool, f_err_teapot},
{"escape", 2, 2, FEARG_1, arg2_string,
ret_string, f_escape},
{"eval", 1, 1, FEARG_1, arg1_string,
ret_any, f_eval},
{"eventhandler", 0, 0, 0, NULL,
ret_number_bool, f_eventhandler},
{"executable", 1, 1, FEARG_1, arg1_string,
ret_number, f_executable},
{"execute", 1, 2, FEARG_1, arg12_execute,
ret_string, f_execute},
{"exepath", 1, 1, FEARG_1, arg1_string,
ret_string, f_exepath},
{"exists", 1, 1, FEARG_1, arg1_string,
ret_number_bool, f_exists},
{"exists_compiled", 1, 1, FEARG_1, arg1_string,
ret_number_bool, f_exists_compiled},
{"exp", 1, 1, FEARG_1, arg1_float_or_nr,
ret_float, f_exp},
{"expand", 1, 3, FEARG_1, arg3_string_bool_bool,
ret_any, f_expand},
{"expandcmd", 1, 2, FEARG_1, arg2_string_dict,
ret_string, f_expandcmd},
{"extend", 2, 3, FEARG_1, arg23_extend,
ret_extend, f_extend},
{"extendnew", 2, 3, FEARG_1, arg23_extendnew,
ret_first_cont, f_extendnew},
{"feedkeys", 1, 2, FEARG_1, arg2_string,
ret_void, f_feedkeys},
{"file_readable", 1, 1, FEARG_1, arg1_string, // obsolete
ret_number_bool, f_filereadable},
{"filereadable", 1, 1, FEARG_1, arg1_string,
ret_number_bool, f_filereadable},
{"filewritable", 1, 1, FEARG_1, arg1_string,
ret_number, f_filewritable},
{"filter", 2, 2, FEARG_1, arg2_filter,
ret_first_arg, f_filter},
{"finddir", 1, 3, FEARG_1, arg3_string_string_number,
ret_finddir, f_finddir},
{"findfile", 1, 3, FEARG_1, arg3_string_string_number,
ret_any, f_findfile},
{"flatten", 1, 2, FEARG_1, arg2_list_any_number,
ret_list_any, f_flatten},
{"flattennew", 1, 2, FEARG_1, arg2_list_any_number,
ret_list_any, f_flattennew},
{"float2nr", 1, 1, FEARG_1, arg1_float_or_nr,
ret_number, f_float2nr},
{"floor", 1, 1, FEARG_1, arg1_float_or_nr,
ret_float, f_floor},
{"fmod", 2, 2, FEARG_1, arg2_float_or_nr,
ret_float, f_fmod},
{"fnameescape", 1, 1, FEARG_1, arg1_string,
ret_string, f_fnameescape},
{"fnamemodify", 2, 2, FEARG_1, arg2_string,
ret_string, f_fnamemodify},
{"foldclosed", 1, 1, FEARG_1, arg1_lnum,
ret_number, f_foldclosed},
{"foldclosedend", 1, 1, FEARG_1, arg1_lnum,
ret_number, f_foldclosedend},
{"foldlevel", 1, 1, FEARG_1, arg1_lnum,
ret_number, f_foldlevel},
{"foldtext", 0, 0, 0, NULL,
ret_string, f_foldtext},
{"foldtextresult", 1, 1, FEARG_1, arg1_lnum,
ret_string, f_foldtextresult},
{"foreach", 2, 2, FEARG_1, arg2_foreach,
ret_first_arg, f_foreach},
{"foreground", 0, 0, 0, NULL,
ret_void, f_foreground},
{"fullcommand", 1, 2, FEARG_1, arg2_string_bool,
ret_string, f_fullcommand},
{"funcref", 1, 3, FEARG_1, arg3_any_list_dict,
ret_func_unknown, f_funcref},
{"function", 1, 3, FEARG_1, arg3_any_list_dict,
ret_func_unknown, f_function},
{"garbagecollect", 0, 1, 0, arg1_bool,
ret_void, f_garbagecollect},
{"get", 2, 3, FEARG_1, arg23_get,
ret_any, f_get},
{"getbufinfo", 0, 1, FEARG_1, arg1_buffer_or_dict_any,
ret_list_dict_any, f_getbufinfo},
{"getbufline", 2, 3, FEARG_1, arg3_buffer_lnum_lnum,
ret_list_string, f_getbufline},
{"getbufoneline", 2, 2, FEARG_1, arg2_buffer_lnum,
ret_string, f_getbufoneline},
{"getbufvar", 2, 3, FEARG_1, arg3_buffer_string_any,
ret_any, f_getbufvar},
{"getcellwidths", 0, 0, 0, NULL,
ret_list_any, f_getcellwidths},
{"getchangelist", 0, 1, FEARG_1, arg1_buffer,
ret_list_any, f_getchangelist},
{"getchar", 0, 1, 0, arg1_bool,
ret_any, f_getchar},
{"getcharmod", 0, 0, 0, NULL,
ret_number, f_getcharmod},
{"getcharpos", 1, 1, FEARG_1, arg1_string,
ret_list_number, f_getcharpos},
{"getcharsearch", 0, 0, 0, NULL,
ret_dict_any, f_getcharsearch},
{"getcharstr", 0, 1, 0, arg1_bool,
ret_string, f_getcharstr},
{"getcmdcompltype", 0, 0, 0, NULL,
ret_string, f_getcmdcompltype},
{"getcmdline", 0, 0, 0, NULL,
ret_string, f_getcmdline},
{"getcmdpos", 0, 0, 0, NULL,
ret_number, f_getcmdpos},
{"getcmdscreenpos", 0, 0, 0, NULL,
ret_number, f_getcmdscreenpos},
{"getcmdtype", 0, 0, 0, NULL,
ret_string, f_getcmdtype},
{"getcmdwintype", 0, 0, 0, NULL,
ret_string, f_getcmdwintype},
{"getcompletion", 2, 3, FEARG_1, arg3_string_string_bool,
ret_list_string, f_getcompletion},
{"getcurpos", 0, 1, FEARG_1, arg1_number,
ret_list_number, f_getcurpos},
{"getcursorcharpos", 0, 1, FEARG_1, arg1_number,
ret_list_number, f_getcursorcharpos},
{"getcwd", 0, 2, FEARG_1, arg2_number,
ret_string, f_getcwd},
{"getenv", 1, 1, FEARG_1, arg1_string,
ret_any, f_getenv},
{"getfontname", 0, 1, 0, arg1_string,
ret_string, f_getfontname},
{"getfperm", 1, 1, FEARG_1, arg1_string,
ret_string, f_getfperm},
{"getfsize", 1, 1, FEARG_1, arg1_string,
ret_number, f_getfsize},
{"getftime", 1, 1, FEARG_1, arg1_string,
ret_number, f_getftime},
{"getftype", 1, 1, FEARG_1, arg1_string,
ret_string, f_getftype},
{"getimstatus", 0, 0, 0, NULL,
ret_number_bool, f_getimstatus},
{"getjumplist", 0, 2, FEARG_1, arg2_number,
ret_list_any, f_getjumplist},
{"getline", 1, 2, FEARG_1, arg2_lnum,
ret_getline, f_getline},
{"getloclist", 1, 2, 0, arg2_number_dict_any,
ret_list_or_dict_1, f_getloclist},
{"getmarklist", 0, 1, FEARG_1, arg1_buffer,
ret_list_dict_any, f_getmarklist},
{"getmatches", 0, 1, 0, arg1_number,
ret_list_dict_any, f_getmatches},
{"getmousepos", 0, 0, 0, NULL,
ret_dict_number, f_getmousepos},
{"getmouseshape", 0, 0, 0, NULL,
ret_string, f_getmouseshape},
{"getpid", 0, 0, 0, NULL,
ret_number, f_getpid},
{"getpos", 1, 1, FEARG_1, arg1_string,
ret_list_number, f_getpos},
{"getqflist", 0, 1, 0, arg1_dict_any,
ret_list_or_dict_0, f_getqflist},
{"getreg", 0, 3, FEARG_1, arg3_string_bool_bool,
ret_getreg, f_getreg},
{"getreginfo", 0, 1, FEARG_1, arg1_string,
ret_dict_any, f_getreginfo},
{"getregion", 2, 3, FEARG_1, arg3_list_list_dict,
ret_list_string, f_getregion},
{"getregtype", 0, 1, FEARG_1, arg1_string,
ret_string, f_getregtype},
{"getscriptinfo", 0, 1, 0, arg1_dict_any,
ret_list_dict_any, f_getscriptinfo},
{"gettabinfo", 0, 1, FEARG_1, arg1_number,
ret_list_dict_any, f_gettabinfo},
{"gettabvar", 2, 3, FEARG_1, arg3_number_string_any,
ret_any, f_gettabvar},
{"gettabwinvar", 3, 4, FEARG_1, arg4_number_number_string_any,
ret_any, f_gettabwinvar},
{"gettagstack", 0, 1, FEARG_1, arg1_number,
ret_dict_any, f_gettagstack},
{"gettext", 1, 1, FEARG_1, arg1_string,
ret_string, f_gettext},
{"getwininfo", 0, 1, FEARG_1, arg1_number,
ret_list_dict_any, f_getwininfo},
{"getwinpos", 0, 1, FEARG_1, arg1_number,
ret_list_number, f_getwinpos},
{"getwinposx", 0, 0, 0, NULL,
ret_number, f_getwinposx},
{"getwinposy", 0, 0, 0, NULL,
ret_number, f_getwinposy},
{"getwinvar", 2, 3, FEARG_1, arg3_number_string_any,
ret_any, f_getwinvar},
{"glob", 1, 4, FEARG_1, arg14_glob,
ret_any, f_glob},
{"glob2regpat", 1, 1, FEARG_1, arg1_string,
ret_string, f_glob2regpat},
{"globpath", 2, 5, FEARG_2, arg25_globpath,
ret_any, f_globpath},
{"has", 1, 2, 0, arg2_string_bool,
ret_number_bool, f_has},
{"has_key", 2, 2, FEARG_1, arg2_dict_any_string_or_nr,
ret_number_bool, f_has_key},
{"haslocaldir", 0, 2, FEARG_1, arg2_number,
ret_number, f_haslocaldir},
{"hasmapto", 1, 3, FEARG_1, arg3_string_string_bool,
ret_number_bool, f_hasmapto},
{"highlightID", 1, 1, FEARG_1, arg1_string, // obsolete
ret_number, f_hlID},
{"highlight_exists",1, 1, FEARG_1, arg1_string, // obsolete
ret_number_bool, f_hlexists},
{"histadd", 2, 2, FEARG_2, arg2_string,
ret_number_bool, f_histadd},
{"histdel", 1, 2, FEARG_1, arg2_string_string_or_number,
ret_number_bool, f_histdel},
{"histget", 1, 2, FEARG_1, arg2_string_number,
ret_string, f_histget},
{"histnr", 1, 1, FEARG_1, arg1_string,
ret_number, f_histnr},
{"hlID", 1, 1, FEARG_1, arg1_string,
ret_number, f_hlID},
{"hlexists", 1, 1, FEARG_1, arg1_string,
ret_number_bool, f_hlexists},
{"hlget", 0, 2, FEARG_1, arg2_string_bool,
ret_list_dict_any, f_hlget},
{"hlset", 1, 1, FEARG_1, arg1_list_any,
ret_number_bool, f_hlset},
{"hostname", 0, 0, 0, NULL,
ret_string, f_hostname},
{"iconv", 3, 3, FEARG_1, arg3_string,
ret_string, f_iconv},
{"indent", 1, 1, FEARG_1, arg1_lnum,
ret_number, f_indent},
{"index", 2, 4, FEARG_1, arg24_index,
ret_number, f_index},
{"indexof", 2, 3, FEARG_1, arg23_index,
ret_number, f_indexof},
{"input", 1, 3, FEARG_1, arg3_string,
ret_string, f_input},
{"inputdialog", 1, 3, FEARG_1, arg3_string,
ret_string, f_inputdialog},
{"inputlist", 1, 1, FEARG_1, arg1_list_string,
ret_number, f_inputlist},
{"inputrestore", 0, 0, 0, NULL,
ret_number_bool, f_inputrestore},
{"inputsave", 0, 0, 0, NULL,
ret_number_bool, f_inputsave},
{"inputsecret", 1, 2, FEARG_1, arg2_string,
ret_string, f_inputsecret},
{"insert", 2, 3, FEARG_1, arg23_insert,
ret_first_arg, f_insert},
{"instanceof", 2, VARGS, FEARG_1|FE_X, arg2_instanceof,
ret_bool, f_instanceof},
{"interrupt", 0, 0, 0, NULL,
ret_void, f_interrupt},
{"invert", 1, 1, FEARG_1, arg1_number,
ret_number, f_invert},
{"isabsolutepath", 1, 1, FEARG_1, arg1_string,
ret_number_bool, f_isabsolutepath},
{"isdirectory", 1, 1, FEARG_1, arg1_string,
ret_number_bool, f_isdirectory},
{"isinf", 1, 1, FEARG_1, arg1_float_or_nr,
ret_number, MATH_FUNC(f_isinf)},
{"islocked", 1, 1, FEARG_1, arg1_string,
ret_number_bool, f_islocked},
{"isnan", 1, 1, FEARG_1, arg1_float_or_nr,
ret_number_bool, MATH_FUNC(f_isnan)},
{"items", 1, 1, FEARG_1, arg1_string_or_list_or_dict,
ret_list_items, f_items},
{"job_getchannel", 1, 1, FEARG_1, arg1_job,
ret_channel, JOB_FUNC(f_job_getchannel)},
{"job_info", 0, 1, FEARG_1, arg1_job,
ret_job_info, JOB_FUNC(f_job_info)},
{"job_setoptions", 2, 2, FEARG_1, arg2_job_dict,
ret_void, JOB_FUNC(f_job_setoptions)},
{"job_start", 1, 2, FEARG_1, arg2_string_or_list_dict,
ret_job, JOB_FUNC(f_job_start)},
{"job_status", 1, 1, FEARG_1, arg1_job,
ret_string, JOB_FUNC(f_job_status)},
{"job_stop", 1, 2, FEARG_1, arg2_job_string_or_number,
ret_number_bool, JOB_FUNC(f_job_stop)},
{"join", 1, 2, FEARG_1, arg2_list_any_string,
ret_string, f_join},
{"js_decode", 1, 1, FEARG_1, arg1_string,
ret_any, f_js_decode},
{"js_encode", 1, 1, FEARG_1, NULL,
ret_string, f_js_encode},
{"json_decode", 1, 1, FEARG_1, arg1_string,
ret_any, f_json_decode},
{"json_encode", 1, 1, FEARG_1, NULL,
ret_string, f_json_encode},
{"keys", 1, 1, FEARG_1, arg1_dict_any,
ret_list_string, f_keys},
{"keytrans", 1, 1, FEARG_1, arg1_string,
ret_string, f_keytrans},
{"last_buffer_nr", 0, 0, 0, NULL, // obsolete
ret_number, f_last_buffer_nr},
{"len", 1, 1, FEARG_1, arg1_len,
ret_number, f_len},
{"libcall", 3, 3, FEARG_3, arg3_libcall,
ret_string, f_libcall},
{"libcallnr", 3, 3, FEARG_3, arg3_libcall,
ret_number, f_libcallnr},
{"line", 1, 2, FEARG_1, arg2_string_number,
ret_number, f_line},
{"line2byte", 1, 1, FEARG_1, arg1_lnum,
ret_number, f_line2byte},
{"lispindent", 1, 1, FEARG_1, arg1_lnum,
ret_number, f_lispindent},
{"list2blob", 1, 1, FEARG_1, arg1_list_number,
ret_blob, f_list2blob},
{"list2str", 1, 2, FEARG_1, arg2_list_number_bool,
ret_string, f_list2str},
{"listener_add", 1, 2, FEARG_2, arg2_any_buffer,
ret_number, f_listener_add},
{"listener_flush", 0, 1, FEARG_1, arg1_buffer,
ret_void, f_listener_flush},
{"listener_remove", 1, 1, FEARG_1, arg1_number,
ret_number_bool, f_listener_remove},
{"localtime", 0, 0, 0, NULL,
ret_number, f_localtime},
{"log", 1, 1, FEARG_1, arg1_float_or_nr,
ret_float, f_log},
{"log10", 1, 1, FEARG_1, arg1_float_or_nr,
ret_float, f_log10},
{"luaeval", 1, 2, FEARG_1, arg2_string_any,
ret_any,
#ifdef FEAT_LUA
f_luaeval
#else
NULL
#endif
},
{"map", 2, 2, FEARG_1, arg2_map,
ret_first_cont, f_map},
{"maparg", 1, 4, FEARG_1, arg14_maparg,
ret_maparg, f_maparg},
{"mapcheck", 1, 3, FEARG_1, arg3_string_string_bool,
ret_string, f_mapcheck},
{"maplist", 0, 1, 0, arg1_bool,
ret_list_dict_any, f_maplist},
{"mapnew", 2, 2, FEARG_1, arg2_mapnew,
ret_first_cont, f_mapnew},
{"mapset", 1, 3, FEARG_1, arg3_string_or_dict_bool_dict,
ret_void, f_mapset},
{"match", 2, 4, FEARG_1, arg24_match_func,
ret_any, f_match},
{"matchadd", 2, 5, FEARG_1, arg25_matchadd,
ret_number, f_matchadd},
{"matchaddpos", 2, 5, FEARG_1, arg25_matchaddpos,
ret_number, f_matchaddpos},
{"matcharg", 1, 1, FEARG_1, arg1_number,
ret_list_string, f_matcharg},
{"matchbufline", 4, 5, FEARG_1, arg45_matchbufline,
ret_list_any, f_matchbufline},
{"matchdelete", 1, 2, FEARG_1, arg2_number,
ret_number_bool, f_matchdelete},
{"matchend", 2, 4, FEARG_1, arg24_match_func,
ret_number, f_matchend},
{"matchfuzzy", 2, 3, FEARG_1, arg3_list_string_dict,
ret_list_any, f_matchfuzzy},
{"matchfuzzypos", 2, 3, FEARG_1, arg3_list_string_dict,
ret_list_any, f_matchfuzzypos},
{"matchlist", 2, 4, FEARG_1, arg24_match_func,
ret_list_string, f_matchlist},
{"matchstr", 2, 4, FEARG_1, arg24_match_func,
ret_string, f_matchstr},
{"matchstrlist", 2, 3, FEARG_1, arg23_matchstrlist,
ret_list_any, f_matchstrlist},
{"matchstrpos", 2, 4, FEARG_1, arg24_match_func,
ret_list_any, f_matchstrpos},
{"max", 1, 1, FEARG_1, arg1_list_or_dict,
ret_number, f_max},
{"menu_info", 1, 2, FEARG_1, arg2_string,
ret_dict_any,
#ifdef FEAT_MENU
f_menu_info
#else
NULL
#endif
},
{"min", 1, 1, FEARG_1, arg1_list_or_dict,
ret_number, f_min},
{"mkdir", 1, 3, FEARG_1, arg3_string_string_number,
ret_number_bool, f_mkdir},
{"mode", 0, 1, FEARG_1, arg1_bool,
ret_string, f_mode},
{"mzeval", 1, 1, FEARG_1, arg1_string,
ret_any,
#ifdef FEAT_MZSCHEME
f_mzeval
#else
NULL
#endif
},
{"nextnonblank", 1, 1, FEARG_1, arg1_lnum,
ret_number, f_nextnonblank},
{"nr2char", 1, 2, FEARG_1, arg2_number_bool,
ret_string, f_nr2char},
{"or", 2, 2, FEARG_1, arg2_number,
ret_number, f_or},
{"pathshorten", 1, 2, FEARG_1, arg2_string_number,
ret_string, f_pathshorten},
{"perleval", 1, 1, FEARG_1, arg1_string,
ret_any,
#ifdef FEAT_PERL
f_perleval
#else
NULL
#endif
},
{"popup_atcursor", 2, 2, FEARG_1, arg2_str_or_nr_or_list_dict,
ret_number, PROP_FUNC(f_popup_atcursor)},
{"popup_beval", 2, 2, FEARG_1, arg2_str_or_nr_or_list_dict,
ret_number, PROP_FUNC(f_popup_beval)},
{"popup_clear", 0, 1, 0, arg1_bool,
ret_void, PROP_FUNC(f_popup_clear)},
{"popup_close", 1, 2, FEARG_1, arg2_number_any,
ret_void, PROP_FUNC(f_popup_close)},
{"popup_create", 2, 2, FEARG_1, arg2_str_or_nr_or_list_dict,
ret_number, PROP_FUNC(f_popup_create)},
{"popup_dialog", 2, 2, FEARG_1, arg2_str_or_nr_or_list_dict,
ret_number, PROP_FUNC(f_popup_dialog)},
{"popup_filter_menu", 2, 2, 0, arg2_number_string,
ret_bool, PROP_FUNC(f_popup_filter_menu)},
{"popup_filter_yesno", 2, 2, 0, arg2_number_string,
ret_bool, PROP_FUNC(f_popup_filter_yesno)},
{"popup_findecho", 0, 0, 0, NULL,
ret_number, PROP_FUNC(f_popup_findecho)},
{"popup_findinfo", 0, 0, 0, NULL,
ret_number, PROP_FUNC(f_popup_findinfo)},
{"popup_findpreview", 0, 0, 0, NULL,
ret_number, PROP_FUNC(f_popup_findpreview)},
{"popup_getoptions", 1, 1, FEARG_1, arg1_number,
ret_dict_any, PROP_FUNC(f_popup_getoptions)},
{"popup_getpos", 1, 1, FEARG_1, arg1_number,
ret_dict_any, PROP_FUNC(f_popup_getpos)},
{"popup_hide", 1, 1, FEARG_1, arg1_number,
ret_void, PROP_FUNC(f_popup_hide)},
{"popup_list", 0, 0, 0, NULL,
ret_list_number, PROP_FUNC(f_popup_list)},
{"popup_locate", 2, 2, 0, arg2_number,
ret_number, PROP_FUNC(f_popup_locate)},
{"popup_menu", 2, 2, FEARG_1, arg2_str_or_nr_or_list_dict,
ret_number, PROP_FUNC(f_popup_menu)},
{"popup_move", 2, 2, FEARG_1, arg2_number_dict_any,
ret_void, PROP_FUNC(f_popup_move)},
{"popup_notification", 2, 2, FEARG_1, arg2_str_or_nr_or_list_dict,
ret_number, PROP_FUNC(f_popup_notification)},
{"popup_setoptions", 2, 2, FEARG_1, arg2_number_dict_any,
ret_void, PROP_FUNC(f_popup_setoptions)},
{"popup_settext", 2, 2, FEARG_1, arg2_number_string_or_list,
ret_void, PROP_FUNC(f_popup_settext)},
{"popup_show", 1, 1, FEARG_1, arg1_number,
ret_void, PROP_FUNC(f_popup_show)},
{"pow", 2, 2, FEARG_1, arg2_float_or_nr,
ret_float, f_pow},
{"prevnonblank", 1, 1, FEARG_1, arg1_lnum,
ret_number, f_prevnonblank},
{"printf", 1, 19, FEARG_2, arg119_printf,
ret_string, f_printf},
{"prompt_getprompt", 1, 1, FEARG_1, arg1_buffer,
ret_string, JOB_FUNC(f_prompt_getprompt)},
{"prompt_setcallback", 2, 2, FEARG_1, arg2_buffer_any,
ret_void, JOB_FUNC(f_prompt_setcallback)},
{"prompt_setinterrupt", 2, 2, FEARG_1, arg2_buffer_any,
ret_void, JOB_FUNC(f_prompt_setinterrupt)},
{"prompt_setprompt", 2, 2, FEARG_1, arg2_buffer_string,
ret_void, JOB_FUNC(f_prompt_setprompt)},
{"prop_add", 3, 3, FEARG_1, arg3_number_number_dict,
ret_number, PROP_FUNC(f_prop_add)},
{"prop_add_list", 2, 2, FEARG_1, arg2_dict_any_list_any,
ret_void, PROP_FUNC(f_prop_add_list)},
{"prop_clear", 1, 3, FEARG_1, arg3_number_number_dict,
ret_void, PROP_FUNC(f_prop_clear)},
{"prop_find", 1, 2, FEARG_1, arg2_dict_string,
ret_dict_any, PROP_FUNC(f_prop_find)},
{"prop_list", 1, 2, FEARG_1, arg2_number_dict_any,
ret_list_dict_any, PROP_FUNC(f_prop_list)},
{"prop_remove", 1, 3, FEARG_1, arg3_dict_number_number,
ret_number, PROP_FUNC(f_prop_remove)},
{"prop_type_add", 2, 2, FEARG_1, arg2_string_dict,
ret_void, PROP_FUNC(f_prop_type_add)},
{"prop_type_change", 2, 2, FEARG_1, arg2_string_dict,
ret_void, PROP_FUNC(f_prop_type_change)},
{"prop_type_delete", 1, 2, FEARG_1, arg2_string_dict,
ret_void, PROP_FUNC(f_prop_type_delete)},
{"prop_type_get", 1, 2, FEARG_1, arg2_string_dict,
ret_dict_any, PROP_FUNC(f_prop_type_get)},
{"prop_type_list", 0, 1, FEARG_1, arg1_dict_any,
ret_list_string, PROP_FUNC(f_prop_type_list)},
{"pum_getpos", 0, 0, 0, NULL,
ret_dict_number, f_pum_getpos},
{"pumvisible", 0, 0, 0, NULL,
ret_number_bool, f_pumvisible},
{"py3eval", 1, 1, FEARG_1, arg1_string,
ret_any,
#ifdef FEAT_PYTHON3
f_py3eval
#else
NULL
#endif
},
{"pyeval", 1, 1, FEARG_1, arg1_string,
ret_any,
#ifdef FEAT_PYTHON
f_pyeval
#else
NULL
#endif
},
{"pyxeval", 1, 1, FEARG_1, arg1_string,
ret_any,
#if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3)
f_pyxeval
#else
NULL
#endif
},
{"rand", 0, 1, FEARG_1, arg1_list_number,
ret_number, f_rand},
{"range", 1, 3, FEARG_1, arg3_number,
ret_list_number, f_range},
{"readblob", 1, 3, FEARG_1, arg3_string_number_number,
ret_blob, f_readblob},
{"readdir", 1, 3, FEARG_1, arg3_string_any_dict,
ret_list_string, f_readdir},
{"readdirex", 1, 3, FEARG_1, arg3_string_any_dict,
ret_list_dict_any, f_readdirex},
{"readfile", 1, 3, FEARG_1, arg3_string_string_number,
ret_list_string, f_readfile},
{"reduce", 2, 3, FEARG_1, arg23_reduce,
ret_any, f_reduce},
{"reg_executing", 0, 0, 0, NULL,
ret_string, f_reg_executing},
{"reg_recording", 0, 0, 0, NULL,
ret_string, f_reg_recording},
{"reltime", 0, 2, FEARG_1, arg2_list_number,
ret_list_any, f_reltime},
{"reltimefloat", 1, 1, FEARG_1, arg1_list_number,
ret_float, f_reltimefloat},
{"reltimestr", 1, 1, FEARG_1, arg1_list_number,
ret_string, f_reltimestr},
{"remote_expr", 2, 4, FEARG_1, arg24_remote_expr,
ret_string, f_remote_expr},
{"remote_foreground", 1, 1, FEARG_1, arg1_string,
ret_string, f_remote_foreground},
{"remote_peek", 1, 2, FEARG_1, arg2_string,
ret_number, f_remote_peek},
{"remote_read", 1, 2, FEARG_1, arg2_string_number,
ret_string, f_remote_read},
{"remote_send", 2, 3, FEARG_1, arg3_string,
ret_string, f_remote_send},
{"remote_startserver", 1, 1, FEARG_1, arg1_string,
ret_void, f_remote_startserver},
{"remove", 2, 3, FEARG_1, arg23_remove,
ret_remove, f_remove},
{"rename", 2, 2, FEARG_1, arg2_string,
ret_number_bool, f_rename},
{"repeat", 2, 2, FEARG_1, arg2_repeat,
ret_repeat, f_repeat},
{"resolve", 1, 1, FEARG_1, arg1_string,
ret_string, f_resolve},
{"reverse", 1, 1, FEARG_1, arg1_string_or_list_or_blob_mod,
ret_first_arg, f_reverse},
{"round", 1, 1, FEARG_1, arg1_float_or_nr,
ret_float, f_round},
{"rubyeval", 1, 1, FEARG_1, arg1_string,
ret_any,
#ifdef FEAT_RUBY
f_rubyeval
#else
NULL
#endif
},
{"screenattr", 2, 2, FEARG_1, arg2_number,
ret_number, f_screenattr},
{"screenchar", 2, 2, FEARG_1, arg2_number,
ret_number, f_screenchar},
{"screenchars", 2, 2, FEARG_1, arg2_number,
ret_list_number, f_screenchars},
{"screencol", 0, 0, 0, NULL,
ret_number, f_screencol},
{"screenpos", 3, 3, FEARG_1, arg3_number,
ret_dict_number, f_screenpos},
{"screenrow", 0, 0, 0, NULL,
ret_number, f_screenrow},
{"screenstring", 2, 2, FEARG_1, arg2_number,
ret_string, f_screenstring},
{"search", 1, 5, FEARG_1, arg15_search,
ret_number, f_search},
{"searchcount", 0, 1, FEARG_1, arg1_dict_any,
ret_dict_any, f_searchcount},
{"searchdecl", 1, 3, FEARG_1, arg3_string_bool_bool,
ret_number_bool, f_searchdecl},
{"searchpair", 3, 7, 0, arg37_searchpair,
ret_number, f_searchpair},
{"searchpairpos", 3, 7, 0, arg37_searchpair,
ret_list_number, f_searchpairpos},
{"searchpos", 1, 5, FEARG_1, arg15_search,
ret_list_number, f_searchpos},
{"server2client", 2, 2, FEARG_1, arg2_string,
ret_number_bool, f_server2client},
{"serverlist", 0, 0, 0, NULL,
ret_string, f_serverlist},
{"setbufline", 3, 3, FEARG_3, arg3_setbufline,
ret_number_bool, f_setbufline},
{"setbufvar", 3, 3, FEARG_3, arg3_buffer_string_any,
ret_void, f_setbufvar},
{"setcellwidths", 1, 1, FEARG_1, arg1_list_any,
ret_void, f_setcellwidths},
{"setcharpos", 2, 2, FEARG_2, arg2_string_list_number,
ret_number_bool, f_setcharpos},
{"setcharsearch", 1, 1, FEARG_1, arg1_dict_any,
ret_void, f_setcharsearch},
{"setcmdline", 1, 2, FEARG_1, arg2_string_number,
ret_number_bool, f_setcmdline},
{"setcmdpos", 1, 1, FEARG_1, arg1_number,
ret_number_bool, f_setcmdpos},
{"setcursorcharpos", 1, 3, FEARG_1, arg13_cursor,
ret_number_bool, f_setcursorcharpos},
{"setenv", 2, 2, FEARG_2, arg2_string_any,
ret_void, f_setenv},
{"setfperm", 2, 2, FEARG_1, arg2_string,
ret_number_bool, f_setfperm},
{"setline", 2, 2, FEARG_2, arg2_setline,
ret_number_bool, f_setline},
{"setloclist", 2, 4, FEARG_2, arg24_setloclist,
ret_number_bool, f_setloclist},
{"setmatches", 1, 2, FEARG_1, arg2_list_any_number,
ret_number_bool, f_setmatches},
{"setpos", 2, 2, FEARG_2, arg2_string_list_number,
ret_number_bool, f_setpos},
{"setqflist", 1, 3, FEARG_1, arg13_setqflist,
ret_number_bool, f_setqflist},
{"setreg", 2, 3, FEARG_2, arg3_string_any_string,
ret_number_bool, f_setreg},
{"settabvar", 3, 3, FEARG_3, arg3_number_string_any,
ret_void, f_settabvar},
{"settabwinvar", 4, 4, FEARG_4, arg4_number_number_string_any,
ret_void, f_settabwinvar},
{"settagstack", 2, 3, FEARG_2, arg23_settagstack,
ret_number_bool, f_settagstack},
{"setwinvar", 3, 3, FEARG_3, arg3_number_string_any,
ret_void, f_setwinvar},
{"sha256", 1, 1, FEARG_1, arg1_string,
ret_string,
#ifdef FEAT_CRYPT
f_sha256
#else
NULL
#endif
},
{"shellescape", 1, 2, FEARG_1, arg2_string_bool,
ret_string, f_shellescape},
{"shiftwidth", 0, 1, FEARG_1, arg1_number,
ret_number, f_shiftwidth},
{"sign_define", 1, 2, FEARG_1, arg2_string_or_list_dict,
ret_any, SIGN_FUNC(f_sign_define)},
{"sign_getdefined", 0, 1, FEARG_1, arg1_string,
ret_list_dict_any, SIGN_FUNC(f_sign_getdefined)},
{"sign_getplaced", 0, 2, FEARG_1, arg02_sign_getplaced,
ret_list_dict_any, SIGN_FUNC(f_sign_getplaced)},
{"sign_jump", 3, 3, FEARG_1, arg3_number_string_buffer,
ret_number, SIGN_FUNC(f_sign_jump)},
{"sign_place", 4, 5, FEARG_1, arg45_sign_place,
ret_number, SIGN_FUNC(f_sign_place)},
{"sign_placelist", 1, 1, FEARG_1, arg1_list_any,
ret_list_number, SIGN_FUNC(f_sign_placelist)},
{"sign_undefine", 0, 1, FEARG_1, arg1_string_or_list_string,
ret_number_bool, SIGN_FUNC(f_sign_undefine)},
{"sign_unplace", 1, 2, FEARG_1, arg2_string_dict,
ret_number_bool, SIGN_FUNC(f_sign_unplace)},
{"sign_unplacelist", 1, 1, FEARG_1, arg1_list_any,
ret_list_number, SIGN_FUNC(f_sign_unplacelist)},
{"simplify", 1, 1, FEARG_1, arg1_string,
ret_string, f_simplify},
{"sin", 1, 1, FEARG_1, arg1_float_or_nr,
ret_float, f_sin},
{"sinh", 1, 1, FEARG_1, arg1_float_or_nr,
ret_float, f_sinh},
{"slice", 2, 3, FEARG_1, arg23_slice,
ret_slice, f_slice},
{"sort", 1, 3, FEARG_1, arg13_sortuniq,
ret_first_arg, f_sort},
{"sound_clear", 0, 0, 0, NULL,
ret_void, SOUND_FUNC(f_sound_clear)},
{"sound_playevent", 1, 2, FEARG_1, arg2_string_any,
ret_number, SOUND_FUNC(f_sound_playevent)},
{"sound_playfile", 1, 2, FEARG_1, arg2_string_any,
ret_number, SOUND_FUNC(f_sound_playfile)},
{"sound_stop", 1, 1, FEARG_1, arg1_number,
ret_void, SOUND_FUNC(f_sound_stop)},
{"soundfold", 1, 1, FEARG_1, arg1_string,
ret_string, f_soundfold},
{"spellbadword", 0, 1, FEARG_1, arg1_string,
ret_list_string, f_spellbadword},
{"spellsuggest", 1, 3, FEARG_1, arg3_string_number_bool,
ret_list_string, f_spellsuggest},
{"split", 1, 3, FEARG_1, arg3_string_string_bool,
ret_list_string, f_split},
{"sqrt", 1, 1, FEARG_1, arg1_float_or_nr,
ret_float, f_sqrt},
{"srand", 0, 1, FEARG_1, arg1_number,
ret_list_number, f_srand},
{"state", 0, 1, FEARG_1, arg1_string,
ret_string, f_state},
{"str2float", 1, 2, FEARG_1, arg2_string_bool,
ret_float, f_str2float},
{"str2list", 1, 2, FEARG_1, arg2_string_bool,
ret_list_number, f_str2list},
{"str2nr", 1, 3, FEARG_1, arg3_string_number_bool,
ret_number, f_str2nr},
{"strcharlen", 1, 1, FEARG_1, arg1_string_or_nr,
ret_number, f_strcharlen},
{"strcharpart", 2, 4, FEARG_1, arg24_strpart,
ret_string, f_strcharpart},
{"strchars", 1, 2, FEARG_1, arg2_string_bool,
ret_number, f_strchars},
{"strdisplaywidth", 1, 2, FEARG_1, arg2_string_number,
ret_number, f_strdisplaywidth},
{"strftime", 1, 2, FEARG_1, arg2_string_number,
ret_string,
#ifdef HAVE_STRFTIME
f_strftime
#else
NULL
#endif
},
{"strgetchar", 2, 2, FEARG_1, arg2_string_number,
ret_number, f_strgetchar},
{"stridx", 2, 3, FEARG_1, arg3_string_string_number,
ret_number, f_stridx},
{"string", 1, 1, FEARG_1|FE_X, NULL,
ret_string, f_string},
{"strlen", 1, 1, FEARG_1, arg1_string_or_nr,
ret_number, f_strlen},
{"strpart", 2, 4, FEARG_1, arg24_strpart,
ret_string, f_strpart},
{"strptime", 2, 2, FEARG_1, arg2_string,
ret_number,
#ifdef HAVE_STRPTIME
f_strptime
#else
NULL
#endif
},
{"strridx", 2, 3, FEARG_1, arg3_string_string_number,
ret_number, f_strridx},
{"strtrans", 1, 1, FEARG_1, arg1_string,
ret_string, f_strtrans},
{"strutf16len", 1, 2, FEARG_1, arg2_string_bool,
ret_number, f_strutf16len},
{"strwidth", 1, 1, FEARG_1, arg1_string,
ret_number, f_strwidth},
{"submatch", 1, 2, FEARG_1, arg2_number_bool,
ret_string, f_submatch},
{"substitute", 4, 4, FEARG_1, arg4_string_string_any_string,
ret_string, f_substitute},
{"swapfilelist", 0, 0, 0, NULL,
ret_list_string, f_swapfilelist},
{"swapinfo", 1, 1, FEARG_1, arg1_string,
ret_dict_any, f_swapinfo},
{"swapname", 1, 1, FEARG_1, arg1_buffer,
ret_string, f_swapname},
{"synID", 3, 3, 0, arg3_lnum_number_bool,
ret_number, f_synID},
{"synIDattr", 2, 3, FEARG_1, arg3_number_string_string,
ret_string, f_synIDattr},
{"synIDtrans", 1, 1, FEARG_1, arg1_number,
ret_number, f_synIDtrans},
{"synconcealed", 2, 2, 0, arg2_lnum_number,
ret_list_any, f_synconcealed},
{"synstack", 2, 2, 0, arg2_lnum_number,
ret_list_number, f_synstack},
{"system", 1, 2, FEARG_1, arg12_system,
ret_string, f_system},
{"systemlist", 1, 2, FEARG_1, arg12_system,
ret_list_string, f_systemlist},
{"tabpagebuflist", 0, 1, FEARG_1, arg1_number,
ret_list_number, f_tabpagebuflist},
{"tabpagenr", 0, 1, 0, arg1_string,
ret_number, f_tabpagenr},
{"tabpagewinnr", 1, 2, FEARG_1, arg2_number_string,
ret_number, f_tabpagewinnr},
{"tagfiles", 0, 0, 0, NULL,
ret_list_string, f_tagfiles},
{"taglist", 1, 2, FEARG_1, arg2_string,
ret_list_dict_any, f_taglist},
{"tan", 1, 1, FEARG_1, arg1_float_or_nr,
ret_float, f_tan},
{"tanh", 1, 1, FEARG_1, arg1_float_or_nr,
ret_float, f_tanh},
{"tempname", 0, 0, 0, NULL,
ret_string, f_tempname},
{"term_dumpdiff", 2, 3, FEARG_1, arg3_string_string_dict,
ret_number, TERM_FUNC(f_term_dumpdiff)},
{"term_dumpload", 1, 2, FEARG_1, arg2_string_dict,
ret_number, TERM_FUNC(f_term_dumpload)},
{"term_dumpwrite", 2, 3, FEARG_2, arg3_buffer_string_dict,
ret_void, TERM_FUNC(f_term_dumpwrite)},
{"term_getaltscreen", 1, 1, FEARG_1, arg1_buffer,
ret_number, TERM_FUNC(f_term_getaltscreen)},
{"term_getansicolors", 1, 1, FEARG_1, arg1_buffer,
ret_list_string,
#if defined(FEAT_TERMINAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
f_term_getansicolors
#else
NULL
#endif
},
{"term_getattr", 2, 2, FEARG_1, arg2_number_string,
ret_number, TERM_FUNC(f_term_getattr)},
{"term_getcursor", 1, 1, FEARG_1, arg1_buffer,
ret_list_any, TERM_FUNC(f_term_getcursor)},
{"term_getjob", 1, 1, FEARG_1, arg1_buffer,
ret_job, TERM_FUNC(f_term_getjob)},
{"term_getline", 2, 2, FEARG_1, arg2_buffer_lnum,
ret_string, TERM_FUNC(f_term_getline)},
{"term_getscrolled", 1, 1, FEARG_1, arg1_buffer,
ret_number, TERM_FUNC(f_term_getscrolled)},
{"term_getsize", 1, 1, FEARG_1, arg1_buffer,
ret_list_number, TERM_FUNC(f_term_getsize)},
{"term_getstatus", 1, 1, FEARG_1, arg1_buffer,
ret_string, TERM_FUNC(f_term_getstatus)},
{"term_gettitle", 1, 1, FEARG_1, arg1_buffer,
ret_string, TERM_FUNC(f_term_gettitle)},
{"term_gettty", 1, 2, FEARG_1, arg2_buffer_bool,
ret_string, TERM_FUNC(f_term_gettty)},
{"term_list", 0, 0, 0, NULL,
ret_list_number, TERM_FUNC(f_term_list)},
{"term_scrape", 2, 2, FEARG_1, arg2_buffer_lnum,
ret_list_dict_any, TERM_FUNC(f_term_scrape)},
{"term_sendkeys", 2, 2, FEARG_1, arg2_buffer_string,
ret_void, TERM_FUNC(f_term_sendkeys)},
{"term_setansicolors", 2, 2, FEARG_1, arg2_buffer_list_any,
ret_void,
#if defined(FEAT_TERMINAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
f_term_setansicolors
#else
NULL
#endif
},
{"term_setapi", 2, 2, FEARG_1, arg2_buffer_string,
ret_void, TERM_FUNC(f_term_setapi)},
{"term_setkill", 2, 2, FEARG_1, arg2_buffer_string,
ret_void, TERM_FUNC(f_term_setkill)},
{"term_setrestore", 2, 2, FEARG_1, arg2_buffer_string,
ret_void, TERM_FUNC(f_term_setrestore)},
{"term_setsize", 3, 3, FEARG_1, arg3_buffer_number_number,
ret_void, TERM_FUNC(f_term_setsize)},
{"term_start", 1, 2, FEARG_1, arg2_string_or_list_dict,
ret_number, TERM_FUNC(f_term_start)},
{"term_wait", 1, 2, FEARG_1, arg2_buffer_number,
ret_void, TERM_FUNC(f_term_wait)},
{"terminalprops", 0, 0, 0, NULL,
ret_dict_string, f_terminalprops},
{"test_alloc_fail", 3, 3, FEARG_1, arg3_number,
ret_void, f_test_alloc_fail},
{"test_autochdir", 0, 0, 0, NULL,
ret_void, f_test_autochdir},
{"test_feedinput", 1, 1, FEARG_1, arg1_string,
ret_void, f_test_feedinput},
{"test_garbagecollect_now", 0, 0, 0, NULL,
ret_void, f_test_garbagecollect_now},
{"test_garbagecollect_soon", 0, 0, 0, NULL,
ret_void, f_test_garbagecollect_soon},
{"test_getvalue", 1, 1, FEARG_1, arg1_string,
ret_number, f_test_getvalue},
{"test_gui_event", 2, 2, FEARG_1, arg2_string_dict,
ret_bool, f_test_gui_event},
{"test_ignore_error", 1, 1, FEARG_1, arg1_string,
ret_void, f_test_ignore_error},
{"test_mswin_event", 2, 2, FEARG_1, arg2_string_dict,
ret_number, f_test_mswin_event},
{"test_null_blob", 0, 0, 0, NULL,
ret_blob, f_test_null_blob},
{"test_null_channel", 0, 0, 0, NULL,
ret_channel, JOB_FUNC(f_test_null_channel)},
{"test_null_dict", 0, 0, 0, NULL,
ret_dict_any, f_test_null_dict},
{"test_null_function", 0, 0, 0, NULL,
ret_func_any, f_test_null_function},
{"test_null_job", 0, 0, 0, NULL,
ret_job, JOB_FUNC(f_test_null_job)},
{"test_null_list", 0, 0, 0, NULL,
ret_list_any, f_test_null_list},
{"test_null_partial", 0, 0, 0, NULL,
ret_func_any, f_test_null_partial},
{"test_null_string", 0, 0, 0, NULL,
ret_string, f_test_null_string},
{"test_option_not_set", 1, 1, FEARG_1, arg1_string,
ret_void, f_test_option_not_set},
{"test_override", 2, 2, FEARG_2, arg2_string_number,
ret_void, f_test_override},
{"test_refcount", 1, 1, FEARG_1|FE_X, NULL,
ret_number, f_test_refcount},
{"test_setmouse", 2, 2, 0, arg2_number,
ret_void, f_test_setmouse},
{"test_settime", 1, 1, FEARG_1, arg1_number,
ret_void, f_test_settime},
{"test_srand_seed", 0, 1, FEARG_1, arg1_number,
ret_void, f_test_srand_seed},
{"test_unknown", 0, 0, 0, NULL,
ret_any, f_test_unknown},
{"test_void", 0, 0, 0, NULL,
ret_void, f_test_void},
{"timer_info", 0, 1, FEARG_1, arg1_number,
ret_list_dict_any, TIMER_FUNC(f_timer_info)},
{"timer_pause", 2, 2, FEARG_1, arg2_number_bool,
ret_void, TIMER_FUNC(f_timer_pause)},
{"timer_start", 2, 3, FEARG_1, arg3_number_any_dict,
ret_number, TIMER_FUNC(f_timer_start)},
{"timer_stop", 1, 1, FEARG_1, arg1_number,
ret_void, TIMER_FUNC(f_timer_stop)},
{"timer_stopall", 0, 0, 0, NULL,
ret_void, TIMER_FUNC(f_timer_stopall)},
{"tolower", 1, 1, FEARG_1, arg1_string,
ret_string, f_tolower},
{"toupper", 1, 1, FEARG_1, arg1_string,
ret_string, f_toupper},
{"tr", 3, 3, FEARG_1, arg3_string,
ret_string, f_tr},
{"trim", 1, 3, FEARG_1, arg3_string_string_number,
ret_string, f_trim},
{"trunc", 1, 1, FEARG_1, arg1_float_or_nr,
ret_float, f_trunc},
{"type", 1, 1, FEARG_1|FE_X, NULL,
ret_number, f_type},
{"typename", 1, 1, FEARG_1|FE_X, NULL,
ret_string, f_typename},
{"undofile", 1, 1, FEARG_1, arg1_string,
ret_string, f_undofile},
{"undotree", 0, 1, FEARG_1, arg1_buffer,
ret_dict_any, f_undotree},
{"uniq", 1, 3, FEARG_1, arg13_sortuniq,
ret_first_arg, f_uniq},
{"utf16idx", 2, 4, FEARG_1, arg4_string_number_bool_bool,
ret_number, f_utf16idx},
{"values", 1, 1, FEARG_1, arg1_dict_any,
ret_list_member, f_values},
{"virtcol", 1, 3, FEARG_1, arg3_string_or_list_bool_number,
ret_virtcol, f_virtcol},
{"virtcol2col", 3, 3, FEARG_1, arg3_number,
ret_number, f_virtcol2col},
{"visualmode", 0, 1, 0, arg1_bool,
ret_string, f_visualmode},
{"wildmenumode", 0, 0, 0, NULL,
ret_number, f_wildmenumode},
{"win_execute", 2, 3, FEARG_2, arg23_win_execute,
ret_string, f_win_execute},
{"win_findbuf", 1, 1, FEARG_1, arg1_number,
ret_list_number, f_win_findbuf},
{"win_getid", 0, 2, FEARG_1, arg2_number,
ret_number, f_win_getid},
{"win_gettype", 0, 1, FEARG_1, arg1_number,
ret_string, f_win_gettype},
{"win_gotoid", 1, 1, FEARG_1, arg1_number,
ret_number_bool, f_win_gotoid},
{"win_id2tabwin", 1, 1, FEARG_1, arg1_number,
ret_list_number, f_win_id2tabwin},
{"win_id2win", 1, 1, FEARG_1, arg1_number,
ret_number, f_win_id2win},
{"win_move_separator", 2, 2, FEARG_1, arg2_number,
ret_number_bool, f_win_move_separator},
{"win_move_statusline", 2, 2, FEARG_1, arg2_number,
ret_number_bool, f_win_move_statusline},
{"win_screenpos", 1, 1, FEARG_1, arg1_number,
ret_list_number, f_win_screenpos},
{"win_splitmove", 2, 3, FEARG_1, arg3_number_number_dict,
ret_number_bool, f_win_splitmove},
{"winbufnr", 1, 1, FEARG_1, arg1_number,
ret_number, f_winbufnr},
{"wincol", 0, 0, 0, NULL,
ret_number, f_wincol},
{"windowsversion", 0, 0, 0, NULL,
ret_string, f_windowsversion},
{"winheight", 1, 1, FEARG_1, arg1_number,
ret_number, f_winheight},
{"winlayout", 0, 1, FEARG_1, arg1_number,
ret_list_any, f_winlayout},
{"winline", 0, 0, 0, NULL,
ret_number, f_winline},
{"winnr", 0, 1, FEARG_1, arg1_string,
ret_number, f_winnr},
{"winrestcmd", 0, 0, 0, NULL,
ret_string, f_winrestcmd},
{"winrestview", 1, 1, FEARG_1, arg1_dict_any,
ret_void, f_winrestview},
{"winsaveview", 0, 0, 0, NULL,
ret_dict_number, f_winsaveview},
{"winwidth", 1, 1, FEARG_1, arg1_number,
ret_number, f_winwidth},
{"wordcount", 0, 0, 0, NULL,
ret_dict_number, f_wordcount},
{"writefile", 2, 3, FEARG_1, arg23_writefile,
ret_number_bool, f_writefile},
{"xor", 2, 2, FEARG_1, arg2_number,
ret_number, f_xor},
};
/*
* Return true if specified function allows a type as an argument.
*/
static int
func_allows_type(int idx)
{
return (global_functions[idx].f_argtype & FE_X) != 0;
}
/*
* Function given to ExpandGeneric() to obtain the list of internal
* or user defined function names.
*/
char_u *
get_function_name(expand_T *xp, int idx)
{
static int intidx = -1;
char_u *name;
if (idx == 0)
intidx = -1;
if (intidx < 0)
{
name = get_user_func_name(xp, idx);
if (name != NULL)
{
if (*name != NUL && *name != '<'
&& STRNCMP("g:", xp->xp_pattern, 2) == 0)
return cat_prefix_varname('g', name);
return name;
}
}
if (++intidx < (int)ARRAY_LENGTH(global_functions))
{
// Skip if the function doesn't have an implementation (feature not
// implemented).
if (global_functions[intidx].f_func == NULL)
return (char_u *)"";
STRCPY(IObuff, global_functions[intidx].f_name);
STRCAT(IObuff, "(");
if (global_functions[intidx].f_max_argc == 0)
STRCAT(IObuff, ")");
return IObuff;
}
return NULL;
}
/*
* Function given to ExpandGeneric() to obtain the list of internal or
* user defined variable or function names.
*/
char_u *
get_expr_name(expand_T *xp, int idx)
{
static int intidx = -1;
char_u *name;
if (idx == 0)
intidx = -1;
if (intidx < 0)
{
name = get_function_name(xp, idx);
if (name != NULL)
return name;
}
return get_user_var_name(xp, ++intidx);
}
/*
* Find internal function "name" in table "global_functions".
* Return index, or -1 if not found or "implemented" is TRUE and the function
* is not implemented.
*/
static int
find_internal_func_opt(char_u *name, int implemented)
{
int first = 0;
int last;
int cmp;
int x;
last = (int)ARRAY_LENGTH(global_functions) - 1;
// Find the function name in the table. Binary search.
while (first <= last)
{
x = first + ((unsigned)(last - first) >> 1);
cmp = STRCMP(name, global_functions[x].f_name);
if (cmp < 0)
last = x - 1;
else if (cmp > 0)
first = x + 1;
else if (implemented && global_functions[x].f_func == NULL)
break;
else
return x;
}
return -1;
}
/*
* Find internal function "name" in table "global_functions".
* Return index, or -1 if not found or the function is not implemented.
*/
int
find_internal_func(char_u *name)
{
return find_internal_func_opt(name, TRUE);
}
int
has_internal_func(char_u *name)
{
return find_internal_func_opt(name, TRUE) >= 0;
}
static int
has_internal_func_name(char_u *name)
{
return find_internal_func_opt(name, FALSE) >= 0;
}
char *
internal_func_name(int idx)
{
return global_functions[idx].f_name;
}
/*
* Check the argument types for builtin function "idx".
* Uses the list of types on the type stack: "types".
* Return FAIL and gives an error message when a type is wrong.
*/
int
internal_func_check_arg_types(
type2_T *types,
int idx,
int argcount,
cctx_T *cctx)
{
// Some internal functions accept types like Class as arguments. For other
// functions, check the arguments are not types.
if (!(func_allows_type(idx)))
{
for (int i = 0; i < argcount; ++i)
if (check_type_is_value(types[i].type_curr) == FAIL)
return FAIL;
}
argcheck_T *argchecks = global_functions[idx].f_argcheck;
if (argchecks == NULL)
return OK;
argcontext_T context;
context.arg_count = argcount;
context.arg_types = types;
context.arg_cctx = cctx;
for (int i = 0; i < argcount && argchecks[i] != NULL; ++i)
{
context.arg_idx = i;
if (argchecks[i](types[i].type_curr, types[i].type_decl,
&context) == FAIL)
return FAIL;
}
return OK;
}
/*
* Get the argument count for function "idx".
* "argcount" is the total argument count, "min_argcount" the non-optional
* argument count.
*/
void
internal_func_get_argcount(int idx, int *argcount, int *min_argcount)
{
*argcount = global_functions[idx].f_max_argc;
*min_argcount = global_functions[idx].f_min_argc;
}
/*
* Call the "f_retfunc" function to obtain the return type of function "idx".
* "decl_type" is set to the declared type.
* "argtypes" is the list of argument types or NULL when there are no
* arguments.
* "argcount" may be less than the actual count when only getting the type.
*/
type_T *
internal_func_ret_type(
int idx,
int argcount,
type2_T *argtypes,
type_T **decl_type,
garray_T *type_gap)
{
type_T *ret;
current_type_gap = type_gap;
*decl_type = NULL;
ret = global_functions[idx].f_retfunc(argcount, argtypes, decl_type);
if (*decl_type == NULL)
*decl_type = ret;
current_type_gap = NULL;
return ret;
}
/*
* Return TRUE if "idx" is for the map() function.
*/
int
internal_func_is_map(int idx)
{
return global_functions[idx].f_func == f_map;
}
/*
* Check the argument count to use for internal function "idx".
* Returns -1 for failure, 0 if no method base accepted, 1 if method base is
* first argument, 2 if method base is second argument, etc. 9 if method base
* is last argument.
*/
int
check_internal_func(int idx, int argcount)
{
funcerror_T res;
char *name;
if (argcount < global_functions[idx].f_min_argc)
res = FCERR_TOOFEW;
else if (argcount > global_functions[idx].f_max_argc)
res = FCERR_TOOMANY;
else
return global_functions[idx].f_argtype & FEARG_MASK;
name = internal_func_name(idx);
if (res == FCERR_TOOMANY)
semsg(_(e_too_many_arguments_for_function_str), name);
else
semsg(_(e_not_enough_arguments_for_function_str), name);
return -1;
}
/*
* Some internal functions accept types like Class as arguments. For other
* functions, check the arguments are not types.
*
* Return OK/FAIL.
*/
static int
check_args_for_type(int idx, int argcount, typval_T *argvars)
{
if (!func_allows_type(idx))
{
for (int i = 0; i < argcount; ++i)
if (check_typval_is_value(&argvars[i]) == FAIL)
return FAIL;
}
return OK;
}
funcerror_T
call_internal_func(
char_u *name,
int argcount,
typval_T *argvars,
typval_T *rettv)
{
int i;
i = find_internal_func(name);
if (i < 0)
return FCERR_UNKNOWN;
if (argcount < global_functions[i].f_min_argc)
return FCERR_TOOFEW;
if (argcount > global_functions[i].f_max_argc)
return FCERR_TOOMANY;
if (check_args_for_type(i, argcount, argvars) == FAIL)
return FCERR_OTHER;
argvars[argcount].v_type = VAR_UNKNOWN;
global_functions[i].f_func(argvars, rettv);
return FCERR_NONE;
}
void
call_internal_func_by_idx(
int idx,
typval_T *argvars,
typval_T *rettv)
{
global_functions[idx].f_func(argvars, rettv);
}
/*
* Invoke a method for base->method().
*/
funcerror_T
call_internal_method(
char_u *name,
int argcount,
typval_T *argvars,
typval_T *rettv,
typval_T *basetv)
{
int fi;
typval_T argv[MAX_FUNC_ARGS + 1];
fi = find_internal_func(name);
if (fi < 0)
return FCERR_UNKNOWN;
if ((global_functions[fi].f_argtype & FEARG_MASK) == 0)
return FCERR_NOTMETHOD;
if (argcount + 1 < global_functions[fi].f_min_argc)
return FCERR_TOOFEW;
if (argcount + 1 > global_functions[fi].f_max_argc)
return FCERR_TOOMANY;
if (check_args_for_type(fi, argcount, argvars) == FAIL)
return FCERR_OTHER;
if ((global_functions[fi].f_argtype & FEARG_MASK) == FEARG_2)
{
if (argcount < 1)
return FCERR_TOOFEW;
// base value goes second
argv[0] = argvars[0];
argv[1] = *basetv;
for (int i = 1; i < argcount; ++i)
argv[i + 1] = argvars[i];
}
else if ((global_functions[fi].f_argtype & FEARG_MASK) == FEARG_3)
{
if (argcount < 2)
return FCERR_TOOFEW;
// base value goes third
argv[0] = argvars[0];
argv[1] = argvars[1];
argv[2] = *basetv;
for (int i = 2; i < argcount; ++i)
argv[i + 1] = argvars[i];
}
else if ((global_functions[fi].f_argtype & FEARG_MASK) == FEARG_4)
{
if (argcount < 3)
return FCERR_TOOFEW;
// base value goes fourth
argv[0] = argvars[0];
argv[1] = argvars[1];
argv[2] = argvars[2];
argv[3] = *basetv;
for (int i = 3; i < argcount; ++i)
argv[i + 1] = argvars[i];
}
else
{
// FEARG_1: base value goes first
argv[0] = *basetv;
for (int i = 0; i < argcount; ++i)
argv[i + 1] = argvars[i];
}
argv[argcount + 1].v_type = VAR_UNKNOWN;
if (check_args_for_type(fi, argcount + 1, argv) == FAIL)
return FCERR_OTHER;
global_functions[fi].f_func(argv, rettv);
return FCERR_NONE;
}
/*
* Return TRUE for a non-zero Number and a non-empty String.
*/
int
non_zero_arg(typval_T *argvars)
{
return ((argvars[0].v_type == VAR_NUMBER
&& argvars[0].vval.v_number != 0)
|| (argvars[0].v_type == VAR_BOOL
&& argvars[0].vval.v_number == VVAL_TRUE)
|| (argvars[0].v_type == VAR_STRING
&& argvars[0].vval.v_string != NULL
&& *argvars[0].vval.v_string != NUL));
}
/*
* "and(expr, expr)" function
*/
static void
f_and(typval_T *argvars, typval_T *rettv)
{
if (in_vim9script()
&& (check_for_number_arg(argvars, 0) == FAIL
|| check_for_number_arg(argvars, 1) == FAIL))
return;
rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL)
& tv_get_number_chk(&argvars[1], NULL);
}
/*
* "balloon_show()" function
*/
#ifdef FEAT_BEVAL
static void
f_balloon_gettext(typval_T *argvars UNUSED, typval_T *rettv)
{
rettv->v_type = VAR_STRING;
if (balloonEval == NULL)
return;
if (balloonEval->msg == NULL)
rettv->vval.v_string = NULL;
else
rettv->vval.v_string = vim_strsave(balloonEval->msg);
}
static void
f_balloon_show(typval_T *argvars, typval_T *rettv UNUSED)
{
if (balloonEval == NULL)
return;
if (in_vim9script()
&& check_for_string_or_list_arg(argvars, 0) == FAIL)
return;
if (argvars[0].v_type == VAR_LIST
# ifdef FEAT_GUI
&& !gui.in_use
# endif
)
{
list_T *l = argvars[0].vval.v_list;
// empty list removes the balloon
post_balloon(balloonEval, NULL,
l == NULL || l->lv_len == 0 ? NULL : l);
}
else
{
char_u *mesg;
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
mesg = tv_get_string_chk(&argvars[0]);
if (mesg != NULL)
// empty string removes the balloon
post_balloon(balloonEval, *mesg == NUL ? NULL : mesg, NULL);
}
}
# if defined(FEAT_BEVAL_TERM)
static void
f_balloon_split(typval_T *argvars, typval_T *rettv UNUSED)
{
if (rettv_list_alloc(rettv) != OK)
return;
char_u *msg;
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
msg = tv_get_string_chk(&argvars[0]);
if (msg != NULL)
{
pumitem_T *array;
int size = split_message(msg, &array);
// Skip the first and last item, they are always empty.
for (int i = 1; i < size - 1; ++i)
list_append_string(rettv->vval.v_list, array[i].pum_text, -1);
while (size > 0)
vim_free(array[--size].pum_text);
vim_free(array);
}
}
# endif
#endif
/*
* Get the buffer from "arg" and give an error and return NULL if it is not
* valid.
*/
buf_T *
get_buf_arg(typval_T *arg)
{
buf_T *buf;
++emsg_off;
buf = tv_get_buf(arg, FALSE);
--emsg_off;
if (buf == NULL)
semsg(_(e_invalid_buffer_name_str), tv_get_string(arg));
return buf;
}
/*
* "byte2line(byte)" function
*/
static void
f_byte2line(typval_T *argvars UNUSED, typval_T *rettv)
{
#ifndef FEAT_BYTEOFF
rettv->vval.v_number = -1;
#else
long boff = 0;
if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL)
return;
boff = tv_get_number(&argvars[0]) - 1; // boff gets -1 on type error
if (boff < 0)
rettv->vval.v_number = -1;
else
rettv->vval.v_number = ml_find_line_or_offset(curbuf,
(linenr_T)0, &boff);
#endif
}
/*
* "call(func, arglist [, dict])" function
*/
static void
f_call(typval_T *argvars, typval_T *rettv)
{
char_u *func;
partial_T *partial = NULL;
dict_T *selfdict = NULL;
char_u *dot;
char_u *tofree = NULL;
if (in_vim9script()
&& (check_for_string_or_func_arg(argvars, 0) == FAIL
|| check_for_list_arg(argvars, 1) == FAIL
|| check_for_opt_dict_arg(argvars, 2) == FAIL))
return;
if (check_for_list_arg(argvars, 1) == FAIL)
return;
if (argvars[1].vval.v_list == NULL)
return;
if (argvars[0].v_type == VAR_FUNC)
func = argvars[0].vval.v_string;
else if (argvars[0].v_type == VAR_PARTIAL)
{
partial = argvars[0].vval.v_partial;
func = partial_name(partial);
}
else
func = tv_get_string(&argvars[0]);
if (func == NULL || *func == NUL)
return; // type error, empty name or null function
dot = vim_strchr(func, '.');
if (dot != NULL)
{
imported_T *import = find_imported(func, dot - func, TRUE);
if (import != NULL && SCRIPT_ID_VALID(import->imp_sid))
{
scriptitem_T *si = SCRIPT_ITEM(import->imp_sid);
if (si->sn_autoload_prefix != NULL)
{
// Turn "import.Func" into "scriptname#Func".
tofree = concat_str(si->sn_autoload_prefix, dot + 1);
if (tofree == NULL)
return;
func = tofree;
}
}
}
if (argvars[2].v_type != VAR_UNKNOWN)
{
if (check_for_dict_arg(argvars, 2) == FAIL)
return;
selfdict = argvars[2].vval.v_dict;
}
(void)func_call(func, &argvars[1], partial, selfdict, rettv);
vim_free(tofree);
}
/*
* "changenr()" function
*/
static void
f_changenr(typval_T *argvars UNUSED, typval_T *rettv)
{
rettv->vval.v_number = curbuf->b_u_seq_cur;
}
/*
* "char2nr(string)" function
*/
static void
f_char2nr(typval_T *argvars, typval_T *rettv)
{
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_opt_bool_arg(argvars, 1) == FAIL))
return;
if (has_mbyte)
{
int utf8 = 0;
if (argvars[1].v_type != VAR_UNKNOWN)
utf8 = (int)tv_get_bool_chk(&argvars[1], NULL);
if (utf8)
rettv->vval.v_number = utf_ptr2char(tv_get_string(&argvars[0]));
else
rettv->vval.v_number = (*mb_ptr2char)(tv_get_string(&argvars[0]));
}
else
rettv->vval.v_number = tv_get_string(&argvars[0])[0];
}
/*
* Get the current cursor column and store it in 'rettv'. If 'charcol' is TRUE,
* returns the character index of the column. Otherwise, returns the byte index
* of the column.
*/
static void
get_col(typval_T *argvars, typval_T *rettv, int charcol)
{
colnr_T col = 0;
pos_T *fp;
switchwin_T switchwin;
int winchanged = FALSE;
if (check_for_string_or_list_arg(argvars, 0) == FAIL
|| check_for_opt_number_arg(argvars, 1) == FAIL)
return;
if (argvars[1].v_type != VAR_UNKNOWN)
{
tabpage_T *tp;
win_T *wp;
// use the window specified in the second argument
wp = win_id2wp_tp((int)tv_get_number(&argvars[1]), &tp);
if (wp == NULL || tp == NULL)
return;
if (switch_win_noblock(&switchwin, wp, tp, TRUE) != OK)
return;
check_cursor();
winchanged = TRUE;
}
int fnum = curbuf->b_fnum;
fp = var2fpos(&argvars[0], FALSE, &fnum, charcol);
if (fp != NULL && fnum == curbuf->b_fnum)
{
if (fp->col == MAXCOL)
{
// '> can be MAXCOL, get the length of the line then
if (fp->lnum <= curbuf->b_ml.ml_line_count)
col = (colnr_T)STRLEN(ml_get(fp->lnum)) + 1;
else
col = MAXCOL;
}
else
{
col = fp->col + 1;
// col(".") when the cursor is on the NUL at the end of the line
// because of "coladd" can be seen as an extra column.
if (virtual_active() && fp == &curwin->w_cursor)
{
char_u *p = ml_get_cursor();
if (curwin->w_cursor.coladd >= (colnr_T)chartabsize(p,
curwin->w_virtcol - curwin->w_cursor.coladd))
{
int l;
if (*p != NUL && p[(l = (*mb_ptr2len)(p))] == NUL)
col += l;
}
}
}
}
rettv->vval.v_number = col;
if (winchanged)
restore_win_noblock(&switchwin, TRUE);
}
/*
* "charcol()" function
*/
static void
f_charcol(typval_T *argvars, typval_T *rettv)
{
get_col(argvars, rettv, TRUE);
}
win_T *
get_optional_window(typval_T *argvars, int idx)
{
win_T *win = curwin;
if (argvars[idx].v_type == VAR_UNKNOWN)
return curwin;
win = find_win_by_nr_or_id(&argvars[idx]);
if (win == NULL)
{
emsg(_(e_invalid_window_number));
return NULL;
}
return win;
}
/*
* "col(string)" function
*/
static void
f_col(typval_T *argvars, typval_T *rettv)
{
get_col(argvars, rettv, FALSE);
}
/*
* "confirm(message, buttons[, default [, type]])" function
*/
static void
f_confirm(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
{
#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
char_u *message;
char_u *buttons = NULL;
char_u buf[NUMBUFLEN];
char_u buf2[NUMBUFLEN];
int def = 1;
int type = VIM_GENERIC;
char_u *typestr;
int error = FALSE;
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| (check_for_opt_string_arg(argvars, 1) == FAIL
|| (argvars[1].v_type != VAR_UNKNOWN
&& (check_for_opt_number_arg(argvars, 2) == FAIL
|| (argvars[2].v_type != VAR_UNKNOWN
&& check_for_opt_string_arg(argvars, 3) == FAIL))))))
return;
message = tv_get_string_chk(&argvars[0]);
if (message == NULL)
error = TRUE;
if (argvars[1].v_type != VAR_UNKNOWN)
{
buttons = tv_get_string_buf_chk(&argvars[1], buf);
if (buttons == NULL)
error = TRUE;
if (argvars[2].v_type != VAR_UNKNOWN)
{
def = (int)tv_get_number_chk(&argvars[2], &error);
if (argvars[3].v_type != VAR_UNKNOWN)
{
typestr = tv_get_string_buf_chk(&argvars[3], buf2);
if (typestr == NULL)
error = TRUE;
else
{
switch (TOUPPER_ASC(*typestr))
{
case 'E': type = VIM_ERROR; break;
case 'Q': type = VIM_QUESTION; break;
case 'I': type = VIM_INFO; break;
case 'W': type = VIM_WARNING; break;
case 'G': type = VIM_GENERIC; break;
}
}
}
}
}
if (buttons == NULL || *buttons == NUL)
buttons = (char_u *)_("&Ok");
if (!error)
rettv->vval.v_number = do_dialog(type, NULL, message, buttons,
def, NULL, FALSE);
#endif
}
/*
* "copy()" function
*/
static void
f_copy(typval_T *argvars, typval_T *rettv)
{
item_copy(&argvars[0], rettv, FALSE, TRUE, 0);
}
/*
* 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.
*/
static void
set_cursorpos(typval_T *argvars, typval_T *rettv, int charcol)
{
long lnum, col;
long coladd = 0;
int set_curswant = TRUE;
if (in_vim9script()
&& (check_for_string_or_number_or_list_arg(argvars, 0) == FAIL
|| check_for_opt_number_arg(argvars, 1) == FAIL
|| (argvars[1].v_type != VAR_UNKNOWN
&& check_for_opt_number_arg(argvars, 2) == FAIL)))
return;
rettv->vval.v_number = -1;
if (argvars[0].v_type == VAR_LIST)
{
pos_T pos;
colnr_T curswant = -1;
if (list2fpos(argvars, &pos, NULL, &curswant, charcol) == FAIL)
{
emsg(_(e_invalid_argument));
return;
}
lnum = pos.lnum;
col = pos.col;
coladd = pos.coladd;
if (curswant >= 0)
{
curwin->w_curswant = curswant - 1;
set_curswant = FALSE;
}
}
else if ((argvars[0].v_type == VAR_NUMBER
|| argvars[0].v_type == VAR_STRING)
&& (argvars[1].v_type == VAR_NUMBER
|| argvars[1].v_type == VAR_STRING))
{
lnum = tv_get_lnum(argvars);
if (lnum < 0)
semsg(_(e_invalid_argument_str), tv_get_string(&argvars[0]));
else if (lnum == 0)
lnum = curwin->w_cursor.lnum;
col = (long)tv_get_number_chk(&argvars[1], NULL);
if (charcol)
col = buf_charidx_to_byteidx(curbuf, lnum, col) + 1;
if (argvars[2].v_type != VAR_UNKNOWN)
coladd = (long)tv_get_number_chk(&argvars[2], NULL);
}
else
{
emsg(_(e_invalid_argument));
return;
}
if (lnum < 0 || col < 0 || coladd < 0)
return; // type error; errmsg already given
if (lnum > 0)
curwin->w_cursor.lnum = lnum;
if (col > 0)
curwin->w_cursor.col = col - 1;
curwin->w_cursor.coladd = coladd;
// Make sure the cursor is in a valid position.
check_cursor();
// Correct cursor for multi-byte character.
if (has_mbyte)
mb_adjust_cursor();
curwin->w_set_curswant = set_curswant;
rettv->vval.v_number = 0;
}
/*
* "cursor(lnum, col)" function, or
* "cursor(list)"
*
* Moves the cursor to the specified line and column.
* Returns 0 when the position could be set, -1 otherwise.
*/
static void
f_cursor(typval_T *argvars, typval_T *rettv)
{
set_cursorpos(argvars, rettv, FALSE);
}
#ifdef MSWIN
/*
* "debugbreak()" function
*/
static void
f_debugbreak(typval_T *argvars, typval_T *rettv)
{
int pid;
rettv->vval.v_number = FAIL;
if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL)
return;
pid = (int)tv_get_number(&argvars[0]);
if (pid == 0)
{
emsg(_(e_invalid_argument));
return;
}
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
if (hProcess == NULL)
return;
DebugBreakProcess(hProcess);
CloseHandle(hProcess);
rettv->vval.v_number = OK;
}
#endif
/*
* "deepcopy()" function
*/
static void
f_deepcopy(typval_T *argvars, typval_T *rettv)
{
varnumber_T noref = 0;
if (check_for_opt_bool_arg(argvars, 1) == FAIL)
return;
if (argvars[1].v_type != VAR_UNKNOWN)
noref = tv_get_bool_chk(&argvars[1], NULL);
item_copy(&argvars[0], rettv, TRUE, TRUE, noref == 0 ? get_copyID() : 0);
}
/*
* "did_filetype()" function
*/
static void
f_did_filetype(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
{
rettv->vval.v_number = did_filetype;
}
/*
* "echoraw({expr})" function
*/
static void
f_echoraw(typval_T *argvars, typval_T *rettv UNUSED)
{
char_u *str;
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
str = tv_get_string_chk(&argvars[0]);
if (str != NULL && *str != NUL)
{
out_str(str);
out_flush();
}
}
/*
* "empty({expr})" function
*/
static void
f_empty(typval_T *argvars, typval_T *rettv)
{
int n = FALSE;
switch (argvars[0].v_type)
{
case VAR_STRING:
case VAR_FUNC:
n = argvars[0].vval.v_string == NULL
|| *argvars[0].vval.v_string == NUL;
break;
case VAR_PARTIAL:
n = FALSE;
break;
case VAR_NUMBER:
n = argvars[0].vval.v_number == 0;
break;
case VAR_FLOAT:
n = argvars[0].vval.v_float == 0.0;
break;
case VAR_LIST:
n = argvars[0].vval.v_list == NULL
|| argvars[0].vval.v_list->lv_len == 0;
break;
case VAR_DICT:
n = argvars[0].vval.v_dict == NULL
|| argvars[0].vval.v_dict->dv_hashtab.ht_used == 0;
break;
case VAR_BOOL:
case VAR_SPECIAL:
n = argvars[0].vval.v_number != VVAL_TRUE;
break;
case VAR_CLASS:
n = argvars[0].vval.v_class != NULL;
break;
case VAR_OBJECT:
n = argvars[0].vval.v_object != NULL;
break;
case VAR_BLOB:
n = argvars[0].vval.v_blob == NULL
|| argvars[0].vval.v_blob->bv_ga.ga_len == 0;
break;
case VAR_JOB:
#ifdef FEAT_JOB_CHANNEL
n = argvars[0].vval.v_job == NULL
|| argvars[0].vval.v_job->jv_status != JOB_STARTED;
break;
#endif
case VAR_CHANNEL:
#ifdef FEAT_JOB_CHANNEL
n = argvars[0].vval.v_channel == NULL
|| !channel_is_open(argvars[0].vval.v_channel);
break;
#endif
case VAR_TYPEALIAS:
n = argvars[0].vval.v_typealias == NULL
|| argvars[0].vval.v_typealias->ta_name == NULL
|| *argvars[0].vval.v_typealias->ta_name == NUL;
break;
case VAR_UNKNOWN:
case VAR_ANY:
case VAR_VOID:
case VAR_INSTR:
internal_error_no_abort("f_empty(UNKNOWN)");
n = TRUE;
break;
}
rettv->vval.v_number = n;
}
/*
* "environ()" function
*/
static void
f_environ(typval_T *argvars UNUSED, typval_T *rettv)
{
#if !defined(AMIGA)
int i = 0;
char_u *entry, *value;
# if defined (MSWIN)
# if !defined(_UCRT)
extern wchar_t **_wenviron;
# endif
# else
extern char **environ;
# endif
if (rettv_dict_alloc(rettv) == FAIL)
return;
# ifdef MSWIN
if (*_wenviron == NULL)
return;
# else
if (*environ == NULL)
return;
# endif
for (i = 0; ; ++i)
{
# ifdef MSWIN
short_u *p;
if ((p = (short_u *)_wenviron[i]) == NULL)
return;
entry = utf16_to_enc(p, NULL);
# else
if ((entry = (char_u *)environ[i]) == NULL)
return;
entry = vim_strsave(entry);
# endif
if (entry == NULL) // out of memory
return;
if ((value = vim_strchr(entry, '=')) == NULL)
{
vim_free(entry);
continue;
}
*value++ = NUL;
dict_add_string(rettv->vval.v_dict, (char *)entry, value);
vim_free(entry);
}
#endif
}
/*
* "err_teapot()" function
*/
static void
f_err_teapot(typval_T *argvars, typval_T *rettv UNUSED)
{
if (argvars[0].v_type != VAR_UNKNOWN)
{
if (argvars[0].v_type == VAR_STRING)
{
char_u *s = tv_get_string_strict(&argvars[0]);
if (*skipwhite(s) == NUL)
return;
}
int err = FALSE;
int do_503 = eval_expr_to_bool(&argvars[0], &err);
if (!err && do_503)
{
emsg(_(e_coffee_currently_not_available));
return;
}
}
emsg(_(e_im_a_teapot));
}
/*
* "escape({string}, {chars})" function
*/
static void
f_escape(typval_T *argvars, typval_T *rettv)
{
char_u buf[NUMBUFLEN];
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_string_arg(argvars, 1) == FAIL))
return;
rettv->vval.v_string = vim_strsave_escaped(tv_get_string(&argvars[0]),
tv_get_string_buf(&argvars[1], buf));
rettv->v_type = VAR_STRING;
}
/*
* "eval()" function
*/
static void
f_eval(typval_T *argvars, typval_T *rettv)
{
char_u *s, *p;
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
s = tv_get_string_chk(&argvars[0]);
if (s != NULL)
s = skipwhite(s);
p = s;
if (s == NULL || eval1(&s, rettv, &EVALARG_EVALUATE) == FAIL)
{
if (p != NULL && !aborting())
semsg(_(e_invalid_expression_str), p);
need_clr_eos = FALSE;
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
}
else if (*s != NUL)
semsg(_(e_trailing_characters_str), s);
}
/*
* "eventhandler()" function
*/
static void
f_eventhandler(typval_T *argvars UNUSED, typval_T *rettv)
{
rettv->vval.v_number = vgetc_busy || input_busy;
}
static garray_T redir_execute_ga;
/*
* Append "value[value_len]" to the execute() output.
*/
void
execute_redir_str(char_u *value, int value_len)
{
int len;
if (value_len == -1)
len = (int)STRLEN(value); // Append the entire string
else
len = value_len; // Append only "value_len" characters
if (ga_grow(&redir_execute_ga, len) == FAIL)
return;
mch_memmove((char *)redir_execute_ga.ga_data
+ redir_execute_ga.ga_len, value, len);
redir_execute_ga.ga_len += len;
}
#if defined(FEAT_LUA) || defined(PROTO)
/*
* Get next line from a string containing NL separated lines.
* Called by do_cmdline() to get the next line.
* Returns an allocated string, or NULL when at the end of the string.
*/
static char_u *
get_str_line(
int c UNUSED,
void *cookie,
int indent UNUSED,
getline_opt_T options UNUSED)
{
char_u *start = *(char_u **)cookie;
char_u *line;
char_u *p;
p = start;
if (p == NULL || *p == NUL)
return NULL;
p = vim_strchr(p, '\n');
if (p == NULL)
line = vim_strsave(start);
else
{
line = vim_strnsave(start, p - start);
p++;
}
*(char_u **)cookie = p;
return line;
}
/*
* Execute a series of Ex commands in 'str'
*/
void
execute_cmds_from_string(char_u *str)
{
do_cmdline(NULL, get_str_line, (void *)&str,
DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED);
}
#endif
/*
* Get next line from a list.
* Called by do_cmdline() to get the next line.
* Returns allocated string, or NULL for end of function.
*/
char_u *
get_list_line(
int c UNUSED,
void *cookie,
int indent UNUSED,
getline_opt_T options UNUSED)
{
listitem_T **p = (listitem_T **)cookie;
listitem_T *item = *p;
char_u buf[NUMBUFLEN];
char_u *s;
if (item == NULL)
return NULL;
s = tv_get_string_buf_chk(&item->li_tv, buf);
*p = item->li_next;
return s == NULL ? NULL : vim_strsave(s);
}
/*
* "execute()" function
*/
void
execute_common(typval_T *argvars, typval_T *rettv, int arg_off)
{
char_u *cmd = NULL;
list_T *list = NULL;
int save_msg_silent = msg_silent;
int save_emsg_silent = emsg_silent;
int save_emsg_noredir = emsg_noredir;
int save_redir_execute = redir_execute;
int save_redir_off = redir_off;
garray_T save_ga;
int save_msg_col = msg_col;
int save_sticky_cmdmod_flags = sticky_cmdmod_flags;
int echo_output = FALSE;
rettv->vval.v_string = NULL;
rettv->v_type = VAR_STRING;
if (argvars[arg_off].v_type == VAR_LIST)
{
list = argvars[arg_off].vval.v_list;
if (list == NULL || list->lv_len == 0)
// empty list, no commands, empty output
return;
++list->lv_refcount;
}
else if (argvars[arg_off].v_type == VAR_JOB
|| argvars[arg_off].v_type == VAR_CHANNEL)
{
semsg(_(e_using_invalid_value_as_string_str),
vartype_name(argvars[arg_off].v_type));
return;
}
else
{
cmd = tv_get_string_chk(&argvars[arg_off]);
if (cmd == NULL)
return;
}
if (argvars[arg_off + 1].v_type != VAR_UNKNOWN)
{
char_u buf[NUMBUFLEN];
char_u *s = tv_get_string_buf_chk_strict(&argvars[arg_off + 1], buf,
in_vim9script());
if (s == NULL)
return;
if (*s == NUL)
echo_output = TRUE;
if (STRNCMP(s, "silent", 6) == 0)
++msg_silent;
if (STRCMP(s, "silent!") == 0)
{
emsg_silent = TRUE;
emsg_noredir = TRUE;
}
}
else
++msg_silent;
if (redir_execute)
save_ga = redir_execute_ga;
ga_init2(&redir_execute_ga, sizeof(char), 500);
redir_execute = TRUE;
redir_off = FALSE;
if (!echo_output)
msg_col = 0; // prevent leading spaces
// For "legacy call execute('cmd')" and "vim9cmd execute('cmd')" apply the
// command modifiers to "cmd".
sticky_cmdmod_flags = cmdmod.cmod_flags & (CMOD_LEGACY | CMOD_VIM9CMD);
if (cmd != NULL)
do_cmdline_cmd(cmd);
else
{
listitem_T *item;
CHECK_LIST_MATERIALIZE(list);
item = list->lv_first;
do_cmdline(NULL, get_list_line, (void *)&item,
DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED);
--list->lv_refcount;
}
sticky_cmdmod_flags = save_sticky_cmdmod_flags;
// Need to append a NUL to the result.
if (ga_grow(&redir_execute_ga, 1) == OK)
{
((char *)redir_execute_ga.ga_data)[redir_execute_ga.ga_len] = NUL;
rettv->vval.v_string = redir_execute_ga.ga_data;
}
else
{
ga_clear(&redir_execute_ga);
rettv->vval.v_string = NULL;
}
msg_silent = save_msg_silent;
emsg_silent = save_emsg_silent;
emsg_noredir = save_emsg_noredir;
redir_execute = save_redir_execute;
if (redir_execute)
redir_execute_ga = save_ga;
redir_off = save_redir_off;
// "silent reg" or "silent echo x" leaves msg_col somewhere in the line.
if (echo_output)
// When not working silently: put it in column zero. A following
// "echon" will overwrite the message, unavoidably.
msg_col = 0;
else
// When working silently: Put it back where it was, since nothing
// should have been written.
msg_col = save_msg_col;
}
/*
* "execute()" function
*/
static void
f_execute(typval_T *argvars, typval_T *rettv)
{
if (in_vim9script()
&& (check_for_string_or_list_arg(argvars, 0) == FAIL
|| check_for_opt_string_arg(argvars, 1) == FAIL))
return;
execute_common(argvars, rettv, 0);
}
/*
* "exists()" function
*/
void
f_exists(typval_T *argvars, typval_T *rettv)
{
char_u *p;
int n = FALSE;
if (in_vim9script() && check_for_nonempty_string_arg(argvars, 0) == FAIL)
return;
p = tv_get_string(&argvars[0]);
if (*p == '$') // environment variable
{
// first try "normal" environment variables (fast)
if (mch_getenv(p + 1) != NULL)
n = TRUE;
else
{
// try expanding things like $VIM and ${HOME}
p = expand_env_save(p);
if (p != NULL && *p != '$')
n = TRUE;
vim_free(p);
}
}
else if (*p == '&' || *p == '+') // option
{
n = (eval_option(&p, NULL, TRUE) == OK);
if (*skipwhite(p) != NUL)
n = FALSE; // trailing garbage
}
else if (*p == '*') // internal or user defined function
{
n = function_exists(p + 1, FALSE);
}
else if (*p == '?') // internal function only
{
n = has_internal_func_name(p + 1);
}
else if (*p == ':')
{
n = cmd_exists(p + 1);
}
else if (*p == '#')
{
if (p[1] == '#')
n = autocmd_supported(p + 2);
else
n = au_exists(p + 1);
}
else // internal variable
{
n = var_exists(p);
}
rettv->vval.v_number = n;
}
static void
f_exists_compiled(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
{
emsg(_(e_exists_compiled_can_only_be_used_in_def_function));
}
/*
* "expand()" function
*/
static void
f_expand(typval_T *argvars, typval_T *rettv)
{
char_u *s;
int len;
int options = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND;
expand_T xpc;
int error = FALSE;
char_u *result;
#ifdef BACKSLASH_IN_FILENAME
char_u *p_csl_save = p_csl;
#endif
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_opt_bool_arg(argvars, 1) == FAIL
|| (argvars[1].v_type != VAR_UNKNOWN
&& check_for_opt_bool_arg(argvars, 2) == FAIL)))
return;
#ifdef BACKSLASH_IN_FILENAME
// avoid using 'completeslash' here
p_csl = empty_option;
#endif
rettv->v_type = VAR_STRING;
if (argvars[1].v_type != VAR_UNKNOWN
&& argvars[2].v_type != VAR_UNKNOWN
&& tv_get_bool_chk(&argvars[2], &error)
&& !error)
rettv_list_set(rettv, NULL);
s = tv_get_string(&argvars[0]);
if (*s == '%' || *s == '#' || *s == '<')
{
char *errormsg = NULL;
if (p_verbose == 0)
++emsg_off;
result = eval_vars(s, s, &len, NULL, &errormsg, NULL, FALSE);
if (p_verbose == 0)
--emsg_off;
else if (errormsg != NULL)
emsg(errormsg);
if (rettv->v_type == VAR_LIST)
{
if (rettv_list_alloc(rettv) == OK && result != NULL)
list_append_string(rettv->vval.v_list, result, -1);
vim_free(result);
}
else
rettv->vval.v_string = result;
}
else
{
// When the optional second argument is non-zero, don't remove matches
// for 'wildignore' and don't put matches for 'suffixes' at the end.
if (argvars[1].v_type != VAR_UNKNOWN
&& tv_get_bool_chk(&argvars[1], &error))
options |= WILD_KEEP_ALL;
if (!error)
{
ExpandInit(&xpc);
xpc.xp_context = EXPAND_FILES;
if (p_wic)
options += WILD_ICASE;
if (rettv->v_type == VAR_STRING)
rettv->vval.v_string = ExpandOne(&xpc, s, NULL,
options, WILD_ALL);
else if (rettv_list_alloc(rettv) == OK)
{
ExpandOne(&xpc, s, NULL, options, WILD_ALL_KEEP);
for (int i = 0; i < xpc.xp_numfiles; i++)
list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1);
ExpandCleanup(&xpc);
}
}
else
rettv->vval.v_string = NULL;
}
#ifdef BACKSLASH_IN_FILENAME
p_csl = p_csl_save;
#endif
}
/*
* "expandcmd()" function
* Expand all the special characters in a command string.
*/
static void
f_expandcmd(typval_T *argvars, typval_T *rettv)
{
exarg_T eap;
char_u *cmdstr;
char *errormsg = NULL;
int emsgoff = TRUE;
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_opt_dict_arg(argvars, 1) == FAIL))
return;
if (argvars[1].v_type == VAR_DICT
&& dict_get_bool(argvars[1].vval.v_dict, "errmsg", VVAL_FALSE))
emsgoff = FALSE;
rettv->v_type = VAR_STRING;
cmdstr = vim_strsave(tv_get_string(&argvars[0]));
CLEAR_FIELD(eap);
eap.cmd = cmdstr;
eap.arg = cmdstr;
eap.argt |= EX_NOSPC;
eap.usefilter = FALSE;
eap.nextcmd = NULL;
eap.cmdidx = CMD_USER;
if (emsgoff)
++emsg_off;
if (expand_filename(&eap, &cmdstr, &errormsg) == FAIL)
if (!emsgoff && errormsg != NULL && *errormsg != NUL)
emsg(errormsg);
if (emsgoff)
--emsg_off;
rettv->vval.v_string = cmdstr;
}
/*
* "feedkeys()" function
*/
static void
f_feedkeys(typval_T *argvars, typval_T *rettv UNUSED)
{
int remap = TRUE;
int insert = FALSE;
char_u *keys, *flags;
char_u nbuf[NUMBUFLEN];
int typed = FALSE;
int execute = FALSE;
int context = FALSE;
int dangerous = FALSE;
int lowlevel = FALSE;
// This is not allowed in the sandbox. If the commands would still be
// executed in the sandbox it would be OK, but it probably happens later,
// when "sandbox" is no longer set.
if (check_secure())
return;
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_opt_string_arg(argvars, 1) == FAIL))
return;
keys = tv_get_string(&argvars[0]);
if (argvars[1].v_type != VAR_UNKNOWN)
{
flags = tv_get_string_buf(&argvars[1], nbuf);
for ( ; *flags != NUL; ++flags)
{
switch (*flags)
{
case 'n': remap = FALSE; break;
case 'm': remap = TRUE; break;
case 't': typed = TRUE; break;
case 'i': insert = TRUE; break;
case 'x': execute = TRUE; break;
case 'c': context = TRUE; break;
case '!': dangerous = TRUE; break;
case 'L': lowlevel = TRUE; break;
}
}
}
if (*keys != NUL || execute)
{
if (lowlevel
#ifdef FEAT_VTP
&& (!is_term_win32()
|| (keys[0] == 3 && ctrl_c_interrupts && typed))
#endif
)
{
#ifdef USE_INPUT_BUF
ch_log(NULL, "feedkeys() lowlevel: %s", keys);
int len = (int)STRLEN(keys);
for (int idx = 0; idx < len; ++idx)
{
// if a CTRL-C was typed, set got_int, similar to what
// happens in fill_input_buf()
if (keys[idx] == 3 && ctrl_c_interrupts && typed)
got_int = TRUE;
add_to_input_buf(keys + idx, 1);
}
#else
emsg(_(e_lowlevel_input_not_supported));
#endif
}
else
{
// Need to escape K_SPECIAL and CSI before putting the string in
// the typeahead buffer.
char_u *keys_esc = vim_strsave_escape_csi(keys);
if (keys_esc == NULL)
return;
ch_log(NULL, "feedkeys(%s): %s", typed ? "typed" : "", keys);
ins_typebuf(keys_esc, (remap ? REMAP_YES : REMAP_NONE),
insert ? 0 : typebuf.tb_len, !typed, FALSE);
if (vgetc_busy
#ifdef FEAT_TIMERS
|| timer_busy
#endif
|| input_busy)
typebuf_was_filled = TRUE;
vim_free(keys_esc);
}
if (execute)
{
int save_msg_scroll = msg_scroll;
sctx_T save_sctx;
// Avoid a 1 second delay when the keys start Insert mode.
msg_scroll = FALSE;
ch_log(NULL, "feedkeys() executing");
if (context)
{
save_sctx = current_sctx;
current_sctx.sc_sid = 0;
current_sctx.sc_version = 0;
}
if (!dangerous)
{
++ex_normal_busy;
++in_feedkeys;
}
exec_normal(TRUE, lowlevel, TRUE);
if (!dangerous)
{
--ex_normal_busy;
--in_feedkeys;
}
msg_scroll |= save_msg_scroll;
if (context)
current_sctx = save_sctx;
}
}
}
/*
* "fnameescape({string})" function
*/
static void
f_fnameescape(typval_T *argvars, typval_T *rettv)
{
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
rettv->vval.v_string = vim_strsave_fnameescape(
tv_get_string(&argvars[0]), VSE_NONE);
rettv->v_type = VAR_STRING;
}
/*
* "foreground()" function
*/
static void
f_foreground(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
{
#ifdef FEAT_GUI
if (gui.in_use)
{
gui_mch_set_foreground();
return;
}
#endif
#if defined(MSWIN) && (!defined(FEAT_GUI) || defined(VIMDLL))
win32_set_foreground();
#endif
}
/*
* "function()" function
* "funcref()" function
*/
static void
common_function(typval_T *argvars, typval_T *rettv, int is_funcref)
{
char_u *s;
char_u *name;
int use_string = FALSE;
partial_T *arg_pt = NULL;
char_u *trans_name = NULL;
int is_global = FALSE;
if (in_vim9script()
&& (check_for_string_or_func_arg(argvars, 0) == FAIL
|| check_for_opt_list_arg(argvars, 1) == FAIL
|| (argvars[1].v_type != VAR_UNKNOWN
&& check_for_opt_dict_arg(argvars, 2) == FAIL)))
return;
if (argvars[0].v_type == VAR_FUNC)
{
// function(MyFunc, [arg], dict)
s = argvars[0].vval.v_string;
}
else if (argvars[0].v_type == VAR_PARTIAL
&& argvars[0].vval.v_partial != NULL)
{
// function(dict.MyFunc, [arg])
arg_pt = argvars[0].vval.v_partial;
s = partial_name(arg_pt);
}
else
{
// function('MyFunc', [arg], dict)
s = tv_get_string(&argvars[0]);
use_string = TRUE;
}
if (s == NULL)
{
semsg(_(e_invalid_argument_str), "NULL");
return;
}
if ((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL) || is_funcref)
{
name = s;
trans_name = save_function_name(&name, &is_global, FALSE,
TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL);
if (*name != NUL)
s = NULL;
}
if (s == NULL || *s == NUL || (use_string && VIM_ISDIGIT(*s))
|| (is_funcref && trans_name == NULL))
semsg(_(e_invalid_argument_str),
use_string ? tv_get_string(&argvars[0]) : s);
// Don't check an autoload name for existence here.
else if (trans_name != NULL && (is_funcref
? find_func(trans_name, is_global) == NULL
: !translated_function_exists(trans_name, is_global)))
semsg(_(e_unknown_function_str_2), s);
else
{
int dict_idx = 0;
int arg_idx = 0;
list_T *list = NULL;
if (STRNCMP(s, "s:", 2) == 0 || STRNCMP(s, "<SID>", 5) == 0)
// Expand s: and <SID> into <SNR>nr_, so that the function can
// also be called from another script. Using trans_function_name()
// would also work, but some plugins depend on the name being
// printable text.
name = get_scriptlocal_funcname(s);
else if (trans_name != NULL && *trans_name == K_SPECIAL)
name = alloc_printable_func_name(trans_name);
else
name = vim_strsave(s);
if (argvars[1].v_type != VAR_UNKNOWN)
{
if (argvars[2].v_type != VAR_UNKNOWN)
{
// function(name, [args], dict)
arg_idx = 1;
dict_idx = 2;
}
else if (argvars[1].v_type == VAR_DICT)
// function(name, dict)
dict_idx = 1;
else
// function(name, [args])
arg_idx = 1;
if (dict_idx > 0)
{
if (check_for_dict_arg(argvars, dict_idx) == FAIL)
{
vim_free(name);
goto theend;
}
if (argvars[dict_idx].vval.v_dict == NULL)
dict_idx = 0;
}
if (arg_idx > 0)
{
if (argvars[arg_idx].v_type != VAR_LIST)
{
emsg(_(e_second_argument_of_function_must_be_list_or_dict));
vim_free(name);
goto theend;
}
list = argvars[arg_idx].vval.v_list;
if (list == NULL || list->lv_len == 0)
arg_idx = 0;
else if (list->lv_len > MAX_FUNC_ARGS)
{
emsg_funcname(e_too_many_arguments_for_function_str, s);
vim_free(name);
goto theend;
}
}
}
if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL || is_funcref)
{
partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
// result is a VAR_PARTIAL
if (pt == NULL)
vim_free(name);
else
{
if (arg_idx > 0 || (arg_pt != NULL && arg_pt->pt_argc > 0))
{
listitem_T *li;
int i = 0;
int arg_len = 0;
int lv_len = 0;
if (arg_pt != NULL)
arg_len = arg_pt->pt_argc;
if (list != NULL)
lv_len = list->lv_len;
pt->pt_argc = arg_len + lv_len;
pt->pt_argv = ALLOC_MULT(typval_T, pt->pt_argc);
if (pt->pt_argv == NULL)
{
vim_free(pt);
vim_free(name);
goto theend;
}
for (i = 0; i < arg_len; i++)
copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]);
if (lv_len > 0)
{
CHECK_LIST_MATERIALIZE(list);
FOR_ALL_LIST_ITEMS(list, li)
copy_tv(&li->li_tv, &pt->pt_argv[i++]);
}
}
// For "function(dict.func, [], dict)" and "func" is a partial
// use "dict". That is backwards compatible.
if (dict_idx > 0)
{
// The dict is bound explicitly, pt_auto is FALSE.
pt->pt_dict = argvars[dict_idx].vval.v_dict;
++pt->pt_dict->dv_refcount;
}
else if (arg_pt != NULL)
{
// If the dict was bound automatically the result is also
// bound automatically.
pt->pt_dict = arg_pt->pt_dict;
pt->pt_auto = arg_pt->pt_auto;
if (pt->pt_dict != NULL)
++pt->pt_dict->dv_refcount;
pt->pt_obj = arg_pt->pt_obj;
if (pt->pt_obj != NULL)
++pt->pt_obj->obj_refcount;
}
pt->pt_refcount = 1;
if (arg_pt != NULL && arg_pt->pt_func != NULL)
{
pt->pt_func = arg_pt->pt_func;
func_ptr_ref(pt->pt_func);
vim_free(name);
}
else if (is_funcref)
{
pt->pt_func = find_func(trans_name, is_global);
func_ptr_ref(pt->pt_func);
vim_free(name);
}
else
{
pt->pt_name = name;
func_ref(name);
}
if (arg_pt != NULL)
{
pt->pt_outer_partial = arg_pt;
++arg_pt->pt_refcount;
}
}
rettv->v_type = VAR_PARTIAL;
rettv->vval.v_partial = pt;
}
else
{
// result is a VAR_FUNC
rettv->v_type = VAR_FUNC;
rettv->vval.v_string = name;
func_ref(name);
}
}
theend:
vim_free(trans_name);
}
/*
* "funcref()" function
*/
static void
f_funcref(typval_T *argvars, typval_T *rettv)
{
common_function(argvars, rettv, TRUE);
}
/*
* "function()" function
*/
static void
f_function(typval_T *argvars, typval_T *rettv)
{
common_function(argvars, rettv, FALSE);
}
/*
* "garbagecollect()" function
*/
static void
f_garbagecollect(typval_T *argvars, typval_T *rettv UNUSED)
{
if (in_vim9script() && check_for_opt_bool_arg(argvars, 0) == FAIL)
return;
// This is postponed until we are back at the toplevel, because we may be
// using Lists and Dicts internally. E.g.: ":echo [garbagecollect()]".
want_garbage_collect = TRUE;
if (argvars[0].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[0]) == 1)
garbage_collect_at_exit = TRUE;
}
/*
* "get()" function
*/
static void
f_get(typval_T *argvars, typval_T *rettv)
{
listitem_T *li;
list_T *l;
dictitem_T *di;
dict_T *d;
typval_T *tv = NULL;
int what_is_dict = FALSE;
if (argvars[0].v_type == VAR_BLOB)
{
int error = FALSE;
int idx = tv_get_number_chk(&argvars[1], &error);
if (!error)
{
rettv->v_type = VAR_NUMBER;
if (idx < 0)
idx = blob_len(argvars[0].vval.v_blob) + idx;
if (idx < 0 || idx >= blob_len(argvars[0].vval.v_blob))
rettv->vval.v_number = -1;
else
{
rettv->vval.v_number = blob_get(argvars[0].vval.v_blob, idx);
tv = rettv;
}
}
}
else if (argvars[0].v_type == VAR_LIST)
{
if ((l = argvars[0].vval.v_list) != NULL)
{
int error = FALSE;
li = list_find(l, (long)tv_get_number_chk(&argvars[1], &error));
if (!error && li != NULL)
tv = &li->li_tv;
}
}
else if (argvars[0].v_type == VAR_DICT)
{
if ((d = argvars[0].vval.v_dict) != NULL)
{
di = dict_find(d, tv_get_string(&argvars[1]), -1);
if (di != NULL)
tv = &di->di_tv;
}
}
else if (argvars[0].v_type == VAR_PARTIAL || argvars[0].v_type == VAR_FUNC)
{
partial_T *pt;
partial_T fref_pt;
if (argvars[0].v_type == VAR_PARTIAL)
pt = argvars[0].vval.v_partial;
else
{
CLEAR_FIELD(fref_pt);
fref_pt.pt_name = argvars[0].vval.v_string;
pt = &fref_pt;
}
if (pt != NULL)
{
char_u *what = tv_get_string(&argvars[1]);
if (STRCMP(what, "func") == 0 || STRCMP(what, "name") == 0)
{
char_u *name = partial_name(pt);
rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING);
if (name == NULL)
rettv->vval.v_string = NULL;
else
{
if (rettv->v_type == VAR_FUNC)
func_ref(name);
if (*what == 'n' && pt->pt_name == NULL
&& pt->pt_func != NULL)
// use <SNR> instead of the byte code
name = printable_func_name(pt->pt_func);
rettv->vval.v_string = vim_strsave(name);
}
}
else if (STRCMP(what, "dict") == 0)
{
what_is_dict = TRUE;
if (pt->pt_dict != NULL)
rettv_dict_set(rettv, pt->pt_dict);
}
else if (STRCMP(what, "args") == 0)
{
rettv->v_type = VAR_LIST;
if (rettv_list_alloc(rettv) == OK)
{
int i;
for (i = 0; i < pt->pt_argc; ++i)
list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]);
}
}
else
semsg(_(e_invalid_argument_str), what);
// When {what} == "dict" and pt->pt_dict == NULL, evaluate the
// third argument
if (!what_is_dict)
return;
}
}
else
semsg(_(e_argument_of_str_must_be_list_dictionary_or_blob), "get()");
if (tv == NULL)
{
if (argvars[2].v_type != VAR_UNKNOWN)
copy_tv(&argvars[2], rettv);
}
else
copy_tv(tv, rettv);
}
/*
* "getchangelist()" function
*/
static void
f_getchangelist(typval_T *argvars, typval_T *rettv)
{
buf_T *buf;
int i;
list_T *l;
dict_T *d;
int changelistindex;
if (rettv_list_alloc(rettv) == FAIL)
return;
if (in_vim9script() && check_for_opt_buffer_arg(argvars, 0) == FAIL)
return;
if (argvars[0].v_type == VAR_UNKNOWN)
buf = curbuf;
else
buf = tv_get_buf_from_arg(&argvars[0]);
if (buf == NULL)
return;
l = list_alloc();
if (l == NULL)
return;
if (list_append_list(rettv->vval.v_list, l) == FAIL)
{
vim_free(l);
return;
}
/*
* The current window change list index tracks only the position for the
* current buffer. For other buffers use the stored index for the current
* window, or, if that's not available, the change list length.
*/
if (buf == curwin->w_buffer)
{
changelistindex = curwin->w_changelistidx;
}
else
{
wininfo_T *wip;
FOR_ALL_BUF_WININFO(buf, wip)
if (wip->wi_win == curwin)
break;
changelistindex = wip != NULL ? wip->wi_changelistidx
: buf->b_changelistlen;
}
list_append_number(rettv->vval.v_list, (varnumber_T)changelistindex);
for (i = 0; i < buf->b_changelistlen; ++i)
{
if (buf->b_changelist[i].lnum == 0)
continue;
if ((d = dict_alloc()) == NULL)
return;
if (list_append_dict(l, d) == FAIL)
return;
dict_add_number(d, "lnum", (long)buf->b_changelist[i].lnum);
dict_add_number(d, "col", (long)buf->b_changelist[i].col);
dict_add_number(d, "coladd", (long)buf->b_changelist[i].coladd);
}
}
static void
getpos_both(
typval_T *argvars,
typval_T *rettv,
int getcurpos,
int charcol)
{
pos_T *fp = NULL;
pos_T pos;
win_T *wp = curwin;
list_T *l;
int fnum = -1;
if (rettv_list_alloc(rettv) == OK)
{
l = rettv->vval.v_list;
if (getcurpos)
{
if (argvars[0].v_type != VAR_UNKNOWN)
{
wp = find_win_by_nr_or_id(&argvars[0]);
if (wp != NULL)
fp = &wp->w_cursor;
}
else
fp = &curwin->w_cursor;
if (fp != NULL && charcol)
{
pos = *fp;
pos.col =
buf_byteidx_to_charidx(wp->w_buffer, pos.lnum, pos.col);
fp = &pos;
}
}
else
fp = var2fpos(&argvars[0], TRUE, &fnum, charcol);
if (fnum != -1)
list_append_number(l, (varnumber_T)fnum);
else
list_append_number(l, (varnumber_T)0);
list_append_number(l, (fp != NULL) ? (varnumber_T)fp->lnum
: (varnumber_T)0);
list_append_number(l, (fp != NULL)
? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1)
: (varnumber_T)0);
list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd :
(varnumber_T)0);
if (getcurpos)
{
int save_set_curswant = curwin->w_set_curswant;
colnr_T save_curswant = curwin->w_curswant;
colnr_T save_virtcol = curwin->w_virtcol;
if (wp == curwin)
update_curswant();
list_append_number(l, wp == NULL ? 0 : wp->w_curswant == MAXCOL
? (varnumber_T)MAXCOL : (varnumber_T)wp->w_curswant + 1);
// Do not change "curswant", as it is unexpected that a get
// function has a side effect.
if (wp == curwin && save_set_curswant)
{
curwin->w_set_curswant = save_set_curswant;
curwin->w_curswant = save_curswant;
curwin->w_virtcol = save_virtcol;
curwin->w_valid &= ~VALID_VIRTCOL;
}
}
}
else
rettv->vval.v_number = FALSE;
}
/*
* "getcharpos()" function
*/
static void
f_getcharpos(typval_T *argvars UNUSED, typval_T *rettv)
{
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
getpos_both(argvars, rettv, FALSE, TRUE);
}
/*
* "getcharsearch()" function
*/
static void
f_getcharsearch(typval_T *argvars UNUSED, typval_T *rettv)
{
if (rettv_dict_alloc(rettv) != OK)
return;
dict_T *dict = rettv->vval.v_dict;
dict_add_string(dict, "char", last_csearch());
dict_add_number(dict, "forward", last_csearch_forward());
dict_add_number(dict, "until", last_csearch_until());
}
/*
* "getenv()" function
*/
static void
f_getenv(typval_T *argvars, typval_T *rettv)
{
int mustfree = FALSE;
char_u *p;
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
p = vim_getenv(tv_get_string(&argvars[0]), &mustfree);
if (p == NULL)
{
rettv->v_type = VAR_SPECIAL;
rettv->vval.v_number = VVAL_NULL;
return;
}
if (!mustfree)
p = vim_strsave(p);
rettv->vval.v_string = p;
rettv->v_type = VAR_STRING;
}
/*
* "getfontname()" function
*/
static void
f_getfontname(typval_T *argvars UNUSED, typval_T *rettv)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
if (in_vim9script() && check_for_opt_string_arg(argvars, 0) == FAIL)
return;
#ifdef FEAT_GUI
if (gui.in_use)
{
GuiFont font;
char_u *name = NULL;
if (argvars[0].v_type == VAR_UNKNOWN)
{
// Get the "Normal" font. Either the name saved by
// hl_set_font_name() or from the font ID.
font = gui.norm_font;
name = hl_get_font_name();
}
else
{
name = tv_get_string(&argvars[0]);
if (STRCMP(name, "*") == 0) // don't use font dialog
return;
font = gui_mch_get_font(name, FALSE);
if (font == NOFONT)
return; // Invalid font name, return empty string.
}
rettv->vval.v_string = gui_mch_get_fontname(font, name);
if (argvars[0].v_type != VAR_UNKNOWN)
gui_mch_free_font(font);
}
#endif
}
/*
* "getjumplist()" function
*/
static void
f_getjumplist(typval_T *argvars, typval_T *rettv)
{
win_T *wp;
int i;
list_T *l;
dict_T *d;
if (rettv_list_alloc(rettv) == FAIL)
return;
if (in_vim9script()
&& (check_for_opt_number_arg(argvars, 0) == FAIL
|| (argvars[0].v_type != VAR_UNKNOWN
&& check_for_opt_number_arg(argvars, 1) == FAIL)))
return;
wp = find_tabwin(&argvars[0], &argvars[1], NULL);
if (wp == NULL)
return;
cleanup_jumplist(wp, TRUE);
l = list_alloc();
if (l == NULL)
return;
if (list_append_list(rettv->vval.v_list, l) == FAIL)
{
vim_free(l);
return;
}
list_append_number(rettv->vval.v_list, (varnumber_T)wp->w_jumplistidx);
for (i = 0; i < wp->w_jumplistlen; ++i)
{
if (wp->w_jumplist[i].fmark.mark.lnum == 0)
continue;
if ((d = dict_alloc()) == NULL)
return;
if (list_append_dict(l, d) == FAIL)
return;
dict_add_number(d, "lnum", (long)wp->w_jumplist[i].fmark.mark.lnum);
dict_add_number(d, "col", (long)wp->w_jumplist[i].fmark.mark.col);
dict_add_number(d, "coladd", (long)wp->w_jumplist[i].fmark.mark.coladd);
dict_add_number(d, "bufnr", (long)wp->w_jumplist[i].fmark.fnum);
if (wp->w_jumplist[i].fname != NULL)
dict_add_string(d, "filename", wp->w_jumplist[i].fname);
}
}
/*
* "getpid()" function
*/
static void
f_getpid(typval_T *argvars UNUSED, typval_T *rettv)
{
rettv->vval.v_number = mch_get_pid();
}
/*
* "getcurpos()" function
*/
static void
f_getcurpos(typval_T *argvars, typval_T *rettv)
{
if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
return;
getpos_both(argvars, rettv, TRUE, FALSE);
}
static void
f_getcursorcharpos(typval_T *argvars, typval_T *rettv)
{
if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
return;
getpos_both(argvars, rettv, TRUE, TRUE);
}
/*
* "getpos(string)" function
*/
static void
f_getpos(typval_T *argvars, typval_T *rettv)
{
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
getpos_both(argvars, rettv, FALSE, FALSE);
}
/*
* Convert from block_def to string
*/
static char_u *
block_def2str(struct block_def *bd)
{
char_u *p, *ret;
size_t size = bd->startspaces + bd->endspaces + bd->textlen;
ret = alloc(size + 1);
if (ret != NULL)
{
p = ret;
vim_memset(p, ' ', bd->startspaces);
p += bd->startspaces;
mch_memmove(p, bd->textstart, bd->textlen);
p += bd->textlen;
vim_memset(p, ' ', bd->endspaces);
*(p + bd->endspaces) = NUL;
}
return ret;
}
/*
* "getregion()" function
*/
static void
f_getregion(typval_T *argvars, typval_T *rettv)
{
linenr_T lnum;
oparg_T oa;
struct block_def bd;
char_u *akt = NULL;
int inclusive = TRUE;
int fnum = -1;
pos_T p1, p2;
char_u *type;
char_u default_type[] = "v";
int save_virtual = -1;
int l;
int region_type = -1;
int is_select_exclusive;
if (rettv_list_alloc(rettv) == FAIL)
return;
if (check_for_list_arg(argvars, 0) == FAIL
|| check_for_list_arg(argvars, 1) == FAIL
|| check_for_opt_dict_arg(argvars, 2) == FAIL)
return;
if (list2fpos(&argvars[0], &p1, &fnum, NULL, FALSE) != OK
|| (fnum >= 0 && fnum != curbuf->b_fnum))
return;
if (list2fpos(&argvars[1], &p2, &fnum, NULL, FALSE) != OK
|| (fnum >= 0 && fnum != curbuf->b_fnum))
return;
if (argvars[2].v_type == VAR_DICT)
{
is_select_exclusive = dict_get_bool(
argvars[2].vval.v_dict, "exclusive", *p_sel == 'e');
type = dict_get_string(
argvars[2].vval.v_dict, "type", FALSE);
if (type == NULL)
type = default_type;
}
else
{
is_select_exclusive = *p_sel == 'e';
type = default_type;
}
if (type[0] == 'v' && type[1] == NUL)
region_type = MCHAR;
else if (type[0] == 'V' && type[1] == NUL)
region_type = MLINE;
else if (type[0] == Ctrl_V && type[1] == NUL)
region_type = MBLOCK;
else
return;
save_virtual = virtual_op;
virtual_op = virtual_active();
// NOTE: Adjust is needed.
p1.col--;
p2.col--;
if (!LT_POS(p1, p2))
{
// swap position
pos_T p;
p = p1;
p1 = p2;
p2 = p;
}
if (region_type == MCHAR)
{
// handle 'selection' == "exclusive"
if (is_select_exclusive && !EQUAL_POS(p1, p2))
{
if (p2.coladd > 0)
p2.coladd--;
else if (p2.col > 0)
{
p2.col--;
mb_adjustpos(curbuf, &p2);
}
else if (p2.lnum > 1)
{
p2.lnum--;
p2.col = (colnr_T)STRLEN(ml_get(p2.lnum));
if (p2.col > 0)
{
p2.col--;
mb_adjustpos(curbuf, &p2);
}
}
}
// if fp2 is on NUL (empty line) inclusive becomes false
if (*ml_get_pos(&p2) == NUL && !virtual_op)
inclusive = FALSE;
}
else if (region_type == MBLOCK)
{
colnr_T sc1, ec1, sc2, ec2;
getvvcol(curwin, &p1, &sc1, NULL, &ec1);
getvvcol(curwin, &p2, &sc2, NULL, &ec2);
oa.motion_type = MBLOCK;
oa.inclusive = TRUE;
oa.op_type = OP_NOP;
oa.start = p1;
oa.end = p2;
oa.start_vcol = MIN(sc1, sc2);
if (is_select_exclusive && ec1 < sc2 && 0 < sc2 && ec2 > ec1)
oa.end_vcol = sc2 - 1;
else
oa.end_vcol = MAX(ec1, ec2);
}
// Include the trailing byte of a multi-byte char.
l = utfc_ptr2len((char_u *)ml_get_pos(&p2));
if (l > 1)
p2.col += l - 1;
for (lnum = p1.lnum; lnum <= p2.lnum; lnum++)
{
int ret = 0;
if (region_type == MLINE)
akt = vim_strsave(ml_get(lnum));
else if (region_type == MBLOCK)
{
block_prep(&oa, &bd, lnum, FALSE);
akt = block_def2str(&bd);
}
else if (p1.lnum < lnum && lnum < p2.lnum)
akt = vim_strsave(ml_get(lnum));
else
{
charwise_block_prep(p1, p2, &bd, lnum, inclusive);
akt = block_def2str(&bd);
}
if (akt)
{
ret = list_append_string(rettv->vval.v_list, akt, -1);
vim_free(akt);
}
if (akt == NULL || ret == FAIL)
{
clear_tv(rettv);
(void)rettv_list_alloc(rettv);
break;
}
}
virtual_op = save_virtual;
}
/*
* Common between getreg(), getreginfo() and getregtype(): get the register
* name from the first argument.
* Returns zero on error.
*/
static int
getreg_get_regname(typval_T *argvars)
{
char_u *strregname;
if (argvars[0].v_type != VAR_UNKNOWN)
{
strregname = tv_get_string_chk(&argvars[0]);
if (strregname != NULL && in_vim9script() && STRLEN(strregname) > 1)
{
semsg(_(e_register_name_must_be_one_char_str), strregname);
strregname = NULL;
}
if (strregname == NULL) // type error; errmsg already given
return 0;
}
else
// Default to v:register
strregname = get_vim_var_str(VV_REG);
return *strregname == 0 ? '"' : *strregname;
}
/*
* "getreg()" function
*/
static void
f_getreg(typval_T *argvars, typval_T *rettv)
{
int regname;
int arg2 = FALSE;
int return_list = FALSE;
if (in_vim9script()
&& (check_for_opt_string_arg(argvars, 0) == FAIL
|| (argvars[0].v_type != VAR_UNKNOWN
&& (check_for_opt_bool_arg(argvars, 1) == FAIL
|| (argvars[1].v_type != VAR_UNKNOWN
&& check_for_opt_bool_arg(argvars, 2) == FAIL)))))
return;
regname = getreg_get_regname(argvars);
if (regname == 0)
return;
if (argvars[0].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_UNKNOWN)
{
int error = FALSE;
arg2 = (int)tv_get_bool_chk(&argvars[1], &error);
if (!error && argvars[2].v_type != VAR_UNKNOWN)
return_list = (int)tv_get_bool_chk(&argvars[2], &error);
if (error)
return;
}
if (return_list)
{
rettv->v_type = VAR_LIST;
rettv->vval.v_list = (list_T *)get_reg_contents(regname,
(arg2 ? GREG_EXPR_SRC : 0) | GREG_LIST);
if (rettv->vval.v_list == NULL)
(void)rettv_list_alloc(rettv);
else
++rettv->vval.v_list->lv_refcount;
}
else
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = get_reg_contents(regname,
arg2 ? GREG_EXPR_SRC : 0);
}
}
/*
* "getregtype()" function
*/
static void
f_getregtype(typval_T *argvars, typval_T *rettv)
{
int regname;
char_u buf[NUMBUFLEN + 2];
long reglen = 0;
// on error return an empty string
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
if (in_vim9script() && check_for_opt_string_arg(argvars, 0) == FAIL)
return;
regname = getreg_get_regname(argvars);
if (regname == 0)
return;
buf[0] = NUL;
buf[1] = NUL;
switch (get_reg_type(regname, &reglen))
{
case MLINE: buf[0] = 'V'; break;
case MCHAR: buf[0] = 'v'; break;
case MBLOCK:
buf[0] = Ctrl_V;
sprintf((char *)buf + 1, "%ld", reglen + 1);
break;
}
rettv->vval.v_string = vim_strsave(buf);
}
/*
* "gettagstack()" function
*/
static void
f_gettagstack(typval_T *argvars, typval_T *rettv)
{
win_T *wp = curwin; // default is current window
if (rettv_dict_alloc(rettv) == FAIL)
return;
if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
return;
if (argvars[0].v_type != VAR_UNKNOWN)
{
wp = find_win_by_nr_or_id(&argvars[0]);
if (wp == NULL)
return;
}
get_tagstack(wp, rettv->vval.v_dict);
}
/*
* "gettext()" function
*/
static void
f_gettext(typval_T *argvars, typval_T *rettv)
{
if (check_for_nonempty_string_arg(argvars, 0) == FAIL)
return;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = vim_strsave((char_u *)_(argvars[0].vval.v_string));
}
// for VIM_VERSION_ defines
#include "version.h"
/*
* "has()" function
*/
void
f_has(typval_T *argvars, typval_T *rettv)
{
int i;
char_u *name;
int x = FALSE;
int n = FALSE;
typedef struct {
char *name;
short present;
} has_item_T;
static has_item_T has_list[] =
{
{"amiga",
#ifdef AMIGA
1
#else
0
#endif
},
{"arp",
#if defined(AMIGA) && defined(FEAT_ARP)
1
#else
0
#endif
},
{"haiku",
#ifdef __HAIKU__
1
#else
0
#endif
},
{"bsd",
#if defined(BSD) && !defined(MACOS_X)
1
#else
0
#endif
},
{"hpux",
#ifdef hpux
1
#else
0
#endif
},
{"linux",
#ifdef __linux__
1
#else
0
#endif
},
{"mac", // Mac OS X (and, once, Mac OS Classic)
#ifdef MACOS_X
1
#else
0
#endif
},
{"osx", // Mac OS X
#ifdef MACOS_X
1
#else
0
#endif
},
{"macunix", // Mac OS X, with the darwin feature
#if defined(MACOS_X) && defined(MACOS_X_DARWIN)
1
#else
0
#endif
},
{"osxdarwin", // synonym for macunix
#if defined(MACOS_X) && defined(MACOS_X_DARWIN)
1
#else
0
#endif
},
{"qnx",
#ifdef __QNX__
1
#else
0
#endif
},
{"sun",
#ifdef SUN_SYSTEM
1
#else
0
#endif
},
{"unix",
#ifdef UNIX
1
#else
0
#endif
},
{"vms",
#ifdef VMS
1
#else
0
#endif
},
{"win32",
#ifdef MSWIN
1
#else
0
#endif
},
{"win32unix",
#ifdef WIN32UNIX
1
#else
0
#endif
},
{"win64",
#ifdef _WIN64
1
#else
0
#endif
},
{"ebcdic", 0 },
{"fname_case",
#ifndef CASE_INSENSITIVE_FILENAME
1
#else
0
#endif
},
{"acl",
#ifdef HAVE_ACL
1
#else
0
#endif
},
{"arabic",
#ifdef FEAT_ARABIC
1
#else
0
#endif
},
{"autocmd", 1},
{"autochdir",
#ifdef FEAT_AUTOCHDIR
1
#else
0
#endif
},
{"autoservername",
#ifdef FEAT_AUTOSERVERNAME
1
#else
0
#endif
},
{"balloon_eval",
#ifdef FEAT_BEVAL_GUI
1
#else
0
#endif
},
{"balloon_multiline",
#ifdef FEAT_BEVAL_GUI
1
#else
0
#endif
},
{"balloon_eval_term",
#ifdef FEAT_BEVAL_TERM
1
#else
0
#endif
},
{"builtin_terms", 1},
{"all_builtin_terms", 1},
{"browsefilter",
#if defined(FEAT_BROWSE) && (defined(USE_FILE_CHOOSER) \
|| defined(FEAT_GUI_MSWIN) \
|| defined(FEAT_GUI_MOTIF))
1
#else
0
#endif
},
{"byte_offset",
#ifdef FEAT_BYTEOFF
1
#else
0
#endif
},
{"channel",
#ifdef FEAT_JOB_CHANNEL
1
#else
0
#endif
},
{"cindent", 1},
{"clientserver",
#ifdef FEAT_CLIENTSERVER
1
#else
0
#endif
},
{"clipboard",
#ifdef FEAT_CLIPBOARD
1
#else
0
#endif
},
{"cmdline_compl", 1},
{"cmdline_hist", 1},
{"cmdwin", 1},
{"comments", 1},
{"conceal",
#ifdef FEAT_CONCEAL
1
#else
0
#endif
},
{"cryptv",
#ifdef FEAT_CRYPT
1
#else
0
#endif
},
{"crypt-blowfish",
#ifdef FEAT_CRYPT
1
#else
0
#endif
},
{"crypt-blowfish2",
#ifdef FEAT_CRYPT
1
#else
0
#endif
},
{"cscope",
#ifdef FEAT_CSCOPE
1
#else
0
#endif
},
{"cursorbind", 1},
{"cursorshape",
#ifdef CURSOR_SHAPE
1
#else
0
#endif
},
{"debug",
#ifdef DEBUG
1
#else
0
#endif
},
{"dialog_con",
#ifdef FEAT_CON_DIALOG
1
#else
0
#endif
},
{"dialog_gui",
#ifdef FEAT_GUI_DIALOG
1
#else
0
#endif
},
{"diff",
#ifdef FEAT_DIFF
1
#else
0
#endif
},
{"digraphs",
#ifdef FEAT_DIGRAPHS
1
#else
0
#endif
},
{"directx",
#ifdef FEAT_DIRECTX
1
#else
0
#endif
},
{"dnd",
#ifdef FEAT_DND
1
#else
0
#endif
},
{"drop_file",
#ifdef HAVE_DROP_FILE
1
#else
0
#endif
},
{"emacs_tags",
#ifdef FEAT_EMACS_TAGS
1
#else
0
#endif
},
{"eval", 1}, // always present, of course!
{"ex_extra", 1}, // graduated feature
{"extra_search",
#ifdef FEAT_SEARCH_EXTRA
1
#else
0
#endif
},
{"file_in_path", 1},
{"filterpipe",
#if defined(FEAT_FILTERPIPE) && !defined(VIMDLL)
1
#else
0
#endif
},
{"find_in_path",
#ifdef FEAT_FIND_ID
1
#else
0
#endif
},
{"float", 1},
{"folding",
#ifdef FEAT_FOLDING
1
#else
0
#endif
},
{"footer", 0},
{"fork",
#if !defined(USE_SYSTEM) && defined(UNIX)
1
#else
0
#endif
},
{"gettext",
#ifdef FEAT_GETTEXT
1
#else
0
#endif
},
{"gui",
#ifdef FEAT_GUI
1
#else
0
#endif
},
{"gui_neXtaw", 0 },
{"gui_athena", 0 },
{"gui_gtk",
#ifdef FEAT_GUI_GTK
1
#else
0
#endif
},
{"gui_gtk2",
#if defined(FEAT_GUI_GTK) && !defined(USE_GTK3)
1
#else
0
#endif
},
{"gui_gtk3",
#if defined(FEAT_GUI_GTK) && defined(USE_GTK3)
1
#else
0
#endif
},
{"gui_gnome",
#ifdef FEAT_GUI_GNOME
1
#else
0
#endif
},
{"gui_haiku",
#ifdef FEAT_GUI_HAIKU
1
#else
0
#endif
},
{"gui_mac", 0},
{"gui_motif",
#ifdef FEAT_GUI_MOTIF
1
#else
0
#endif
},
{"gui_photon",
#ifdef FEAT_GUI_PHOTON
1
#else
0
#endif
},
{"gui_win32",
#ifdef FEAT_GUI_MSWIN
1
#else
0
#endif
},
{"iconv",
#if defined(HAVE_ICONV_H) && defined(USE_ICONV)
1
#else
0
#endif
},
{"insert_expand", 1},
{"ipv6",
#ifdef FEAT_IPV6
1
#else
0
#endif
},
{"job",
#ifdef FEAT_JOB_CHANNEL
1
#else
0
#endif
},
{"jumplist", 1},
{"keymap",
#ifdef FEAT_KEYMAP
1
#else
0
#endif
},
{"lambda", 1}, // always with FEAT_EVAL, since 7.4.2120 with closure
{"langmap",
#ifdef FEAT_LANGMAP
1
#else
0
#endif
},
{"libcall",
#ifdef FEAT_LIBCALL
1
#else
0
#endif
},
{"linebreak",
#ifdef FEAT_LINEBREAK
1
#else
0
#endif
},
{"lispindent", 1},
{"listcmds", 1},
{"localmap", 1},
{"lua",
#if defined(FEAT_LUA) && !defined(DYNAMIC_LUA)
1
#else
0
#endif
},
{"menu",
#ifdef FEAT_MENU
1
#else
0
#endif
},
{"mksession",
#ifdef FEAT_SESSION
1
#else
0
#endif
},
{"modify_fname", 1},
{"mouse", 1},
{"mouseshape",
#ifdef FEAT_MOUSESHAPE
1
#else
0
#endif
},
{"mouse_dec",
#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_DEC)
1
#else
0
#endif
},
{"mouse_gpm",
#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_GPM) && !defined(DYNAMIC_GPM)
1
#else
0
#endif
},
{"mouse_jsbterm",
#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_JSB)
1
#else
0
#endif
},
{"mouse_netterm",
#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_NET)
1
#else
0
#endif
},
{"mouse_pterm",
#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_PTERM)
1
#else
0
#endif
},
{"mouse_sgr",
#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_XTERM)
1
#else
0
#endif
},
{"mouse_sysmouse",
#if (defined(UNIX) || defined(VMS)) && defined(FEAT_SYSMOUSE)
1
#else
0
#endif
},
{"mouse_urxvt",
#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_URXVT)
1
#else
0
#endif
},
{"mouse_xterm",
#if (defined(UNIX) || defined(VMS)) && defined(FEAT_MOUSE_XTERM)
1
#else
0
#endif
},
{"multi_byte", 1},
{"multi_byte_ime",
#ifdef FEAT_MBYTE_IME
1
#else
0
#endif
},
{"multi_lang",
#ifdef FEAT_MULTI_LANG
1
#else
0
#endif
},
{"mzscheme",
#if defined(FEAT_MZSCHEME) && !defined(DYNAMIC_MZSCHEME)
1
#else
0
#endif
},
{"nanotime",
#ifdef ST_MTIM_NSEC
1
#else
0
#endif
},
{"num64", 1},
{"ole",
#ifdef FEAT_OLE
1
#else
0
#endif
},
{"packages",
#ifdef FEAT_EVAL
1
#else
0
#endif
},
{"path_extra", 1},
{"perl",
#if defined(FEAT_PERL) && !defined(DYNAMIC_PERL)
1
#else
0
#endif
},
{"persistent_undo",
#ifdef FEAT_PERSISTENT_UNDO
1
#else
0
#endif
},
{"python_compiled",
#if defined(FEAT_PYTHON)
1
#else
0
#endif
},
{"python_dynamic",
#if defined(FEAT_PYTHON) && defined(DYNAMIC_PYTHON)
1
#else
0
#endif
},
{"python",
#if defined(FEAT_PYTHON) && !defined(DYNAMIC_PYTHON)
1
#else
0
#endif
},
{"pythonx",
#if (defined(FEAT_PYTHON) && !defined(DYNAMIC_PYTHON)) \
|| (defined(FEAT_PYTHON3) && !defined(DYNAMIC_PYTHON3))
1
#else
0
#endif
},
{"python3_compiled",
#if defined(FEAT_PYTHON3)
1
#else
0
#endif
},
{"python3_dynamic",
#if defined(FEAT_PYTHON3) && defined(DYNAMIC_PYTHON3)
1
#else
0
#endif
},
{"python3_stable",
#if defined(FEAT_PYTHON3) && defined(DYNAMIC_PYTHON3_STABLE_ABI)
1
#else
0
#endif
},
{"python3",
#if defined(FEAT_PYTHON3) && !defined(DYNAMIC_PYTHON3)
1
#else
0
#endif
},
{"popupwin",
#ifdef FEAT_PROP_POPUP
1
#else
0
#endif
},
{"postscript",
#ifdef FEAT_POSTSCRIPT
1
#else
0
#endif
},
{"printer",
#ifdef FEAT_PRINTER
1
#else
0
#endif
},
{"profile",
#ifdef FEAT_PROFILE
1
#else
0
#endif
},
{"prof_nsec",
#ifdef PROF_NSEC
1
#else
0
#endif
},
{"reltime",
#ifdef FEAT_RELTIME
1
#else
0
#endif
},
{"quickfix",
#ifdef FEAT_QUICKFIX
1
#else
0
#endif
},
{"rightleft",
#ifdef FEAT_RIGHTLEFT
1
#else
0
#endif
},
{"ruby",
#if defined(FEAT_RUBY) && !defined(DYNAMIC_RUBY)
1
#else
0
#endif
},
{"scrollbind", 1},
{"showcmd", 1},
{"cmdline_info", 1},
{"signs",
#ifdef FEAT_SIGNS
1
#else
0
#endif
},
{"smartindent", 1},
{"startuptime",
#ifdef STARTUPTIME
1
#else
0
#endif
},
{"statusline",
#ifdef FEAT_STL_OPT
1
#else
0
#endif
},
{"netbeans_intg",
#ifdef FEAT_NETBEANS_INTG
1
#else
0
#endif
},
{"sodium",
#if defined(FEAT_SODIUM) && !defined(DYNAMIC_SODIUM)
1
#else
0
#endif
},
{"sound",
#ifdef FEAT_SOUND
1
#else
0
#endif
},
{"spell",
#ifdef FEAT_SPELL
1
#else
0
#endif
},
{"syntax",
#ifdef FEAT_SYN_HL
1
#else
0
#endif
},
{"system",
#if defined(USE_SYSTEM) || !defined(UNIX)
1
#else
0
#endif
},
{"tag_binary", 1}, // graduated feature
{"tcl",
#if defined(FEAT_TCL) && !defined(DYNAMIC_TCL)
1
#else
0
#endif
},
{"termguicolors",
#ifdef FEAT_TERMGUICOLORS
1
#else
0
#endif
},
{"terminal",
#if defined(FEAT_TERMINAL) && !defined(MSWIN)
1
#else
0
#endif
},
{"terminfo",
#ifdef TERMINFO
1
#else
0
#endif
},
{"termresponse",
#ifdef FEAT_TERMRESPONSE
1
#else
0
#endif
},
{"textobjects", 1},
{"textprop",
#ifdef FEAT_PROP_POPUP
1
#else
0
#endif
},
{"tgetent",
#ifdef HAVE_TGETENT
1
#else
0
#endif
},
{"timers",
#ifdef FEAT_TIMERS
1
#else
0
#endif
},
{"title", 1},
{"toolbar",
#ifdef FEAT_TOOLBAR
1
#else
0
#endif
},
{"unnamedplus",
#if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
1
#else
0
#endif
},
{"user-commands", 1}, // was accidentally included in 5.4
{"user_commands", 1},
{"vartabs",
#ifdef FEAT_VARTABS
1
#else
0
#endif
},
{"vertsplit", 1},
{"viminfo",
#ifdef FEAT_VIMINFO
1
#else
0
#endif
},
{"vim9script", 1},
{"vimscript-1", 1},
{"vimscript-2", 1},
{"vimscript-3", 1},
{"vimscript-4", 1},
{"virtualedit", 1},
{"visual", 1},
{"visualextra", 1},
{"vreplace", 1},
{"vtp",
#ifdef FEAT_VTP
1
#else
0
#endif
},
{"wildignore", 1},
{"wildmenu", 1},
{"windows", 1},
{"winaltkeys",
#ifdef FEAT_WAK
1
#else
0
#endif
},
{"writebackup",
#ifdef FEAT_WRITEBACKUP
1
#else
0
#endif
},
{"xattr",
#ifdef FEAT_XATTR
1
#else
0
#endif
},
{"xim",
#ifdef FEAT_XIM
1
#else
0
#endif
},
{"xfontset",
#ifdef FEAT_XFONTSET
1
#else
0
#endif
},
{"xpm",
#if defined(FEAT_XPM_W32) || defined(HAVE_XPM)
1
#else
0
#endif
},
{"xpm_w32", // for backward compatibility
#ifdef FEAT_XPM_W32
1
#else
0
#endif
},
{"xsmp",
#ifdef USE_XSMP
1
#else
0
#endif
},
{"xsmp_interact",
#ifdef USE_XSMP_INTERACT
1
#else
0
#endif
},
{"xterm_clipboard",
#ifdef FEAT_XCLIPBOARD
1
#else
0
#endif
},
{"xterm_save",
#ifdef FEAT_XTERM_SAVE
1
#else
0
#endif
},
{"X11",
#if defined(UNIX) && defined(FEAT_X11)
1
#else
0
#endif
},
{":tearoff",
// same #ifdef as used for ex_tearoff().
#if defined(FEAT_GUI_MSWIN) && defined(FEAT_MENU) && defined(FEAT_TEAROFF)
1
#else
0
#endif
},
{NULL, 0}
};
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_opt_bool_arg(argvars, 1) == FAIL))
return;
name = tv_get_string(&argvars[0]);
for (i = 0; has_list[i].name != NULL; ++i)
if (STRICMP(name, has_list[i].name) == 0)
{
x = TRUE;
n = has_list[i].present;
break;
}
// features also in has_list[] but sometimes enabled at runtime
if (x == TRUE && n == FALSE)
{
if (0)
{
// intentionally empty
}
#ifdef VIMDLL
else if (STRICMP(name, "filterpipe") == 0)
n = gui.in_use || gui.starting;
#endif
#if defined(USE_ICONV) && defined(DYNAMIC_ICONV)
else if (STRICMP(name, "iconv") == 0)
n = iconv_enabled(FALSE);
#endif
#ifdef DYNAMIC_LUA
else if (STRICMP(name, "lua") == 0)
n = lua_enabled(FALSE);
#endif
#ifdef DYNAMIC_MZSCHEME
else if (STRICMP(name, "mzscheme") == 0)
n = mzscheme_enabled(FALSE);
#endif
#ifdef DYNAMIC_PERL
else if (STRICMP(name, "perl") == 0)
n = perl_enabled(FALSE);
#endif
#ifdef DYNAMIC_PYTHON
else if (STRICMP(name, "python") == 0)
n = python_enabled(FALSE);
#endif
#ifdef DYNAMIC_PYTHON3
else if (STRICMP(name, "python3") == 0)
n = python3_enabled(FALSE);
#endif
#if defined(DYNAMIC_PYTHON) || defined(DYNAMIC_PYTHON3)
else if (STRICMP(name, "pythonx") == 0)
{
# if defined(DYNAMIC_PYTHON) && defined(DYNAMIC_PYTHON3)
if (p_pyx == 0)
n = python3_enabled(FALSE) || python_enabled(FALSE);
else if (p_pyx == 3)
n = python3_enabled(FALSE);
else if (p_pyx == 2)
n = python_enabled(FALSE);
# elif defined(DYNAMIC_PYTHON)
n = python_enabled(FALSE);
# elif defined(DYNAMIC_PYTHON3)
n = python3_enabled(FALSE);
# endif
}
#endif
#ifdef DYNAMIC_RUBY
else if (STRICMP(name, "ruby") == 0)
n = ruby_enabled(FALSE);
#endif
#ifdef DYNAMIC_TCL
else if (STRICMP(name, "tcl") == 0)
n = tcl_enabled(FALSE);
#endif
#ifdef DYNAMIC_SODIUM
else if (STRICMP(name, "sodium") == 0)
n = sodium_enabled(FALSE);
#endif
#if defined(FEAT_TERMINAL) && defined(MSWIN)
else if (STRICMP(name, "terminal") == 0)
n = terminal_enabled();
#endif
#ifdef DYNAMIC_GPM
else if (STRICMP(name, "mouse_gpm") == 0)
n = gpm_available();
#endif
}
// features not in has_list[]
if (x == FALSE)
{
if (STRNICMP(name, "patch", 5) == 0)
{
x = TRUE;
if (name[5] == '-'
&& STRLEN(name) >= 11
&& (name[6] >= '1' && name[6] <= '9'))
{
char *end;
int major, minor;
// This works for patch-8.1.2, patch-9.0.3, patch-10.0.4, etc.
// Not for patch-9.10.5.
major = (int)strtoul((char *)name + 6, &end, 10);
if (*end == '.' && vim_isdigit(end[1])
&& end[2] == '.' && vim_isdigit(end[3]))
{
minor = atoi(end + 1);
// Expect "patch-9.9.01234".
n = (major < VIM_VERSION_MAJOR
|| (major == VIM_VERSION_MAJOR
&& (minor < VIM_VERSION_MINOR
|| (minor == VIM_VERSION_MINOR
&& has_patch(atoi(end + 3))))));
}
}
else if (SAFE_isdigit(name[5]))
n = has_patch(atoi((char *)name + 5));
}
else if (STRICMP(name, "vim_starting") == 0)
{
x = TRUE;
n = (starting != 0);
}
else if (STRICMP(name, "ttyin") == 0)
{
x = TRUE;
n = mch_input_isatty();
}
else if (STRICMP(name, "ttyout") == 0)
{
x = TRUE;
n = stdout_isatty;
}
else if (STRICMP(name, "multi_byte_encoding") == 0)
{
x = TRUE;
n = has_mbyte;
}
else if (STRICMP(name, "gui_running") == 0)
{
x = TRUE;
#ifdef FEAT_GUI
n = (gui.in_use || gui.starting);
#endif
}
else if (STRICMP(name, "browse") == 0)
{
x = TRUE;
#if defined(FEAT_GUI) && defined(FEAT_BROWSE)
n = gui.in_use; // gui_mch_browse() works when GUI is running
#endif
}
else if (STRICMP(name, "syntax_items") == 0)
{
x = TRUE;
#ifdef FEAT_SYN_HL
n = syntax_present(curwin);
#endif
}
else if (STRICMP(name, "vcon") == 0)
{
x = TRUE;
#ifdef FEAT_VTP
n = is_term_win32() && has_vtp_working();
#endif
}
else if (STRICMP(name, "netbeans_enabled") == 0)
{
x = TRUE;
#ifdef FEAT_NETBEANS_INTG
n = netbeans_active();
#endif
}
else if (STRICMP(name, "mouse_gpm_enabled") == 0)
{
x = TRUE;
#ifdef FEAT_MOUSE_GPM
n = gpm_enabled();
#endif
}
else if (STRICMP(name, "conpty") == 0)
{
x = TRUE;
#if defined(FEAT_TERMINAL) && defined(MSWIN)
n = use_conpty();
#endif
}
else if (STRICMP(name, "clipboard_working") == 0)
{
x = TRUE;
#ifdef FEAT_CLIPBOARD
n = clip_star.available;
#endif
}
}
if (argvars[1].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[1]))
// return whether feature could ever be enabled
rettv->vval.v_number = x;
else
// return whether feature is enabled
rettv->vval.v_number = n;
}
/*
* Return TRUE if "feature" can change later.
* Also when checking for the feature has side effects, such as loading a DLL.
*/
int
dynamic_feature(char_u *feature)
{
return (feature == NULL
#if defined(FEAT_GUI) && defined(FEAT_BROWSE)
|| (STRICMP(feature, "browse") == 0 && !gui.in_use)
#endif
#ifdef VIMDLL
|| STRICMP(feature, "filterpipe") == 0
#endif
#if defined(FEAT_GUI) && !defined(ALWAYS_USE_GUI) && !defined(VIMDLL)
// this can only change on Unix where the ":gui" command could be
// used.
|| (STRICMP(feature, "gui_running") == 0 && !gui.in_use)
#endif
#if defined(USE_ICONV) && defined(DYNAMIC_ICONV)
|| STRICMP(feature, "iconv") == 0
#endif
#ifdef DYNAMIC_LUA
|| STRICMP(feature, "lua") == 0
#endif
#ifdef FEAT_MOUSE_GPM
|| (STRICMP(feature, "mouse_gpm_enabled") == 0 && !gpm_enabled())
#endif
#ifdef DYNAMIC_MZSCHEME
|| STRICMP(feature, "mzscheme") == 0
#endif
#ifdef FEAT_NETBEANS_INTG
|| STRICMP(feature, "netbeans_enabled") == 0
#endif
#ifdef DYNAMIC_PERL
|| STRICMP(feature, "perl") == 0
#endif
#ifdef DYNAMIC_PYTHON
|| STRICMP(feature, "python") == 0
#endif
#ifdef DYNAMIC_PYTHON3
|| STRICMP(feature, "python3") == 0
#endif
#if defined(DYNAMIC_PYTHON) || defined(DYNAMIC_PYTHON3)
|| STRICMP(feature, "pythonx") == 0
#endif
#ifdef DYNAMIC_RUBY
|| STRICMP(feature, "ruby") == 0
#endif
#ifdef FEAT_SYN_HL
|| STRICMP(feature, "syntax_items") == 0
#endif
#ifdef DYNAMIC_TCL
|| STRICMP(feature, "tcl") == 0
#endif
// once "starting" is zero it will stay that way
|| (STRICMP(feature, "vim_starting") == 0 && starting != 0)
|| STRICMP(feature, "multi_byte_encoding") == 0
#if defined(FEAT_TERMINAL) && defined(MSWIN)
|| STRICMP(feature, "conpty") == 0
#endif
);
}
/*
* "haslocaldir()" function
*/
static void
f_haslocaldir(typval_T *argvars, typval_T *rettv)
{
tabpage_T *tp = NULL;
win_T *wp = NULL;
if (in_vim9script()
&& (check_for_opt_number_arg(argvars, 0) == FAIL
|| (argvars[0].v_type != VAR_UNKNOWN
&& check_for_opt_number_arg(argvars, 1) == FAIL)))
return;
wp = find_tabwin(&argvars[0], &argvars[1], &tp);
// Check for window-local and tab-local directories
if (wp != NULL && wp->w_localdir != NULL)
rettv->vval.v_number = 1;
else if (tp != NULL && tp->tp_localdir != NULL)
rettv->vval.v_number = 2;
else
rettv->vval.v_number = 0;
}
/*
* "highlightID(name)" function
*/
static void
f_hlID(typval_T *argvars, typval_T *rettv)
{
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
rettv->vval.v_number = syn_name2id(tv_get_string(&argvars[0]));
}
/*
* "highlight_exists()" function
*/
static void
f_hlexists(typval_T *argvars, typval_T *rettv)
{
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
rettv->vval.v_number = highlight_exists(tv_get_string(&argvars[0]));
}
/*
* "hostname()" function
*/
static void
f_hostname(typval_T *argvars UNUSED, typval_T *rettv)
{
char_u hostname[256];
mch_get_host_name(hostname, 256);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = vim_strsave(hostname);
}
/*
* "index()" function
*/
static void
f_index(typval_T *argvars, typval_T *rettv)
{
list_T *l;
listitem_T *item;
blob_T *b;
long idx = 0;
int ic = FALSE;
int error = FALSE;
rettv->vval.v_number = -1;
if (in_vim9script()
&& (check_for_list_or_blob_arg(argvars, 0) == FAIL
|| (argvars[0].v_type == VAR_BLOB
&& check_for_number_arg(argvars, 1) == FAIL)
|| check_for_opt_number_arg(argvars, 2) == FAIL
|| (argvars[2].v_type != VAR_UNKNOWN
&& check_for_opt_bool_arg(argvars, 3) == FAIL)))
return;
if (argvars[0].v_type == VAR_BLOB)
{
typval_T tv;
int start = 0;
if (argvars[2].v_type != VAR_UNKNOWN)
{
start = tv_get_number_chk(&argvars[2], &error);
if (error)
return;
}
b = argvars[0].vval.v_blob;
if (b == NULL)
return;
if (start < 0)
{
start = blob_len(b) + start;
if (start < 0)
start = 0;
}
for (idx = start; idx < blob_len(b); ++idx)
{
tv.v_type = VAR_NUMBER;
tv.vval.v_number = blob_get(b, idx);
if (tv_equal(&tv, &argvars[1], ic, FALSE))
{
rettv->vval.v_number = idx;
return;
}
}
return;
}
else if (argvars[0].v_type != VAR_LIST)
{
emsg(_(e_list_or_blob_required));
return;
}
l = argvars[0].vval.v_list;
if (l == NULL)
return;
CHECK_LIST_MATERIALIZE(l);
item = l->lv_first;
if (argvars[2].v_type != VAR_UNKNOWN)
{
// Start at specified item. Use the cached index that list_find()
// sets, so that a negative number also works.
item = list_find(l, (long)tv_get_number_chk(&argvars[2], &error));
idx = l->lv_u.mat.lv_idx;
if (argvars[3].v_type != VAR_UNKNOWN)
ic = (int)tv_get_bool_chk(&argvars[3], &error);
if (error)
item = NULL;
}
for ( ; item != NULL; item = item->li_next, ++idx)
if (tv_equal(&item->li_tv, &argvars[1], ic, FALSE))
{
rettv->vval.v_number = idx;
break;
}
}
/*
* Evaluate 'expr' with the v:key and v:val arguments and return the result.
* The expression is expected to return a boolean value. The caller should set
* the VV_KEY and VV_VAL vim variables before calling this function.
*/
static int
indexof_eval_expr(typval_T *expr)
{
typval_T argv[3];
typval_T newtv;
varnumber_T found;
int error = FALSE;
argv[0] = *get_vim_var_tv(VV_KEY);
argv[1] = *get_vim_var_tv(VV_VAL);
newtv.v_type = VAR_UNKNOWN;
if (eval_expr_typval(expr, FALSE, argv, 2, NULL, &newtv) == FAIL)
return FALSE;
found = tv_get_bool_chk(&newtv, &error);
clear_tv(&newtv);
return error ? FALSE : found;
}
/*
* Evaluate 'expr' for each byte in the Blob 'b' starting with the byte at
* 'startidx' and return the index of the byte where 'expr' is TRUE. Returns
* -1 if 'expr' doesn't evaluate to TRUE for any of the bytes.
*/
static int
indexof_blob(blob_T *b, long startidx, typval_T *expr)
{
long idx = 0;
if (b == NULL)
return -1;
if (startidx < 0)
{
// negative index: index from the last byte
startidx = blob_len(b) + startidx;
if (startidx < 0)
startidx = 0;
}
set_vim_var_type(VV_KEY, VAR_NUMBER);
set_vim_var_type(VV_VAL, VAR_NUMBER);
for (idx = startidx; idx < blob_len(b); ++idx)
{
set_vim_var_nr(VV_KEY, idx);
set_vim_var_nr(VV_VAL, blob_get(b, idx));
if (indexof_eval_expr(expr))
return idx;
}
return -1;
}
/*
* Evaluate 'expr' for each item in the List 'l' starting with the item at
* 'startidx' and return the index of the item where 'expr' is TRUE. Returns
* -1 if 'expr' doesn't evaluate to TRUE for any of the items.
*/
static int
indexof_list(list_T *l, long startidx, typval_T *expr)
{
listitem_T *item;
long idx = 0;
int found;
if (l == NULL)
return -1;
CHECK_LIST_MATERIALIZE(l);
if (startidx == 0)
item = l->lv_first;
else
{
// Start at specified item. Use the cached index that list_find()
// sets, so that a negative number also works.
item = list_find(l, startidx);
if (item != NULL)
idx = l->lv_u.mat.lv_idx;
}
set_vim_var_type(VV_KEY, VAR_NUMBER);
for ( ; item != NULL; item = item->li_next, ++idx)
{
set_vim_var_nr(VV_KEY, idx);
copy_tv(&item->li_tv, get_vim_var_tv(VV_VAL));
found = indexof_eval_expr(expr);
clear_tv(get_vim_var_tv(VV_VAL));
if (found)
return idx;
}
return -1;
}
/*
* "indexof()" function
*/
static void
f_indexof(typval_T *argvars, typval_T *rettv)
{
long startidx = 0;
typval_T save_val;
typval_T save_key;
int save_did_emsg;
rettv->vval.v_number = -1;
if (check_for_list_or_blob_arg(argvars, 0) == FAIL
|| check_for_string_or_func_arg(argvars, 1) == FAIL
|| check_for_opt_dict_arg(argvars, 2) == FAIL)
return;
if ((argvars[1].v_type == VAR_STRING && argvars[1].vval.v_string == NULL)
|| (argvars[1].v_type == VAR_FUNC
&& argvars[1].vval.v_partial == NULL))
return;
if (argvars[2].v_type == VAR_DICT)
startidx = dict_get_number_def(argvars[2].vval.v_dict, "startidx", 0);
prepare_vimvar(VV_VAL, &save_val);
prepare_vimvar(VV_KEY, &save_key);
// We reset "did_emsg" to be able to detect whether an error occurred
// during evaluation of the expression.
save_did_emsg = did_emsg;
did_emsg = FALSE;
if (argvars[0].v_type == VAR_BLOB)
rettv->vval.v_number = indexof_blob(argvars[0].vval.v_blob, startidx,
&argvars[1]);
else
rettv->vval.v_number = indexof_list(argvars[0].vval.v_list, startidx,
&argvars[1]);
restore_vimvar(VV_KEY, &save_key);
restore_vimvar(VV_VAL, &save_val);
did_emsg |= save_did_emsg;
}
static int inputsecret_flag = 0;
/*
* "input()" function
* Also handles inputsecret() when inputsecret is set.
*/
static void
f_input(typval_T *argvars, typval_T *rettv)
{
get_user_input(argvars, rettv, FALSE, inputsecret_flag);
}
/*
* "inputdialog()" function
*/
static void
f_inputdialog(typval_T *argvars, typval_T *rettv)
{
#if defined(FEAT_GUI_TEXTDIALOG)
// Use a GUI dialog if the GUI is running and 'c' is not in 'guioptions'
if (gui.in_use && vim_strchr(p_go, GO_CONDIALOG) == NULL)
{
char_u *message;
char_u buf[NUMBUFLEN];
char_u *defstr = (char_u *)"";
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_opt_string_arg(argvars, 1) == FAIL
|| (argvars[1].v_type != VAR_UNKNOWN
&& check_for_opt_string_arg(argvars, 2) == FAIL)))
return;
message = tv_get_string_chk(&argvars[0]);
if (argvars[1].v_type != VAR_UNKNOWN
&& (defstr = tv_get_string_buf_chk(&argvars[1], buf)) != NULL)
vim_strncpy(IObuff, defstr, IOSIZE - 1);
else
IObuff[0] = NUL;
if (message != NULL && defstr != NULL
&& do_dialog(VIM_QUESTION, NULL, message,
(char_u *)_("&OK\n&Cancel"), 1, IObuff, FALSE) == 1)
rettv->vval.v_string = vim_strsave(IObuff);
else
{
if (message != NULL && defstr != NULL
&& argvars[1].v_type != VAR_UNKNOWN
&& argvars[2].v_type != VAR_UNKNOWN)
rettv->vval.v_string = vim_strsave(
tv_get_string_buf(&argvars[2], buf));
else
rettv->vval.v_string = NULL;
}
rettv->v_type = VAR_STRING;
}
else
#endif
get_user_input(argvars, rettv, TRUE, inputsecret_flag);
}
/*
* "inputlist()" function
*/
static void
f_inputlist(typval_T *argvars, typval_T *rettv)
{
list_T *l;
listitem_T *li;
int selected;
int mouse_used;
#ifdef NO_CONSOLE_INPUT
// While starting up, there is no place to enter text. When running tests
// with --not-a-term we assume feedkeys() will be used.
if (no_console_input() && !is_not_a_term())
return;
#endif
if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
return;
if (argvars[0].v_type != VAR_LIST || argvars[0].vval.v_list == NULL)
{
semsg(_(e_argument_of_str_must_be_list), "inputlist()");
return;
}
msg_start();
msg_row = Rows - 1; // for when 'cmdheight' > 1
lines_left = Rows; // avoid more prompt
msg_scroll = TRUE;
msg_clr_eos();
l = argvars[0].vval.v_list;
CHECK_LIST_MATERIALIZE(l);
FOR_ALL_LIST_ITEMS(l, li)
{
msg_puts((char *)tv_get_string(&li->li_tv));
msg_putchar('\n');
}
// Ask for choice.
selected = prompt_for_number(&mouse_used);
if (mouse_used)
selected -= lines_left;
rettv->vval.v_number = selected;
}
static garray_T ga_userinput = {0, 0, sizeof(tasave_T), 4, NULL};
/*
* "inputrestore()" function
*/
static void
f_inputrestore(typval_T *argvars UNUSED, typval_T *rettv)
{
if (ga_userinput.ga_len > 0)
{
--ga_userinput.ga_len;
restore_typeahead((tasave_T *)(ga_userinput.ga_data)
+ ga_userinput.ga_len, TRUE);
// default return is zero == OK
}
else if (p_verbose > 1)
{
verb_msg(_("called inputrestore() more often than inputsave()"));
rettv->vval.v_number = 1; // Failed
}
}
/*
* "inputsave()" function
*/
static void
f_inputsave(typval_T *argvars UNUSED, typval_T *rettv)
{
// Add an entry to the stack of typeahead storage.
if (ga_grow(&ga_userinput, 1) == OK)
{
save_typeahead((tasave_T *)(ga_userinput.ga_data)
+ ga_userinput.ga_len);
++ga_userinput.ga_len;
// default return is zero == OK
}
else
rettv->vval.v_number = 1; // Failed
}
/*
* "inputsecret()" function
*/
static void
f_inputsecret(typval_T *argvars, typval_T *rettv)
{
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_opt_string_arg(argvars, 1) == FAIL))
return;
++cmdline_star;
++inputsecret_flag;
f_input(argvars, rettv);
--cmdline_star;
--inputsecret_flag;
}
/*
* "interrupt()" function
*/
static void
f_interrupt(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
{
got_int = TRUE;
}
/*
* "invert(expr)" function
*/
static void
f_invert(typval_T *argvars, typval_T *rettv)
{
if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL)
return;
rettv->vval.v_number = ~tv_get_number_chk(&argvars[0], NULL);
}
/*
* Free resources in lval_root allocated by fill_exec_lval_root().
*/
static void
free_lval_root(lval_root_T *root)
{
if (root->lr_tv != NULL)
free_tv(root->lr_tv);
class_unref(root->lr_cl_exec);
root->lr_tv = NULL;
root->lr_cl_exec = NULL;
}
/*
* This is used if executing in a method, the argument string is a
* variable/item expr/reference. It may start with a potential class/object
* variable.
*
* Adjust "root" as needed; lr_tv may be changed or freed.
*
* Always returns OK.
* Free resources and return FAIL if the root should not be used. Otherwise OK.
*/
static int
fix_variable_reference_lval_root(lval_root_T *root, char_u *name)
{
// Check if lr_tv is the name of an object/class reference: name start with
// "this" or name is class variable. Clear lr_tv if neither.
int found_member = FALSE;
if (root->lr_tv->v_type == VAR_OBJECT)
{
if (STRNCMP("this.", name, 5) == 0 ||STRCMP("this", name) == 0)
found_member = TRUE;
}
if (!found_member) // not object member, try class member
{
// Explicitly check if the name is a class member.
// If it's not then do nothing.
char_u *end;
for (end = name; ASCII_ISALNUM(*end) || *end == '_'; ++end)
;
int idx = class_member_idx(root->lr_cl_exec, name, end - name);
if (idx >= 0)
{
// A class variable, replace lr_tv with it
clear_tv(root->lr_tv);
copy_tv(&root->lr_cl_exec->class_members_tv[idx], root->lr_tv);
found_member = TRUE;
}
}
if (!found_member)
{
free_tv(root->lr_tv);
root->lr_tv = NULL; // Not a member variable
}
// If FAIL, then must free_lval_root(root);
return OK;
}
/*
* "islocked()" function
*/
static void
f_islocked(typval_T *argvars, typval_T *rettv)
{
lval_T lv;
char_u *end;
dictitem_T *di;
rettv->vval.v_number = -1;
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
char_u *name = tv_get_string(&argvars[0]);
#ifdef LOG_LOCKVAR
ch_log(NULL, "LKVAR: f_islocked(): name: %s", name);
#endif
lval_root_T aroot; // fully initialized in fill_exec_lval_root
lval_root_T *root = NULL;
// Set up lval_root if executing in a method.
if (fill_exec_lval_root(&aroot) == OK)
{
// Almost always produces a valid lval_root since lr_cl_exec is used
// for access verification, lr_tv may be set to NULL.
if (fix_variable_reference_lval_root(&aroot, name) == OK)
root = &aroot;
}
lval_root_T *lval_root_save = lval_root;
lval_root = root;
end = get_lval(name, NULL, &lv, FALSE, FALSE,
GLV_NO_AUTOLOAD | GLV_READ_ONLY | GLV_NO_DECL,
FNE_CHECK_START);
lval_root = lval_root_save;
if (end != NULL && lv.ll_name != NULL)
{
if (*end != NUL)
{
semsg(_(lv.ll_name == lv.ll_name_end
? e_invalid_argument_str : e_trailing_characters_str), end);
}
else
{
if (lv.ll_tv == NULL)
{
di = find_var(lv.ll_name, NULL, TRUE);
if (di != NULL)
{
// Consider a variable locked when:
// 1. the variable itself is locked
// 2. the value of the variable is locked.
// 3. the List or Dict value is locked.
rettv->vval.v_number = ((di->di_flags & DI_FLAGS_LOCK)
|| tv_islocked(&di->di_tv));
}
}
else if (lv.ll_is_root)
{
rettv->vval.v_number = tv_islocked(lv.ll_tv);
}
else if (lv.ll_object != NULL)
{
typval_T *tv = ((typval_T *)(lv.ll_object + 1)) + lv.ll_oi;
rettv->vval.v_number = tv_islocked(tv);
#ifdef LOG_LOCKVAR
ch_log(NULL, "LKVAR: f_islocked(): name %s (obj)", lv.ll_name);
#endif
}
else if (lv.ll_class != NULL)
{
typval_T *tv = &lv.ll_class->class_members_tv[lv.ll_oi];
rettv->vval.v_number = tv_islocked(tv);
#ifdef LOG_LOCKVAR
ch_log(NULL, "LKVAR: f_islocked(): name %s (cl)", lv.ll_name);
#endif
}
else if (lv.ll_range)
emsg(_(e_range_not_allowed));
else if (lv.ll_newkey != NULL)
semsg(_(e_key_not_present_in_dictionary_str), lv.ll_newkey);
else if (lv.ll_list != NULL)
// List item.
rettv->vval.v_number = tv_islocked(&lv.ll_li->li_tv);
else
// Dictionary item.
rettv->vval.v_number = tv_islocked(&lv.ll_di->di_tv);
}
}
if (root != NULL)
free_lval_root(root);
clear_lval(&lv);
}
/*
* "keytrans()" function
*/
static void
f_keytrans(typval_T *argvars, typval_T *rettv)
{
char_u *escaped;
rettv->v_type = VAR_STRING;
if (check_for_string_arg(argvars, 0) == FAIL
|| argvars[0].vval.v_string == NULL)
return;
// Need to escape K_SPECIAL and CSI for mb_unescape().
escaped = vim_strsave_escape_csi(argvars[0].vval.v_string);
rettv->vval.v_string = str2special_save(escaped, TRUE, TRUE);
vim_free(escaped);
}
/*
* "last_buffer_nr()" function.
*/
static void
f_last_buffer_nr(typval_T *argvars UNUSED, typval_T *rettv)
{
int n = 0;
buf_T *buf;
FOR_ALL_BUFFERS(buf)
if (n < buf->b_fnum)
n = buf->b_fnum;
rettv->vval.v_number = n;
}
/*
* "len()" function
*/
void
f_len(typval_T *argvars, typval_T *rettv)
{
switch (argvars[0].v_type)
{
case VAR_STRING:
case VAR_NUMBER:
rettv->vval.v_number = (varnumber_T)STRLEN(
tv_get_string(&argvars[0]));
break;
case VAR_BLOB:
rettv->vval.v_number = blob_len(argvars[0].vval.v_blob);
break;
case VAR_LIST:
rettv->vval.v_number = list_len(argvars[0].vval.v_list);
break;
case VAR_DICT:
rettv->vval.v_number = dict_len(argvars[0].vval.v_dict);
break;
case VAR_UNKNOWN:
case VAR_ANY:
case VAR_VOID:
case VAR_BOOL:
case VAR_SPECIAL:
case VAR_FLOAT:
case VAR_FUNC:
case VAR_PARTIAL:
case VAR_JOB:
case VAR_CHANNEL:
case VAR_INSTR:
case VAR_CLASS:
case VAR_OBJECT:
case VAR_TYPEALIAS:
emsg(_(e_invalid_type_for_len));
break;
}
}
static void
libcall_common(typval_T *argvars UNUSED, typval_T *rettv, int type)
{
#ifdef FEAT_LIBCALL
char_u *string_in;
char_u **string_result;
int nr_result;
#endif
rettv->v_type = type;
if (type != VAR_NUMBER)
rettv->vval.v_string = NULL;
if (check_restricted() || check_secure())
return;
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_string_arg(argvars, 1) == FAIL
|| check_for_string_or_number_arg(argvars, 2) == FAIL))
return;
#ifdef FEAT_LIBCALL
// The first two args must be strings, otherwise it's meaningless
if (argvars[0].v_type == VAR_STRING && argvars[1].v_type == VAR_STRING)
{
string_in = NULL;
if (argvars[2].v_type == VAR_STRING)
string_in = argvars[2].vval.v_string;
if (type == VAR_NUMBER)
{
string_result = NULL;
}
else
{
rettv->vval.v_string = NULL;
string_result = &rettv->vval.v_string;
}
if (mch_libcall(argvars[0].vval.v_string,
argvars[1].vval.v_string,
string_in,
argvars[2].vval.v_number,
string_result,
&nr_result) == OK
&& type == VAR_NUMBER)
rettv->vval.v_number = nr_result;
}
#endif
}
/*
* "libcall()" function
*/
static void
f_libcall(typval_T *argvars, typval_T *rettv)
{
libcall_common(argvars, rettv, VAR_STRING);
}
/*
* "libcallnr()" function
*/
static void
f_libcallnr(typval_T *argvars, typval_T *rettv)
{
libcall_common(argvars, rettv, VAR_NUMBER);
}
/*
* "line(string, [winid])" function
*/
static void
f_line(typval_T *argvars, typval_T *rettv)
{
linenr_T lnum = 0;
pos_T *fp = NULL;
int fnum;
int id;
tabpage_T *tp;
win_T *wp;
switchwin_T switchwin;
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_opt_number_arg(argvars, 1) == FAIL))
return;
if (argvars[1].v_type != VAR_UNKNOWN)
{
// use window specified in the second argument
id = (int)tv_get_number(&argvars[1]);
wp = win_id2wp_tp(id, &tp);
if (wp != NULL && tp != NULL)
{
if (switch_win_noblock(&switchwin, wp, tp, TRUE) == OK)
{
check_cursor();
fp = var2fpos(&argvars[0], TRUE, &fnum, FALSE);
}
restore_win_noblock(&switchwin, TRUE);
}
}
else
// use current window
fp = var2fpos(&argvars[0], TRUE, &fnum, FALSE);
if (fp != NULL)
lnum = fp->lnum;
rettv->vval.v_number = lnum;
}
/*
* "line2byte(lnum)" function
*/
static void
f_line2byte(typval_T *argvars UNUSED, typval_T *rettv)
{
#ifndef FEAT_BYTEOFF
rettv->vval.v_number = -1;
#else
linenr_T lnum;
if (in_vim9script() && check_for_lnum_arg(argvars, 0) == FAIL)
return;
lnum = tv_get_lnum(argvars);
if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1)
rettv->vval.v_number = -1;
else
rettv->vval.v_number = ml_find_line_or_offset(curbuf, lnum, NULL);
if (rettv->vval.v_number >= 0)
++rettv->vval.v_number;
#endif
}
#ifdef FEAT_LUA
/*
* "luaeval()" function
*/
static void
f_luaeval(typval_T *argvars, typval_T *rettv)
{
char_u *str;
char_u buf[NUMBUFLEN];
if (check_restricted() || check_secure())
return;
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
str = tv_get_string_buf(&argvars[0], buf);
do_luaeval(str, argvars + 1, rettv);
}
#endif
typedef enum
{
MATCH_END, // matchend()
MATCH_MATCH, // match()
MATCH_STR, // matchstr()
MATCH_LIST, // matchlist()
MATCH_POS // matchstrpos()
} matchtype_T;
static void
find_some_match(typval_T *argvars, typval_T *rettv, matchtype_T type)
{
char_u *str = NULL;
long len = 0;
char_u *expr = NULL;
char_u *pat;
regmatch_T regmatch;
char_u patbuf[NUMBUFLEN];
char_u strbuf[NUMBUFLEN];
char_u *save_cpo;
long start = 0;
long nth = 1;
colnr_T startcol = 0;
int match = 0;
list_T *l = NULL;
listitem_T *li = NULL;
long idx = 0;
char_u *tofree = NULL;
// Make 'cpoptions' empty, the 'l' flag should not be used here.
save_cpo = p_cpo;
p_cpo = empty_option;
rettv->vval.v_number = -1;
if (type == MATCH_LIST || type == MATCH_POS)
{
// type MATCH_LIST: return empty list when there are no matches.
// type MATCH_POS: return ["", -1, -1, -1]
if (rettv_list_alloc(rettv) == FAIL)
goto theend;
if (type == MATCH_POS
&& (list_append_string(rettv->vval.v_list,
(char_u *)"", 0) == FAIL
|| list_append_number(rettv->vval.v_list,
(varnumber_T)-1) == FAIL
|| list_append_number(rettv->vval.v_list,
(varnumber_T)-1) == FAIL
|| list_append_number(rettv->vval.v_list,
(varnumber_T)-1) == FAIL))
{
list_free(rettv->vval.v_list);
rettv->vval.v_list = NULL;
goto theend;
}
}
else if (type == MATCH_STR)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
}
if (in_vim9script()
&& (check_for_string_or_list_arg(argvars, 0) == FAIL
|| check_for_string_arg(argvars, 1) == FAIL
|| check_for_opt_number_arg(argvars, 2) == FAIL
|| (argvars[2].v_type != VAR_UNKNOWN
&& check_for_opt_number_arg(argvars, 3) == FAIL)))
goto theend;
if (argvars[0].v_type == VAR_LIST)
{
if ((l = argvars[0].vval.v_list) == NULL)
goto theend;
CHECK_LIST_MATERIALIZE(l);
li = l->lv_first;
}
else
{
expr = str = tv_get_string(&argvars[0]);
len = (long)STRLEN(str);
}
pat = tv_get_string_buf_chk(&argvars[1], patbuf);
if (pat == NULL)
goto theend;
if (argvars[2].v_type != VAR_UNKNOWN)
{
int error = FALSE;
start = (long)tv_get_number_chk(&argvars[2], &error);
if (error)
goto theend;
if (l != NULL)
{
li = list_find(l, start);
if (li == NULL)
goto theend;
idx = l->lv_u.mat.lv_idx; // use the cached index
}
else
{
if (start < 0)
start = 0;
if (start > len)
goto theend;
// When "count" argument is there ignore matches before "start",
// otherwise skip part of the string. Differs when pattern is "^"
// or "\<".
if (argvars[3].v_type != VAR_UNKNOWN)
startcol = start;
else
{
str += start;
len -= start;
}
}
if (argvars[3].v_type != VAR_UNKNOWN)
nth = (long)tv_get_number_chk(&argvars[3], &error);
if (error)
goto theend;
}
regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog != NULL)
{
regmatch.rm_ic = p_ic;
for (;;)
{
if (l != NULL)
{
if (li == NULL)
{
match = FALSE;
break;
}
vim_free(tofree);
expr = str = echo_string(&li->li_tv, &tofree, strbuf, 0);
if (str == NULL)
break;
}
match = vim_regexec_nl(&regmatch, str, startcol);
if (match && --nth <= 0)
break;
if (l == NULL && !match)
break;
// Advance to just after the match.
if (l != NULL)
{
li = li->li_next;
++idx;
}
else
{
startcol = (colnr_T)(regmatch.startp[0]
+ (*mb_ptr2len)(regmatch.startp[0]) - str);
if (startcol > (colnr_T)len
|| str + startcol <= regmatch.startp[0])
{
match = FALSE;
break;
}
}
}
if (match)
{
if (type == MATCH_POS)
{
listitem_T *li1 = rettv->vval.v_list->lv_first;
listitem_T *li2 = li1->li_next;
listitem_T *li3 = li2->li_next;
listitem_T *li4 = li3->li_next;
vim_free(li1->li_tv.vval.v_string);
li1->li_tv.vval.v_string = vim_strnsave(regmatch.startp[0],
regmatch.endp[0] - regmatch.startp[0]);
li3->li_tv.vval.v_number =
(varnumber_T)(regmatch.startp[0] - expr);
li4->li_tv.vval.v_number =
(varnumber_T)(regmatch.endp[0] - expr);
if (l != NULL)
li2->li_tv.vval.v_number = (varnumber_T)idx;
}
else if (type == MATCH_LIST)
{
int i;
// return list with matched string and submatches
for (i = 0; i < NSUBEXP; ++i)
{
if (regmatch.endp[i] == NULL)
{
if (list_append_string(rettv->vval.v_list,
(char_u *)"", 0) == FAIL)
break;
}
else if (list_append_string(rettv->vval.v_list,
regmatch.startp[i],
(int)(regmatch.endp[i] - regmatch.startp[i]))
== FAIL)
break;
}
}
else if (type == MATCH_STR)
{
// return matched string
if (l != NULL)
copy_tv(&li->li_tv, rettv);
else
rettv->vval.v_string = vim_strnsave(regmatch.startp[0],
regmatch.endp[0] - regmatch.startp[0]);
}
else if (l != NULL)
rettv->vval.v_number = idx;
else
{
if (type != MATCH_END)
rettv->vval.v_number =
(varnumber_T)(regmatch.startp[0] - str);
else
rettv->vval.v_number =
(varnumber_T)(regmatch.endp[0] - str);
rettv->vval.v_number += (varnumber_T)(str - expr);
}
}
vim_regfree(regmatch.regprog);
}
theend:
if (type == MATCH_POS && l == NULL && rettv->vval.v_list != NULL)
// matchstrpos() without a list: drop the second item.
listitem_remove(rettv->vval.v_list,
rettv->vval.v_list->lv_first->li_next);
vim_free(tofree);
p_cpo = save_cpo;
}
/*
* Return all the matches in string "str" for pattern "rmp".
* The matches are returned in the List "mlist".
* If "submatches" is TRUE, then submatch information is also returned.
* "matchbuf" is TRUE when called for matchbufline().
*/
static int
get_matches_in_str(
char_u *str,
regmatch_T *rmp,
list_T *mlist,
int idx,
int submatches,
int matchbuf)
{
long len = (long)STRLEN(str);
int match = 0;
colnr_T startidx = 0;
for (;;)
{
match = vim_regexec_nl(rmp, str, startidx);
if (!match)
break;
dict_T *d = dict_alloc();
if (d == NULL)
return FAIL;
if (list_append_dict(mlist, d) == FAIL)
return FAIL;;
if (dict_add_number(d, matchbuf ? "lnum" : "idx", idx) == FAIL)
return FAIL;
if (dict_add_number(d, "byteidx",
(colnr_T)(rmp->startp[0] - str)) == FAIL)
return FAIL;
if (dict_add_string_len(d, "text", rmp->startp[0],
(int)(rmp->endp[0] - rmp->startp[0])) == FAIL)
return FAIL;
if (submatches)
{
list_T *sml = list_alloc();
if (sml == NULL)
return FAIL;
if (dict_add_list(d, "submatches", sml) == FAIL)
return FAIL;
// return a list with the submatches
for (int i = 1; i < NSUBEXP; ++i)
{
if (rmp->endp[i] == NULL)
{
if (list_append_string(sml, (char_u *)"", 0) == FAIL)
return FAIL;
}
else if (list_append_string(sml, rmp->startp[i],
(int)(rmp->endp[i] - rmp->startp[i])) == FAIL)
return FAIL;
}
}
startidx = (colnr_T)(rmp->endp[0] - str);
if (startidx >= (colnr_T)len || str + startidx <= rmp->startp[0])
break;
}
return OK;
}
/*
* "matchbufline()" function
*/
static void
f_matchbufline(typval_T *argvars, typval_T *rettv)
{
list_T *retlist = NULL;
char_u *save_cpo;
char_u patbuf[NUMBUFLEN];
regmatch_T regmatch;
rettv->vval.v_number = -1;
if (rettv_list_alloc(rettv) != OK)
return;
retlist = rettv->vval.v_list;
if (check_for_buffer_arg(argvars, 0) == FAIL
|| check_for_string_arg(argvars, 1) == FAIL
|| check_for_lnum_arg(argvars, 2) == FAIL
|| check_for_lnum_arg(argvars, 3) == FAIL
|| check_for_opt_dict_arg(argvars, 4) == FAIL)
return;
int prev_did_emsg = did_emsg;
buf_T *buf = tv_get_buf(&argvars[0], FALSE);
if (buf == NULL)
{
if (did_emsg == prev_did_emsg)
semsg(_(e_invalid_buffer_name_str), tv_get_string(&argvars[0]));
return;
}
if (buf->b_ml.ml_mfp == NULL)
{
emsg(_(e_buffer_is_not_loaded));
return;
}
char_u *pat = tv_get_string_buf(&argvars[1], patbuf);
int did_emsg_before = did_emsg;
linenr_T slnum = tv_get_lnum_buf(&argvars[2], buf);
if (did_emsg > did_emsg_before)
return;
if (slnum < 1)
{
semsg(_(e_invalid_value_for_argument_str), "lnum");
return;
}
linenr_T elnum = tv_get_lnum_buf(&argvars[3], buf);
if (did_emsg > did_emsg_before)
return;
if (elnum < 1 || elnum < slnum)
{
semsg(_(e_invalid_value_for_argument_str), "end_lnum");
return;
}
if (elnum > buf->b_ml.ml_line_count)
elnum = buf->b_ml.ml_line_count;
int submatches = FALSE;
if (argvars[4].v_type != VAR_UNKNOWN)
{
dict_T *d = argvars[4].vval.v_dict;
if (d != NULL)
{
dictitem_T *di = dict_find(d, (char_u *)"submatches", -1);
if (di != NULL)
{
if (di->di_tv.v_type != VAR_BOOL)
{
semsg(_(e_invalid_value_for_argument_str), "submatches");
return;
}
submatches = tv_get_bool(&di->di_tv);
}
}
}
// Make 'cpoptions' empty, the 'l' flag should not be used here.
save_cpo = p_cpo;
p_cpo = empty_option;
regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog == NULL)
goto theend;
regmatch.rm_ic = p_ic;
while (slnum <= elnum)
{
char_u *str = ml_get_buf(buf, slnum, FALSE);
if (get_matches_in_str(str, &regmatch, retlist, slnum, submatches,
TRUE) == FAIL)
goto cleanup;
slnum++;
}
cleanup:
vim_regfree(regmatch.regprog);
theend:
p_cpo = save_cpo;
}
/*
* "match()" function
*/
static void
f_match(typval_T *argvars, typval_T *rettv)
{
find_some_match(argvars, rettv, MATCH_MATCH);
}
/*
* "matchend()" function
*/
static void
f_matchend(typval_T *argvars, typval_T *rettv)
{
find_some_match(argvars, rettv, MATCH_END);
}
/*
* "matchlist()" function
*/
static void
f_matchlist(typval_T *argvars, typval_T *rettv)
{
find_some_match(argvars, rettv, MATCH_LIST);
}
/*
* "matchstr()" function
*/
static void
f_matchstr(typval_T *argvars, typval_T *rettv)
{
find_some_match(argvars, rettv, MATCH_STR);
}
/*
* "matchstrlist()" function
*/
static void
f_matchstrlist(typval_T *argvars, typval_T *rettv)
{
list_T *retlist = NULL;
char_u *save_cpo;
list_T *l = NULL;
listitem_T *li = NULL;
char_u patbuf[NUMBUFLEN];
regmatch_T regmatch;
rettv->vval.v_number = -1;
if (rettv_list_alloc(rettv) != OK)
return;
retlist = rettv->vval.v_list;
if (check_for_list_arg(argvars, 0) == FAIL
|| check_for_string_arg(argvars, 1) == FAIL
|| check_for_opt_dict_arg(argvars, 2) == FAIL)
return;
if ((l = argvars[0].vval.v_list) == NULL)
return;
char_u *pat = tv_get_string_buf_chk(&argvars[1], patbuf);
if (pat == NULL)
return;
// Make 'cpoptions' empty, the 'l' flag should not be used here.
save_cpo = p_cpo;
p_cpo = empty_option;
regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog == NULL)
goto theend;
regmatch.rm_ic = p_ic;
int submatches = FALSE;
if (argvars[2].v_type != VAR_UNKNOWN)
{
dict_T *d = argvars[2].vval.v_dict;
if (d != NULL)
{
dictitem_T *di = dict_find(d, (char_u *)"submatches", -1);
if (di != NULL)
{
if (di->di_tv.v_type != VAR_BOOL)
{
semsg(_(e_invalid_value_for_argument_str), "submatches");
goto cleanup;
}
submatches = tv_get_bool(&di->di_tv);
}
}
}
int idx = 0;
CHECK_LIST_MATERIALIZE(l);
FOR_ALL_LIST_ITEMS(l, li)
{
if (li->li_tv.v_type == VAR_STRING && li->li_tv.vval.v_string != NULL)
{
char_u *str = li->li_tv.vval.v_string;
if (get_matches_in_str(str, &regmatch, retlist, idx, submatches,
FALSE) == FAIL)
goto cleanup;
}
idx++;
}
cleanup:
vim_regfree(regmatch.regprog);
theend:
p_cpo = save_cpo;
}
/*
* "matchstrpos()" function
*/
static void
f_matchstrpos(typval_T *argvars, typval_T *rettv)
{
find_some_match(argvars, rettv, MATCH_POS);
}
static void
max_min(typval_T *argvars, typval_T *rettv, int domax)
{
varnumber_T n = 0;
varnumber_T i;
int error = FALSE;
if (in_vim9script() && check_for_list_or_dict_arg(argvars, 0) == FAIL)
return;
if (argvars[0].v_type == VAR_LIST)
{
list_T *l;
listitem_T *li;
l = argvars[0].vval.v_list;
if (l != NULL && l->lv_len > 0)
{
if (l->lv_first == &range_list_item)
{
if ((l->lv_u.nonmat.lv_stride > 0) ^ domax)
n = l->lv_u.nonmat.lv_start;
else
n = l->lv_u.nonmat.lv_start + ((varnumber_T)l->lv_len - 1)
* l->lv_u.nonmat.lv_stride;
}
else
{
li = l->lv_first;
if (li != NULL)
{
n = tv_get_number_chk(&li->li_tv, &error);
if (error)
return; // type error; errmsg already given
for (;;)
{
li = li->li_next;
if (li == NULL)
break;
i = tv_get_number_chk(&li->li_tv, &error);
if (error)
return; // type error; errmsg already given
if (domax ? i > n : i < n)
n = i;
}
}
}
}
}
else if (argvars[0].v_type == VAR_DICT)
{
dict_T *d;
int first = TRUE;
hashitem_T *hi;
int todo;
d = argvars[0].vval.v_dict;
if (d != NULL)
{
todo = (int)d->dv_hashtab.ht_used;
FOR_ALL_HASHTAB_ITEMS(&d->dv_hashtab, hi, todo)
{
if (!HASHITEM_EMPTY(hi))
{
--todo;
i = tv_get_number_chk(&HI2DI(hi)->di_tv, &error);
if (error)
return; // type error; errmsg already given
if (first)
{
n = i;
first = FALSE;
}
else if (domax ? i > n : i < n)
n = i;
}
}
}
}
else
semsg(_(e_argument_of_str_must_be_list_or_dictionary), domax ? "max()" : "min()");
rettv->vval.v_number = n;
}
/*
* "max()" function
*/
static void
f_max(typval_T *argvars, typval_T *rettv)
{
max_min(argvars, rettv, TRUE);
}
/*
* "min()" function
*/
static void
f_min(typval_T *argvars, typval_T *rettv)
{
max_min(argvars, rettv, FALSE);
}
#if defined(FEAT_MZSCHEME) || defined(PROTO)
/*
* "mzeval()" function
*/
static void
f_mzeval(typval_T *argvars, typval_T *rettv)
{
char_u *str;
char_u buf[NUMBUFLEN];
if (check_restricted() || check_secure())
return;
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
str = tv_get_string_buf(&argvars[0], buf);
do_mzeval(str, rettv);
}
void
mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv)
{
typval_T argvars[3];
argvars[0].v_type = VAR_STRING;
argvars[0].vval.v_string = name;
copy_tv(args, &argvars[1]);
argvars[2].v_type = VAR_UNKNOWN;
f_call(argvars, rettv);
clear_tv(&argvars[1]);
}
#endif
/*
* "nextnonblank()" function
*/
static void
f_nextnonblank(typval_T *argvars, typval_T *rettv)
{
linenr_T lnum;
if (in_vim9script() && check_for_lnum_arg(argvars, 0) == FAIL)
return;
for (lnum = tv_get_lnum(argvars); ; ++lnum)
{
if (lnum < 0 || lnum > curbuf->b_ml.ml_line_count)
{
lnum = 0;
break;
}
if (*skipwhite(ml_get(lnum)) != NUL)
break;
}
rettv->vval.v_number = lnum;
}
/*
* "nr2char()" function
*/
static void
f_nr2char(typval_T *argvars, typval_T *rettv)
{
char_u buf[NUMBUFLEN];
if (in_vim9script()
&& (check_for_number_arg(argvars, 0) == FAIL
|| check_for_opt_bool_arg(argvars, 1) == FAIL))
return;
if (has_mbyte)
{
int utf8 = 0;
if (argvars[1].v_type != VAR_UNKNOWN)
utf8 = (int)tv_get_bool_chk(&argvars[1], NULL);
if (utf8)
buf[utf_char2bytes((int)tv_get_number(&argvars[0]), buf)] = NUL;
else
buf[(*mb_char2bytes)((int)tv_get_number(&argvars[0]), buf)] = NUL;
}
else
{
buf[0] = (char_u)tv_get_number(&argvars[0]);
buf[1] = NUL;
}
rettv->v_type = VAR_STRING;
rettv->vval.v_string = vim_strsave(buf);
}
/*
* "or(expr, expr)" function
*/
static void
f_or(typval_T *argvars, typval_T *rettv)
{
if (in_vim9script()
&& (check_for_number_arg(argvars, 0) == FAIL
|| check_for_number_arg(argvars, 1) == FAIL))
return;
rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL)
| tv_get_number_chk(&argvars[1], NULL);
}
#ifdef FEAT_PERL
/*
* "perleval()" function
*/
static void
f_perleval(typval_T *argvars, typval_T *rettv)
{
char_u *str;
char_u buf[NUMBUFLEN];
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
str = tv_get_string_buf(&argvars[0], buf);
do_perleval(str, rettv);
}
#endif
/*
* "prevnonblank()" function
*/
static void
f_prevnonblank(typval_T *argvars, typval_T *rettv)
{
linenr_T lnum;
if (in_vim9script() && check_for_lnum_arg(argvars, 0) == FAIL)
return;
lnum = tv_get_lnum(argvars);
if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count)
lnum = 0;
else
while (lnum >= 1 && *skipwhite(ml_get(lnum)) == NUL)
--lnum;
rettv->vval.v_number = lnum;
}
// This dummy va_list is here because:
// - passing a NULL pointer doesn't work when va_list isn't a pointer
// - locally in the function results in a "used before set" warning
// - using va_start() to initialize it gives "function with fixed args" error
static va_list ap;
/*
* "printf()" function
*/
static void
f_printf(typval_T *argvars, typval_T *rettv)
{
char_u buf[NUMBUFLEN];
int len;
char_u *s;
int saved_did_emsg = did_emsg;
char *fmt;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
if (in_vim9script() && check_for_string_or_number_arg(argvars, 0) == FAIL)
return;
// Get the required length, allocate the buffer and do it for real.
did_emsg = FALSE;
fmt = (char *)tv_get_string_buf(&argvars[0], buf);
len = vim_vsnprintf_typval(NULL, 0, fmt, ap, argvars + 1);
if (!did_emsg)
{
s = alloc(len + 1);
if (s != NULL)
{
rettv->vval.v_string = s;
(void)vim_vsnprintf_typval((char *)s, len + 1, fmt,
ap, argvars + 1);
}
}
did_emsg |= saved_did_emsg;
}
/*
* "pum_getpos()" function
*/
static void
f_pum_getpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
{
if (rettv_dict_alloc(rettv) == FAIL)
return;
pum_set_event_info(rettv->vval.v_dict);
}
/*
* "pumvisible()" function
*/
static void
f_pumvisible(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
{
if (pum_visible())
rettv->vval.v_number = 1;
}
#ifdef FEAT_PYTHON3
/*
* "py3eval()" function
*/
static void
f_py3eval(typval_T *argvars, typval_T *rettv)
{
char_u *str;
char_u buf[NUMBUFLEN];
if (check_restricted() || check_secure())
return;
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
if (p_pyx == 0)
p_pyx = 3;
str = tv_get_string_buf(&argvars[0], buf);
do_py3eval(str, rettv);
}
#endif
#ifdef FEAT_PYTHON
/*
* "pyeval()" function
*/
static void
f_pyeval(typval_T *argvars, typval_T *rettv)
{
char_u *str;
char_u buf[NUMBUFLEN];
if (check_restricted() || check_secure())
return;
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
if (p_pyx == 0)
p_pyx = 2;
str = tv_get_string_buf(&argvars[0], buf);
do_pyeval(str, rettv);
}
#endif
#if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3)
/*
* "pyxeval()" function
*/
static void
f_pyxeval(typval_T *argvars, typval_T *rettv)
{
if (check_restricted() || check_secure())
return;
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)
init_pyxversion();
if (p_pyx == 2)
f_pyeval(argvars, rettv);
else
f_py3eval(argvars, rettv);
# elif defined(FEAT_PYTHON)
f_pyeval(argvars, rettv);
# elif defined(FEAT_PYTHON3)
f_py3eval(argvars, rettv);
# endif
}
#endif
static UINT32_T srand_seed_for_testing = 0;
static int srand_seed_for_testing_is_used = FALSE;
static void
f_test_srand_seed(typval_T *argvars, typval_T *rettv UNUSED)
{
if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
return;
if (argvars[0].v_type == VAR_UNKNOWN)
srand_seed_for_testing_is_used = FALSE;
else
{
srand_seed_for_testing = (UINT32_T)tv_get_number(&argvars[0]);
srand_seed_for_testing_is_used = TRUE;
}
}
static void
init_srand(UINT32_T *x)
{
#ifndef MSWIN
static int dev_urandom_state = NOTDONE; // FAIL or OK once tried
#endif
if (srand_seed_for_testing_is_used)
{
*x = srand_seed_for_testing;
return;
}
#ifndef MSWIN
if (dev_urandom_state != FAIL)
{
int fd = open("/dev/urandom", O_RDONLY);
struct {
union {
UINT32_T number;
char bytes[sizeof(UINT32_T)];
} contents;
} buf;
// Attempt reading /dev/urandom.
if (fd == -1)
dev_urandom_state = FAIL;
else
{
buf.contents.number = 0;
if (read(fd, buf.contents.bytes, sizeof(UINT32_T))
!= sizeof(UINT32_T))
dev_urandom_state = FAIL;
else
{
dev_urandom_state = OK;
*x = buf.contents.number;
}
close(fd);
}
}
if (dev_urandom_state != OK)
#endif
{
// Reading /dev/urandom doesn't work, fall back to:
// - randombytes_random()
// - reltime() or time()
// - XOR with process ID
#if defined(FEAT_SODIUM)
if (crypt_sodium_init() >= 0)
*x = crypt_sodium_randombytes_random();
else
#endif
{
#if defined(FEAT_RELTIME)
proftime_T res;
profile_start(&res);
# if defined(MSWIN)
*x = (UINT32_T)res.LowPart;
# else
*x = (UINT32_T)res.tv_fsec;
# endif
#else
*x = vim_time();
#endif
*x ^= mch_get_pid();
}
}
}
#define ROTL(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
#define SPLITMIX32(x, z) ( \
(z) = ((x) += 0x9e3779b9), \
(z) = ((z) ^ ((z) >> 16)) * 0x85ebca6b, \
(z) = ((z) ^ ((z) >> 13)) * 0xc2b2ae35, \
(z) ^ ((z) >> 16) \
)
#define SHUFFLE_XOSHIRO128STARSTAR(x, y, z, w) \
result = ROTL((y) * 5, 7) * 9; \
t = (y) << 9; \
(z) ^= (x); \
(w) ^= (y); \
(y) ^= (z), (x) ^= (w); \
(z) ^= t; \
(w) = ROTL(w, 11);
/*
* "rand()" function
*/
static void
f_rand(typval_T *argvars, typval_T *rettv)
{
list_T *l = NULL;
static UINT32_T gx, gy, gz, gw;
static int initialized = FALSE;
listitem_T *lx, *ly, *lz, *lw;
UINT32_T x = 0, y, z, w, t, result;
if (in_vim9script() && check_for_opt_list_arg(argvars, 0) == FAIL)
return;
if (argvars[0].v_type == VAR_UNKNOWN)
{
// When no argument is given use the global seed list.
if (initialized == FALSE)
{
// Initialize the global seed list.
init_srand(&x);
gx = SPLITMIX32(x, z);
gy = SPLITMIX32(x, z);
gz = SPLITMIX32(x, z);
gw = SPLITMIX32(x, z);
initialized = TRUE;
}
SHUFFLE_XOSHIRO128STARSTAR(gx, gy, gz, gw);
}
else if (argvars[0].v_type == VAR_LIST)
{
l = argvars[0].vval.v_list;
if (l == NULL || list_len(l) != 4)
goto theend;
lx = list_find(l, 0L);
ly = list_find(l, 1L);
lz = list_find(l, 2L);
lw = list_find(l, 3L);
if (lx->li_tv.v_type != VAR_NUMBER) goto theend;
if (ly->li_tv.v_type != VAR_NUMBER) goto theend;
if (lz->li_tv.v_type != VAR_NUMBER) goto theend;
if (lw->li_tv.v_type != VAR_NUMBER) goto theend;
x = (UINT32_T)lx->li_tv.vval.v_number;
y = (UINT32_T)ly->li_tv.vval.v_number;
z = (UINT32_T)lz->li_tv.vval.v_number;
w = (UINT32_T)lw->li_tv.vval.v_number;
SHUFFLE_XOSHIRO128STARSTAR(x, y, z, w);
lx->li_tv.vval.v_number = (varnumber_T)x;
ly->li_tv.vval.v_number = (varnumber_T)y;
lz->li_tv.vval.v_number = (varnumber_T)z;
lw->li_tv.vval.v_number = (varnumber_T)w;
}
else
goto theend;
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = (varnumber_T)result;
return;
theend:
semsg(_(e_invalid_argument_str), tv_get_string(&argvars[0]));
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = -1;
}
/*
* "srand()" function
*/
static void
f_srand(typval_T *argvars, typval_T *rettv)
{
UINT32_T x = 0, z;
if (rettv_list_alloc(rettv) == FAIL)
return;
if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
return;
if (argvars[0].v_type == VAR_UNKNOWN)
{
init_srand(&x);
}
else
{
int error = FALSE;
x = (UINT32_T)tv_get_number_chk(&argvars[0], &error);
if (error)
return;
}
list_append_number(rettv->vval.v_list, (varnumber_T)SPLITMIX32(x, z));
list_append_number(rettv->vval.v_list, (varnumber_T)SPLITMIX32(x, z));
list_append_number(rettv->vval.v_list, (varnumber_T)SPLITMIX32(x, z));
list_append_number(rettv->vval.v_list, (varnumber_T)SPLITMIX32(x, z));
}
#undef ROTL
#undef SPLITMIX32
#undef SHUFFLE_XOSHIRO128STARSTAR
/*
* "range()" function
*/
static void
f_range(typval_T *argvars, typval_T *rettv)
{
varnumber_T start;
varnumber_T end;
varnumber_T stride = 1;
int error = FALSE;
if (rettv_list_alloc(rettv) == FAIL)
return;
if (in_vim9script()
&& (check_for_number_arg(argvars, 0) == FAIL
|| check_for_opt_number_arg(argvars, 1) == FAIL
|| (argvars[1].v_type != VAR_UNKNOWN
&& check_for_opt_number_arg(argvars, 2) == FAIL)))
return;
start = tv_get_number_chk(&argvars[0], &error);
if (argvars[1].v_type == VAR_UNKNOWN)
{
end = start - 1;
start = 0;
}
else
{
end = tv_get_number_chk(&argvars[1], &error);
if (argvars[2].v_type != VAR_UNKNOWN)
stride = tv_get_number_chk(&argvars[2], &error);
}
if (error)
return; // type error; errmsg already given
if (stride == 0)
{
emsg(_(e_stride_is_zero));
return;
}
if (stride > 0 ? end + 1 < start : end - 1 > start)
{
emsg(_(e_start_past_end));
return;
}
list_T *list = rettv->vval.v_list;
// Create a non-materialized list. This is much more efficient and
// works with ":for". If used otherwise CHECK_LIST_MATERIALIZE() must
// be called.
list->lv_first = &range_list_item;
list->lv_u.nonmat.lv_start = start;
list->lv_u.nonmat.lv_end = end;
list->lv_u.nonmat.lv_stride = stride;
if (stride > 0 ? end < start : end > start)
list->lv_len = 0;
else
list->lv_len = (end - start) / stride + 1;
}
/*
* Materialize "list".
* Do not call directly, use CHECK_LIST_MATERIALIZE()
*/
void
range_list_materialize(list_T *list)
{
varnumber_T start = list->lv_u.nonmat.lv_start;
varnumber_T end = list->lv_u.nonmat.lv_end;
int stride = list->lv_u.nonmat.lv_stride;
varnumber_T i;
list->lv_first = NULL;
list->lv_u.mat.lv_last = NULL;
list->lv_len = 0;
list->lv_u.mat.lv_idx_item = NULL;
for (i = start; stride > 0 ? i <= end : i >= end; i += stride)
{
if (list_append_number(list, i) == FAIL)
break;
if (list->lv_lock & VAR_ITEMS_LOCKED)
list->lv_u.mat.lv_last->li_tv.v_lock = VAR_LOCKED;
}
list->lv_lock &= ~VAR_ITEMS_LOCKED;
}
/*
* "getreginfo()" function
*/
static void
f_getreginfo(typval_T *argvars, typval_T *rettv)
{
int regname;
char_u buf[NUMBUFLEN + 2];
long reglen = 0;
dict_T *dict;
list_T *list;
if (in_vim9script() && check_for_opt_string_arg(argvars, 0) == FAIL)
return;
regname = getreg_get_regname(argvars);
if (regname == 0)
return;
if (regname == '@')
regname = '"';
if (rettv_dict_alloc(rettv) == FAIL)
return;
dict = rettv->vval.v_dict;
list = (list_T *)get_reg_contents(regname, GREG_EXPR_SRC | GREG_LIST);
if (list == NULL)
return;
(void)dict_add_list(dict, "regcontents", list);
buf[0] = NUL;
buf[1] = NUL;
switch (get_reg_type(regname, &reglen))
{
case MLINE: buf[0] = 'V'; break;
case MCHAR: buf[0] = 'v'; break;
case MBLOCK:
vim_snprintf((char *)buf, sizeof(buf), "%c%ld", Ctrl_V,
reglen + 1);
break;
}
(void)dict_add_string(dict, (char *)"regtype", buf);
buf[0] = get_register_name(get_unname_register());
buf[1] = NUL;
if (regname == '"')
(void)dict_add_string(dict, (char *)"points_to", buf);
else
{
dictitem_T *item = dictitem_alloc((char_u *)"isunnamed");
if (item != NULL)
{
item->di_tv.v_type = VAR_BOOL;
item->di_tv.vval.v_number = regname == buf[0]
? VVAL_TRUE : VVAL_FALSE;
(void)dict_add(dict, item);
}
}
}
static void
return_register(int regname, typval_T *rettv)
{
char_u buf[2] = {0, 0};
buf[0] = (char_u)regname;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = vim_strsave(buf);
}
/*
* "reg_executing()" function
*/
static void
f_reg_executing(typval_T *argvars UNUSED, typval_T *rettv)
{
return_register(reg_executing, rettv);
}
/*
* "reg_recording()" function
*/
static void
f_reg_recording(typval_T *argvars UNUSED, typval_T *rettv)
{
return_register(reg_recording, rettv);
}
/*
* "rename({from}, {to})" function
*/
static void
f_rename(typval_T *argvars, typval_T *rettv)
{
char_u buf[NUMBUFLEN];
rettv->vval.v_number = -1;
if (check_restricted() || check_secure())
return;
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_string_arg(argvars, 1) == FAIL))
return;
rettv->vval.v_number = vim_rename(tv_get_string(&argvars[0]),
tv_get_string_buf(&argvars[1], buf));
}
/*
* "repeat()" function
*/
static void
f_repeat(typval_T *argvars, typval_T *rettv)
{
char_u *p;
varnumber_T n;
int slen;
int len;
char_u *r;
int i;
if (in_vim9script()
&& (check_for_string_or_number_or_list_or_blob_arg(argvars, 0)
== FAIL
|| check_for_number_arg(argvars, 1) == FAIL))
return;
n = tv_get_number(&argvars[1]);
if (argvars[0].v_type == VAR_LIST)
{
if (rettv_list_alloc(rettv) == OK && argvars[0].vval.v_list != NULL)
while (n-- > 0)
if (list_extend(rettv->vval.v_list,
argvars[0].vval.v_list, NULL) == FAIL)
break;
}
else if (argvars[0].v_type == VAR_BLOB)
{
if (rettv_blob_alloc(rettv) == FAIL
|| argvars[0].vval.v_blob == NULL
|| n <= 0)
return;
slen = argvars[0].vval.v_blob->bv_ga.ga_len;
len = (int)slen * n;
if (len <= 0)
return;
if (ga_grow(&rettv->vval.v_blob->bv_ga, len) == FAIL)
return;
rettv->vval.v_blob->bv_ga.ga_len = len;
for (i = 0; i < slen; ++i)
if (blob_get(argvars[0].vval.v_blob, i) != 0)
break;
if (i == slen)
// No need to copy since all bytes are already zero
return;
for (i = 0; i < n; ++i)
blob_set_range(rettv->vval.v_blob,
(long)i * slen, ((long)i + 1) * slen - 1, argvars);
}
else
{
p = tv_get_string(&argvars[0]);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
slen = (int)STRLEN(p);
len = slen * n;
if (len <= 0)
return;
r = alloc(len + 1);
if (r != NULL)
{
for (i = 0; i < n; i++)
mch_memmove(r + i * slen, p, (size_t)slen);
r[len] = NUL;
}
rettv->vval.v_string = r;
}
}
#define SP_NOMOVE 0x01 // don't move cursor
#define SP_REPEAT 0x02 // repeat to find outer pair
#define SP_RETCOUNT 0x04 // return matchcount
#define SP_SETPCMARK 0x08 // set previous context mark
#define SP_START 0x10 // accept match at start position
#define SP_SUBPAT 0x20 // return nr of matching sub-pattern
#define SP_END 0x40 // leave cursor at end of match
#define SP_COLUMN 0x80 // start at cursor column
/*
* Get flags for a search function.
* Possibly sets "p_ws".
* Returns BACKWARD, FORWARD or zero (for an error).
*/
static int
get_search_arg(typval_T *varp, int *flagsp)
{
int dir = FORWARD;
char_u *flags;
char_u nbuf[NUMBUFLEN];
int mask;
if (varp->v_type == VAR_UNKNOWN)
return FORWARD;
flags = tv_get_string_buf_chk(varp, nbuf);
if (flags == NULL)
return 0; // type error; errmsg already given
while (*flags != NUL)
{
switch (*flags)
{
case 'b': dir = BACKWARD; break;
case 'w': p_ws = TRUE; break;
case 'W': p_ws = FALSE; break;
default: mask = 0;
if (flagsp != NULL)
switch (*flags)
{
case 'c': mask = SP_START; break;
case 'e': mask = SP_END; break;
case 'm': mask = SP_RETCOUNT; break;
case 'n': mask = SP_NOMOVE; break;
case 'p': mask = SP_SUBPAT; break;
case 'r': mask = SP_REPEAT; break;
case 's': mask = SP_SETPCMARK; break;
case 'z': mask = SP_COLUMN; break;
}
if (mask == 0)
{
semsg(_(e_invalid_argument_str), flags);
dir = 0;
}
else
*flagsp |= mask;
}
if (dir == 0)
break;
++flags;
}
return dir;
}
/*
* Shared by search() and searchpos() functions.
*/
static int
search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
{
int flags;
char_u *pat;
pos_T pos;
pos_T save_cursor;
int save_p_ws = p_ws;
int dir;
int retval = 0; // default: FAIL
long lnum_stop = 0;
#ifdef FEAT_RELTIME
long time_limit = 0;
#endif
int options = SEARCH_KEEP;
int subpatnum;
searchit_arg_T sia;
int use_skip = FALSE;
pos_T firstpos;
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_opt_string_arg(argvars, 1) == FAIL
|| (argvars[1].v_type != VAR_UNKNOWN
&& (check_for_opt_number_arg(argvars, 2) == FAIL
|| (argvars[2].v_type != VAR_UNKNOWN
&& check_for_opt_number_arg(argvars, 3) == FAIL)))))
goto theend;
pat = tv_get_string(&argvars[0]);
dir = get_search_arg(&argvars[1], flagsp); // may set p_ws
if (dir == 0)
goto theend;
flags = *flagsp;
if (flags & SP_START)
options |= SEARCH_START;
if (flags & SP_END)
options |= SEARCH_END;
if (flags & SP_COLUMN)
options |= SEARCH_COL;
// Optional arguments: line number to stop searching, timeout and skip.
if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN)
{
lnum_stop = (long)tv_get_number_chk(&argvars[2], NULL);
if (lnum_stop < 0)
goto theend;
if (argvars[3].v_type != VAR_UNKNOWN)
{
#ifdef FEAT_RELTIME
time_limit = (long)tv_get_number_chk(&argvars[3], NULL);
if (time_limit < 0)
goto theend;
#endif
use_skip = eval_expr_valid_arg(&argvars[4]);
}
}
/*
* This function does not accept SP_REPEAT and SP_RETCOUNT flags.
* Check to make sure only those flags are set.
* Also, Only the SP_NOMOVE or the SP_SETPCMARK flag can be set. Both
* flags cannot be set. Check for that condition also.
*/
if (((flags & (SP_REPEAT | SP_RETCOUNT)) != 0)
|| ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK)))
{
semsg(_(e_invalid_argument_str), tv_get_string(&argvars[1]));
goto theend;
}
pos = save_cursor = curwin->w_cursor;
CLEAR_FIELD(firstpos);
CLEAR_FIELD(sia);
sia.sa_stop_lnum = (linenr_T)lnum_stop;
#ifdef FEAT_RELTIME
sia.sa_tm = time_limit;
#endif
// Repeat until {skip} returns FALSE.
for (;;)
{
subpatnum = searchit(curwin, curbuf, &pos, NULL, dir, pat, 1L,
options, RE_SEARCH, &sia);
// finding the first match again means there is no match where {skip}
// evaluates to zero.
if (firstpos.lnum != 0 && EQUAL_POS(pos, firstpos))
subpatnum = FAIL;
if (subpatnum == FAIL || !use_skip)
// didn't find it or no skip argument
break;
if (firstpos.lnum == 0)
firstpos = pos;
// If the skip expression matches, ignore this match.
{
int do_skip;
int err;
pos_T save_pos = curwin->w_cursor;
curwin->w_cursor = pos;
err = FALSE;
do_skip = eval_expr_to_bool(&argvars[4], &err);
curwin->w_cursor = save_pos;
if (err)
{
// Evaluating {skip} caused an error, break here.
subpatnum = FAIL;
break;
}
if (!do_skip)
break;
}
// clear the start flag to avoid getting stuck here
options &= ~SEARCH_START;
}
if (subpatnum != FAIL)
{
if (flags & SP_SUBPAT)
retval = subpatnum;
else
retval = pos.lnum;
if (flags & SP_SETPCMARK)
setpcmark();
curwin->w_cursor = pos;
if (match_pos != NULL)
{
// Store the match cursor position
match_pos->lnum = pos.lnum;
match_pos->col = pos.col + 1;
}
// "/$" will put the cursor after the end of the line, may need to
// correct that here
check_cursor();
}
// If 'n' flag is used: restore cursor position.
if (flags & SP_NOMOVE)
curwin->w_cursor = save_cursor;
else
curwin->w_set_curswant = TRUE;
theend:
p_ws = save_p_ws;
return retval;
}
#ifdef FEAT_RUBY
/*
* "rubyeval()" function
*/
static void
f_rubyeval(typval_T *argvars, typval_T *rettv)
{
char_u *str;
char_u buf[NUMBUFLEN];
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
str = tv_get_string_buf(&argvars[0], buf);
do_rubyeval(str, rettv);
}
#endif
/*
* "screenattr()" function
*/
static void
f_screenattr(typval_T *argvars, typval_T *rettv)
{
int row;
int col;
int c;
if (in_vim9script()
&& (check_for_number_arg(argvars, 0) == FAIL
|| check_for_number_arg(argvars, 1) == FAIL))
return;
row = (int)tv_get_number_chk(&argvars[0], NULL) - 1;
col = (int)tv_get_number_chk(&argvars[1], NULL) - 1;
if (row < 0 || row >= screen_Rows
|| col < 0 || col >= screen_Columns)
c = -1;
else
c = ScreenAttrs[LineOffset[row] + col];
rettv->vval.v_number = c;
}
/*
* "screenchar()" function
*/
static void
f_screenchar(typval_T *argvars, typval_T *rettv)
{
int row;
int col;
int c;
if (in_vim9script()
&& (check_for_number_arg(argvars, 0) == FAIL
|| check_for_number_arg(argvars, 1) == FAIL))
return;
row = (int)tv_get_number_chk(&argvars[0], NULL) - 1;
col = (int)tv_get_number_chk(&argvars[1], NULL) - 1;
if (row < 0 || row >= screen_Rows || col < 0 || col >= screen_Columns)
c = -1;
else
{
char_u buf[MB_MAXBYTES + 1];
screen_getbytes(row, col, buf, NULL);
c = (*mb_ptr2char)(buf);
}
rettv->vval.v_number = c;
}
/*
* "screenchars()" function
*/
static void
f_screenchars(typval_T *argvars, typval_T *rettv)
{
int row;
int col;
int c;
int i;
if (rettv_list_alloc(rettv) == FAIL)
return;
if (in_vim9script()
&& (check_for_number_arg(argvars, 0) == FAIL
|| check_for_number_arg(argvars, 1) == FAIL))
return;
row = (int)tv_get_number_chk(&argvars[0], NULL) - 1;
col = (int)tv_get_number_chk(&argvars[1], NULL) - 1;
if (row < 0 || row >= screen_Rows || col < 0 || col >= screen_Columns)
return;
char_u buf[MB_MAXBYTES + 1];
screen_getbytes(row, col, buf, NULL);
int pcc[MAX_MCO];
if (enc_utf8)
c = utfc_ptr2char(buf, pcc);
else
c = (*mb_ptr2char)(buf);
list_append_number(rettv->vval.v_list, (varnumber_T)c);
if (enc_utf8)
for (i = 0; i < Screen_mco && pcc[i] != 0; ++i)
list_append_number(rettv->vval.v_list, (varnumber_T)pcc[i]);
}
/*
* "screencol()" function
*
* First column is 1 to be consistent with virtcol().
*/
static void
f_screencol(typval_T *argvars UNUSED, typval_T *rettv)
{
rettv->vval.v_number = screen_screencol() + 1;
}
/*
* "screenrow()" function
*/
static void
f_screenrow(typval_T *argvars UNUSED, typval_T *rettv)
{
rettv->vval.v_number = screen_screenrow() + 1;
}
/*
* "screenstring()" function
*/
static void
f_screenstring(typval_T *argvars, typval_T *rettv)
{
int row;
int col;
char_u buf[MB_MAXBYTES + 1];
rettv->vval.v_string = NULL;
rettv->v_type = VAR_STRING;
if (in_vim9script()
&& (check_for_number_arg(argvars, 0) == FAIL
|| check_for_number_arg(argvars, 1) == FAIL))
return;
row = (int)tv_get_number_chk(&argvars[0], NULL) - 1;
col = (int)tv_get_number_chk(&argvars[1], NULL) - 1;
if (row < 0 || row >= screen_Rows || col < 0 || col >= screen_Columns)
return;
screen_getbytes(row, col, buf, NULL);
rettv->vval.v_string = vim_strsave(buf);
}
/*
* "search()" function
*/
static void
f_search(typval_T *argvars, typval_T *rettv)
{
int flags = 0;
rettv->vval.v_number = search_cmn(argvars, NULL, &flags);
}
/*
* "searchdecl()" function
*/
static void
f_searchdecl(typval_T *argvars, typval_T *rettv)
{
int locally = TRUE;
int thisblock = FALSE;
int error = FALSE;
char_u *name;
rettv->vval.v_number = 1; // default: FAIL
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_opt_bool_arg(argvars, 1) == FAIL
|| (argvars[1].v_type != VAR_UNKNOWN
&& check_for_opt_bool_arg(argvars, 2) == FAIL)))
return;
name = tv_get_string_chk(&argvars[0]);
if (argvars[1].v_type != VAR_UNKNOWN)
{
locally = !(int)tv_get_bool_chk(&argvars[1], &error);
if (!error && argvars[2].v_type != VAR_UNKNOWN)
thisblock = (int)tv_get_bool_chk(&argvars[2], &error);
}
if (!error && name != NULL)
rettv->vval.v_number = find_decl(name, (int)STRLEN(name),
locally, thisblock, SEARCH_KEEP) == FAIL;
}
/*
* Used by searchpair() and searchpairpos()
*/
static int
searchpair_cmn(typval_T *argvars, pos_T *match_pos)
{
char_u *spat, *mpat, *epat;
typval_T *skip;
int save_p_ws = p_ws;
int dir;
int flags = 0;
char_u nbuf1[NUMBUFLEN];
char_u nbuf2[NUMBUFLEN];
int retval = 0; // default: FAIL
long lnum_stop = 0;
long time_limit = 0;
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_string_arg(argvars, 1) == FAIL
|| check_for_string_arg(argvars, 2) == FAIL
|| check_for_opt_string_arg(argvars, 3) == FAIL
|| (argvars[3].v_type != VAR_UNKNOWN
&& argvars[4].v_type != VAR_UNKNOWN
&& (check_for_opt_number_arg(argvars, 5) == FAIL
|| (argvars[5].v_type != VAR_UNKNOWN
&& check_for_opt_number_arg(argvars, 6) == FAIL)))))
goto theend;
// Get the three pattern arguments: start, middle, end. Will result in an
// error if not a valid argument.
spat = tv_get_string_chk(&argvars[0]);
mpat = tv_get_string_buf_chk(&argvars[1], nbuf1);
epat = tv_get_string_buf_chk(&argvars[2], nbuf2);
if (spat == NULL || mpat == NULL || epat == NULL)
goto theend; // type error
// Handle the optional fourth argument: flags
dir = get_search_arg(&argvars[3], &flags); // may set p_ws
if (dir == 0)
goto theend;
// Don't accept SP_END or SP_SUBPAT.
// Only one of the SP_NOMOVE or SP_SETPCMARK flags can be set.
if ((flags & (SP_END | SP_SUBPAT)) != 0
|| ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK)))
{
semsg(_(e_invalid_argument_str), tv_get_string(&argvars[3]));
goto theend;
}
// Using 'r' implies 'W', otherwise it doesn't work.
if (flags & SP_REPEAT)
p_ws = FALSE;
// Optional fifth argument: skip expression
if (argvars[3].v_type == VAR_UNKNOWN
|| argvars[4].v_type == VAR_UNKNOWN)
skip = NULL;
else
{
// Type is checked later.
skip = &argvars[4];
if (argvars[5].v_type != VAR_UNKNOWN)
{
lnum_stop = (long)tv_get_number_chk(&argvars[5], NULL);
if (lnum_stop < 0)
{
semsg(_(e_invalid_argument_str), tv_get_string(&argvars[5]));
goto theend;
}
#ifdef FEAT_RELTIME
if (argvars[6].v_type != VAR_UNKNOWN)
{
time_limit = (long)tv_get_number_chk(&argvars[6], NULL);
if (time_limit < 0)
{
semsg(_(e_invalid_argument_str), tv_get_string(&argvars[6]));
goto theend;
}
}
#endif
}
}
retval = do_searchpair(spat, mpat, epat, dir, skip, flags,
match_pos, lnum_stop, time_limit);
theend:
p_ws = save_p_ws;
return retval;
}
/*
* "searchpair()" function
*/
static void
f_searchpair(typval_T *argvars, typval_T *rettv)
{
rettv->vval.v_number = searchpair_cmn(argvars, NULL);
}
/*
* "searchpairpos()" function
*/
static void
f_searchpairpos(typval_T *argvars, typval_T *rettv)
{
pos_T match_pos;
int lnum = 0;
int col = 0;
if (rettv_list_alloc(rettv) == FAIL)
return;
if (searchpair_cmn(argvars, &match_pos) > 0)
{
lnum = match_pos.lnum;
col = match_pos.col;
}
list_append_number(rettv->vval.v_list, (varnumber_T)lnum);
list_append_number(rettv->vval.v_list, (varnumber_T)col);
}
/*
* Search for a start/middle/end thing.
* Used by searchpair(), see its documentation for the details.
* Returns 0 or -1 for no match,
*/
long
do_searchpair(
char_u *spat, // start pattern
char_u *mpat, // middle pattern
char_u *epat, // end pattern
int dir, // BACKWARD or FORWARD
typval_T *skip, // skip expression
int flags, // SP_SETPCMARK and other SP_ values
pos_T *match_pos,
linenr_T lnum_stop, // stop at this line if not zero
long time_limit UNUSED) // stop after this many msec
{
char_u *save_cpo;
char_u *pat, *pat2 = NULL, *pat3 = NULL;
long retval = 0;
pos_T pos;
pos_T firstpos;
pos_T foundpos;
pos_T save_cursor;
pos_T save_pos;
int n;
int r;
int nest = 1;
int use_skip = FALSE;
int err;
int options = SEARCH_KEEP;
// Make 'cpoptions' empty, the 'l' flag should not be used here.
save_cpo = p_cpo;
p_cpo = empty_option;
// Make two search patterns: start/end (pat2, for in nested pairs) and
// start/middle/end (pat3, for the top pair).
pat2 = alloc(STRLEN(spat) + STRLEN(epat) + 17);
pat3 = alloc(STRLEN(spat) + STRLEN(mpat) + STRLEN(epat) + 25);
if (pat2 == NULL || pat3 == NULL)
goto theend;
sprintf((char *)pat2, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat);
if (*mpat == NUL)
STRCPY(pat3, pat2);
else
sprintf((char *)pat3, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)",
spat, epat, mpat);
if (flags & SP_START)
options |= SEARCH_START;
if (skip != NULL)
use_skip = eval_expr_valid_arg(skip);
#ifdef FEAT_RELTIME
if (time_limit > 0)
init_regexp_timeout(time_limit);
#endif
save_cursor = curwin->w_cursor;
pos = curwin->w_cursor;
CLEAR_POS(&firstpos);
CLEAR_POS(&foundpos);
pat = pat3;
for (;;)
{
searchit_arg_T sia;
CLEAR_FIELD(sia);
sia.sa_stop_lnum = lnum_stop;
n = searchit(curwin, curbuf, &pos, NULL, dir, pat, 1L,
options, RE_SEARCH, &sia);
if (n == FAIL || (firstpos.lnum != 0 && EQUAL_POS(pos, firstpos)))
// didn't find it or found the first match again: FAIL
break;
if (firstpos.lnum == 0)
firstpos = pos;
if (EQUAL_POS(pos, foundpos))
{
// Found the same position again. Can happen with a pattern that
// has "\zs" at the end and searching backwards. Advance one
// character and try again.
if (dir == BACKWARD)
decl(&pos);
else
incl(&pos);
}
foundpos = pos;
// clear the start flag to avoid getting stuck here
options &= ~SEARCH_START;
// If the skip pattern matches, ignore this match.
if (use_skip)
{
save_pos = curwin->w_cursor;
curwin->w_cursor = pos;
err = FALSE;
r = eval_expr_to_bool(skip, &err);
curwin->w_cursor = save_pos;
if (err)
{
// Evaluating {skip} caused an error, break here.
curwin->w_cursor = save_cursor;
retval = -1;
break;
}
if (r)
continue;
}
if ((dir == BACKWARD && n == 3) || (dir == FORWARD && n == 2))
{
// Found end when searching backwards or start when searching
// forward: nested pair.
++nest;
pat = pat2; // nested, don't search for middle
}
else
{
// Found end when searching forward or start when searching
// backward: end of (nested) pair; or found middle in outer pair.
if (--nest == 1)
pat = pat3; // outer level, search for middle
}
if (nest == 0)
{
// Found the match: return matchcount or line number.
if (flags & SP_RETCOUNT)
++retval;
else
retval = pos.lnum;
if (flags & SP_SETPCMARK)
setpcmark();
curwin->w_cursor = pos;
if (!(flags & SP_REPEAT))
break;
nest = 1; // search for next unmatched
}
}
if (match_pos != NULL)
{
// Store the match cursor position
match_pos->lnum = curwin->w_cursor.lnum;
match_pos->col = curwin->w_cursor.col + 1;
}
// If 'n' flag is used or search failed: restore cursor position.
if ((flags & SP_NOMOVE) || retval == 0)
curwin->w_cursor = save_cursor;
theend:
#ifdef FEAT_RELTIME
if (time_limit > 0)
disable_regexp_timeout();
#endif
vim_free(pat2);
vim_free(pat3);
if (p_cpo == empty_option)
p_cpo = save_cpo;
else
{
// Darn, evaluating the {skip} expression changed the value.
// If it's still empty it was changed and restored, need to restore in
// the complicated way.
if (*p_cpo == NUL)
set_option_value_give_err((char_u *)"cpo", 0L, save_cpo, 0);
free_string_option(save_cpo);
}
return retval;
}
/*
* "searchpos()" function
*/
static void
f_searchpos(typval_T *argvars, typval_T *rettv)
{
pos_T match_pos;
int lnum = 0;
int col = 0;
int n;
int flags = 0;
if (rettv_list_alloc(rettv) == FAIL)
return;
n = search_cmn(argvars, &match_pos, &flags);
if (n > 0)
{
lnum = match_pos.lnum;
col = match_pos.col;
}
list_append_number(rettv->vval.v_list, (varnumber_T)lnum);
list_append_number(rettv->vval.v_list, (varnumber_T)col);
if (flags & SP_SUBPAT)
list_append_number(rettv->vval.v_list, (varnumber_T)n);
}
/*
* Set the cursor or mark position.
* If "charpos" is TRUE, then use the column number as a character offset.
* Otherwise use the column number as a byte offset.
*/
static void
set_position(typval_T *argvars, typval_T *rettv, int charpos)
{
pos_T pos;
int fnum;
char_u *name;
colnr_T curswant = -1;
rettv->vval.v_number = -1;
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_list_arg(argvars, 1) == FAIL))
return;
name = tv_get_string_chk(argvars);
if (name == NULL)
return;
if (list2fpos(&argvars[1], &pos, &fnum, &curswant, charpos) != OK)
return;
if (pos.col != MAXCOL && --pos.col < 0)
pos.col = 0;
if ((name[0] == '.' && name[1] == NUL))
{
// set cursor; "fnum" is ignored
curwin->w_cursor = pos;
if (curswant >= 0)
{
curwin->w_curswant = curswant - 1;
curwin->w_set_curswant = FALSE;
}
check_cursor();
rettv->vval.v_number = 0;
}
else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL)
{
// set mark
if (setmark_pos(name[1], &pos, fnum) == OK)
rettv->vval.v_number = 0;
}
else
emsg(_(e_invalid_argument));
}
/*
* "setcharpos()" function
*/
static void
f_setcharpos(typval_T *argvars, typval_T *rettv)
{
set_position(argvars, rettv, TRUE);
}
static void
f_setcharsearch(typval_T *argvars, typval_T *rettv UNUSED)
{
dict_T *d;
dictitem_T *di;
char_u *csearch;
if (check_for_dict_arg(argvars, 0) == FAIL)
return;
if ((d = argvars[0].vval.v_dict) == NULL)
return;
csearch = dict_get_string(d, "char", FALSE);
if (csearch != NULL)
{
if (enc_utf8)
{
int pcc[MAX_MCO];
int c = utfc_ptr2char(csearch, pcc);
set_last_csearch(c, csearch, utfc_ptr2len(csearch));
}
else
set_last_csearch(PTR2CHAR(csearch),
csearch, mb_ptr2len(csearch));
}
di = dict_find(d, (char_u *)"forward", -1);
if (di != NULL)
set_csearch_direction((int)tv_get_number(&di->di_tv)
? FORWARD : BACKWARD);
di = dict_find(d, (char_u *)"until", -1);
if (di != NULL)
set_csearch_until(!!tv_get_number(&di->di_tv));
}
/*
* "setcursorcharpos" function
*/
static void
f_setcursorcharpos(typval_T *argvars, typval_T *rettv)
{
set_cursorpos(argvars, rettv, TRUE);
}
/*
* "setenv()" function
*/
static void
f_setenv(typval_T *argvars, typval_T *rettv UNUSED)
{
char_u namebuf[NUMBUFLEN];
char_u valbuf[NUMBUFLEN];
char_u *name;
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
// setting an environment variable may be dangerous, e.g. you could
// setenv GCONV_PATH=/tmp and then have iconv() unexpectedly call
// a shell command using some shared library:
if (check_restricted() || check_secure())
return;
name = tv_get_string_buf(&argvars[0], namebuf);
if (argvars[1].v_type == VAR_SPECIAL
&& argvars[1].vval.v_number == VVAL_NULL)
vim_unsetenv_ext(name);
else
vim_setenv_ext(name, tv_get_string_buf(&argvars[1], valbuf));
}
/*
* "setfperm({fname}, {mode})" function
*/
static void
f_setfperm(typval_T *argvars, typval_T *rettv)
{
char_u *fname;
char_u modebuf[NUMBUFLEN];
char_u *mode_str;
int i;
int mask;
int mode = 0;
rettv->vval.v_number = 0;
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_string_arg(argvars, 1) == FAIL))
return;
fname = tv_get_string_chk(&argvars[0]);
if (fname == NULL)
return;
mode_str = tv_get_string_buf_chk(&argvars[1], modebuf);
if (mode_str == NULL)
return;
if (STRLEN(mode_str) != 9)
{
semsg(_(e_invalid_argument_str), mode_str);
return;
}
mask = 1;
for (i = 8; i >= 0; --i)
{
if (mode_str[i] != '-')
mode |= mask;
mask = mask << 1;
}
rettv->vval.v_number = mch_setperm(fname, mode) == OK;
}
/*
* "setpos()" function
*/
static void
f_setpos(typval_T *argvars, typval_T *rettv)
{
set_position(argvars, rettv, FALSE);
}
/*
* Translate a register type string to the yank type and block length
*/
static int
get_yank_type(char_u **pp, char_u *yank_type, long *block_len)
{
char_u *stropt = *pp;
switch (*stropt)
{
case 'v': case 'c': // character-wise selection
*yank_type = MCHAR;
break;
case 'V': case 'l': // line-wise selection
*yank_type = MLINE;
break;
case 'b': case Ctrl_V: // block-wise selection
*yank_type = MBLOCK;
if (VIM_ISDIGIT(stropt[1]))
{
++stropt;
*block_len = getdigits(&stropt) - 1;
--stropt;
}
break;
default:
return FAIL;
}
*pp = stropt;
return OK;
}
/*
* "setreg()" function
*/
static void
f_setreg(typval_T *argvars, typval_T *rettv)
{
int regname;
char_u *strregname;
char_u *stropt;
char_u *strval;
int append;
char_u yank_type;
long block_len;
typval_T *regcontents;
int pointreg;
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_opt_string_arg(argvars, 2) == FAIL))
return;
pointreg = 0;
regcontents = NULL;
block_len = -1;
yank_type = MAUTO;
append = FALSE;
strregname = tv_get_string_chk(argvars);
rettv->vval.v_number = 1; // FAIL is default
if (strregname == NULL)
return; // type error; errmsg already given
if (in_vim9script() && STRLEN(strregname) > 1)
{
semsg(_(e_register_name_must_be_one_char_str), strregname);
return;
}
regname = *strregname;
if (regname == 0 || regname == '@')
regname = '"';
if (argvars[1].v_type == VAR_DICT)
{
dict_T *d = argvars[1].vval.v_dict;
dictitem_T *di;
if (d == NULL || d->dv_hashtab.ht_used == 0)
{
// Empty dict, clear the register (like setreg(0, []))
char_u *lstval[2] = {NULL, NULL};
write_reg_contents_lst(regname, lstval, 0, FALSE, MAUTO, -1);
return;
}
di = dict_find(d, (char_u *)"regcontents", -1);
if (di != NULL)
regcontents = &di->di_tv;
stropt = dict_get_string(d, "regtype", FALSE);
if (stropt != NULL)
{
int ret = get_yank_type(&stropt, &yank_type, &block_len);
if (ret == FAIL || *++stropt != NUL)
{
semsg(_(e_invalid_value_for_argument_str), "value");
return;
}
}
if (regname == '"')
{
stropt = dict_get_string(d, "points_to", FALSE);
if (stropt != NULL)
{
pointreg = *stropt;
regname = pointreg;
}
}
else if (dict_get_bool(d, "isunnamed", -1) > 0)
pointreg = regname;
}
else
regcontents = &argvars[1];
if (argvars[2].v_type != VAR_UNKNOWN)
{
if (yank_type != MAUTO)
{
semsg(_(e_too_many_arguments_for_function_str), "setreg");
return;
}
stropt = tv_get_string_chk(&argvars[2]);
if (stropt == NULL)
return; // type error
for (; *stropt != NUL; ++stropt)
switch (*stropt)
{
case 'a': case 'A': // append
append = TRUE;
break;
default:
get_yank_type(&stropt, &yank_type, &block_len);
}
}
if (regcontents && regcontents->v_type == VAR_LIST)
{
char_u **lstval;
char_u **allocval;
char_u buf[NUMBUFLEN];
char_u **curval;
char_u **curallocval;
list_T *ll = regcontents->vval.v_list;
listitem_T *li;
int len;
// If the list is NULL handle like an empty list.
len = ll == NULL ? 0 : ll->lv_len;
// First half: use for pointers to result lines; second half: use for
// pointers to allocated copies.
lstval = ALLOC_MULT(char_u *, (len + 1) * 2);
if (lstval == NULL)
return;
curval = lstval;
allocval = lstval + len + 2;
curallocval = allocval;
if (ll != NULL)
{
CHECK_LIST_MATERIALIZE(ll);
FOR_ALL_LIST_ITEMS(ll, li)
{
strval = tv_get_string_buf_chk(&li->li_tv, buf);
if (strval == NULL)
goto free_lstval;
if (strval == buf)
{
// Need to make a copy, next tv_get_string_buf_chk() will
// overwrite the string.
strval = vim_strsave(buf);
if (strval == NULL)
goto free_lstval;
*curallocval++ = strval;
}
*curval++ = strval;
}
}
*curval++ = NULL;
write_reg_contents_lst(regname, lstval, -1,
append, yank_type, block_len);
free_lstval:
while (curallocval > allocval)
vim_free(*--curallocval);
vim_free(lstval);
}
else if (regcontents)
{
strval = tv_get_string_chk(regcontents);
if (strval == NULL)
return;
write_reg_contents_ex(regname, strval, -1,
append, yank_type, block_len);
}
if (pointreg != 0)
get_yank_register(pointreg, TRUE);
rettv->vval.v_number = 0;
}
/*
* "settagstack()" function
*/
static void
f_settagstack(typval_T *argvars, typval_T *rettv)
{
win_T *wp;
dict_T *d;
int action = 'r';
rettv->vval.v_number = -1;
if (in_vim9script()
&& (check_for_number_arg(argvars, 0) == FAIL
|| check_for_dict_arg(argvars, 1) == FAIL
|| check_for_opt_string_arg(argvars, 2) == FAIL))
return;
// first argument: window number or id
wp = find_win_by_nr_or_id(&argvars[0]);
if (wp == NULL)
return;
// second argument: dict with items to set in the tag stack
if (check_for_dict_arg(argvars, 1) == FAIL)
return;
d = argvars[1].vval.v_dict;
if (d == NULL)
return;
// third argument: action - 'a' for append and 'r' for replace.
// default is to replace the stack.
if (argvars[2].v_type == VAR_UNKNOWN)
action = 'r';
else if (check_for_string_arg(argvars, 2) == FAIL)
return;
else
{
char_u *actstr;
actstr = tv_get_string_chk(&argvars[2]);
if (actstr == NULL)
return;
if ((*actstr == 'r' || *actstr == 'a' || *actstr == 't')
&& actstr[1] == NUL)
action = *actstr;
else
{
semsg(_(e_invalid_action_str_2), actstr);
return;
}
}
if (set_tagstack(wp, d, action) == OK)
rettv->vval.v_number = 0;
}
#ifdef FEAT_CRYPT
/*
* "sha256({string})" function
*/
static void
f_sha256(typval_T *argvars, typval_T *rettv)
{
char_u *p;
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
p = tv_get_string(&argvars[0]);
rettv->vval.v_string = vim_strsave(
sha256_bytes(p, (int)STRLEN(p), NULL, 0));
rettv->v_type = VAR_STRING;
}
#endif // FEAT_CRYPT
/*
* "shellescape({string})" function
*/
static void
f_shellescape(typval_T *argvars, typval_T *rettv)
{
int do_special;
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_opt_bool_arg(argvars, 1) == FAIL))
return;
do_special = non_zero_arg(&argvars[1]);
rettv->vval.v_string = vim_strsave_shellescape(
tv_get_string(&argvars[0]), do_special, do_special);
rettv->v_type = VAR_STRING;
}
/*
* shiftwidth() function
*/
static void
f_shiftwidth(typval_T *argvars UNUSED, typval_T *rettv)
{
rettv->vval.v_number = 0;
if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
return;
if (argvars[0].v_type != VAR_UNKNOWN)
{
long col;
col = (long)tv_get_number_chk(argvars, NULL);
if (col < 0)
return; // type error; errmsg already given
#ifdef FEAT_VARTABS
rettv->vval.v_number = get_sw_value_col(curbuf, col);
return;
#endif
}
rettv->vval.v_number = get_sw_value(curbuf);
}
/*
* "soundfold({word})" function
*/
static void
f_soundfold(typval_T *argvars, typval_T *rettv)
{
char_u *s;
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
rettv->v_type = VAR_STRING;
s = tv_get_string(&argvars[0]);
#ifdef FEAT_SPELL
rettv->vval.v_string = eval_soundfold(s);
#else
rettv->vval.v_string = vim_strsave(s);
#endif
}
/*
* "spellbadword()" function
*/
static void
f_spellbadword(typval_T *argvars UNUSED, typval_T *rettv)
{
char_u *word = (char_u *)"";
hlf_T attr = HLF_COUNT;
int len = 0;
#ifdef FEAT_SPELL
int wo_spell_save = curwin->w_p_spell;
if (in_vim9script() && check_for_opt_string_arg(argvars, 0) == FAIL)
return;
if (!curwin->w_p_spell)
{
parse_spelllang(curwin);
curwin->w_p_spell = TRUE;
}
if (*curwin->w_s->b_p_spl == NUL)
{
emsg(_(e_spell_checking_is_not_possible));
curwin->w_p_spell = wo_spell_save;
return;
}
#endif
if (rettv_list_alloc(rettv) == FAIL)
{
#ifdef FEAT_SPELL
curwin->w_p_spell = wo_spell_save;
#endif
return;
}
#ifdef FEAT_SPELL
if (argvars[0].v_type == VAR_UNKNOWN)
{
// Find the start and length of the badly spelled word.
len = spell_move_to(curwin, FORWARD, TRUE, TRUE, &attr);
if (len != 0)
{
word = ml_get_cursor();
curwin->w_set_curswant = TRUE;
}
}
else if (*curbuf->b_s.b_p_spl != NUL)
{
char_u *str = tv_get_string_chk(&argvars[0]);
int capcol = -1;
if (str != NULL)
{
// Check the argument for spelling.
while (*str != NUL)
{
len = spell_check(curwin, str, &attr, &capcol, FALSE);
if (attr != HLF_COUNT)
{
word = str;
break;
}
str += len;
capcol -= len;
len = 0;
}
}
}
curwin->w_p_spell = wo_spell_save;
#endif
list_append_string(rettv->vval.v_list, word, len);
list_append_string(rettv->vval.v_list, (char_u *)(
attr == HLF_SPB ? "bad" :
attr == HLF_SPR ? "rare" :
attr == HLF_SPL ? "local" :
attr == HLF_SPC ? "caps" :
""), -1);
}
/*
* "spellsuggest()" function
*/
static void
f_spellsuggest(typval_T *argvars UNUSED, typval_T *rettv)
{
#ifdef FEAT_SPELL
char_u *str;
int typeerr = FALSE;
int maxcount;
garray_T ga;
int i;
listitem_T *li;
int need_capital = FALSE;
int wo_spell_save = curwin->w_p_spell;
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_opt_number_arg(argvars, 1) == FAIL
|| (argvars[1].v_type != VAR_UNKNOWN
&& check_for_opt_bool_arg(argvars, 2) == FAIL)))
return;
if (!curwin->w_p_spell)
{
parse_spelllang(curwin);
curwin->w_p_spell = TRUE;
}
if (*curwin->w_s->b_p_spl == NUL)
{
emsg(_(e_spell_checking_is_not_possible));
curwin->w_p_spell = wo_spell_save;
return;
}
#endif
if (rettv_list_alloc(rettv) == FAIL)
{
#ifdef FEAT_SPELL
curwin->w_p_spell = wo_spell_save;
#endif
return;
}
#ifdef FEAT_SPELL
str = tv_get_string(&argvars[0]);
if (argvars[1].v_type != VAR_UNKNOWN)
{
maxcount = (int)tv_get_number_chk(&argvars[1], &typeerr);
if (maxcount <= 0)
return;
if (argvars[2].v_type != VAR_UNKNOWN)
{
need_capital = (int)tv_get_bool_chk(&argvars[2], &typeerr);
if (typeerr)
return;
}
}
else
maxcount = 25;
spell_suggest_list(&ga, str, maxcount, need_capital, FALSE);
for (i = 0; i < ga.ga_len; ++i)
{
str = ((char_u **)ga.ga_data)[i];
li = listitem_alloc();
if (li == NULL)
vim_free(str);
else
{
li->li_tv.v_type = VAR_STRING;
li->li_tv.v_lock = 0;
li->li_tv.vval.v_string = str;
list_append(rettv->vval.v_list, li);
}
}
ga_clear(&ga);
curwin->w_p_spell = wo_spell_save;
#endif
}
static void
f_split(typval_T *argvars, typval_T *rettv)
{
char_u *str;
char_u *end;
char_u *pat = NULL;
regmatch_T regmatch;
char_u patbuf[NUMBUFLEN];
char_u *save_cpo;
int match;
colnr_T col = 0;
int keepempty = FALSE;
int typeerr = FALSE;
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_opt_string_arg(argvars, 1) == FAIL
|| (argvars[1].v_type != VAR_UNKNOWN
&& check_for_opt_bool_arg(argvars, 2) == FAIL)))
return;
// Make 'cpoptions' empty, the 'l' flag should not be used here.
save_cpo = p_cpo;
p_cpo = empty_option;
str = tv_get_string(&argvars[0]);
if (argvars[1].v_type != VAR_UNKNOWN)
{
pat = tv_get_string_buf_chk(&argvars[1], patbuf);
if (pat == NULL)
typeerr = TRUE;
if (argvars[2].v_type != VAR_UNKNOWN)
keepempty = (int)tv_get_bool_chk(&argvars[2], &typeerr);
}
if (pat == NULL || *pat == NUL)
pat = (char_u *)"[\\x01- ]\\+";
if (rettv_list_alloc(rettv) == FAIL)
goto theend;
if (typeerr)
goto theend;
regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog != NULL)
{
regmatch.rm_ic = FALSE;
while (*str != NUL || keepempty)
{
if (*str == NUL)
match = FALSE; // empty item at the end
else
match = vim_regexec_nl(&regmatch, str, col);
if (match)
end = regmatch.startp[0];
else
end = str + STRLEN(str);
if (keepempty || end > str || (rettv->vval.v_list->lv_len > 0
&& *str != NUL && match && end < regmatch.endp[0]))
{
if (list_append_string(rettv->vval.v_list, str,
(int)(end - str)) == FAIL)
break;
}
if (!match)
break;
// Advance to just after the match.
if (regmatch.endp[0] > str)
col = 0;
else
// Don't get stuck at the same match.
col = (*mb_ptr2len)(regmatch.endp[0]);
str = regmatch.endp[0];
}
vim_regfree(regmatch.regprog);
}
theend:
p_cpo = save_cpo;
}
/*
* "submatch()" function
*/
static void
f_submatch(typval_T *argvars, typval_T *rettv)
{
int error = FALSE;
int no;
int retList = 0;
if (in_vim9script()
&& (check_for_number_arg(argvars, 0) == FAIL
|| check_for_opt_bool_arg(argvars, 1) == FAIL))
return;
no = (int)tv_get_number_chk(&argvars[0], &error);
if (error)
return;
if (no < 0 || no >= NSUBEXP)
{
semsg(_(e_invalid_submatch_number_nr), no);
return;
}
if (argvars[1].v_type != VAR_UNKNOWN)
retList = (int)tv_get_bool_chk(&argvars[1], &error);
if (error)
return;
if (retList == 0)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = reg_submatch(no);
}
else
{
rettv->v_type = VAR_LIST;
rettv->vval.v_list = reg_submatch_list(no);
}
}
/*
* "substitute()" function
*/
static void
f_substitute(typval_T *argvars, typval_T *rettv)
{
char_u patbuf[NUMBUFLEN];
char_u subbuf[NUMBUFLEN];
char_u flagsbuf[NUMBUFLEN];
char_u *str;
char_u *pat;
char_u *sub = NULL;
typval_T *expr = NULL;
char_u *flg;
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_string_arg(argvars, 1) == FAIL
|| check_for_string_arg(argvars, 3) == FAIL))
return;
str = tv_get_string_chk(&argvars[0]);
pat = tv_get_string_buf_chk(&argvars[1], patbuf);
flg = tv_get_string_buf_chk(&argvars[3], flagsbuf);
if (argvars[2].v_type == VAR_FUNC
|| argvars[2].v_type == VAR_PARTIAL
|| argvars[2].v_type == VAR_INSTR
|| argvars[2].v_type == VAR_CLASS
|| argvars[2].v_type == VAR_OBJECT)
expr = &argvars[2];
else
sub = tv_get_string_buf_chk(&argvars[2], subbuf);
rettv->v_type = VAR_STRING;
if (str == NULL || pat == NULL || (sub == NULL && expr == NULL)
|| flg == NULL)
rettv->vval.v_string = NULL;
else
rettv->vval.v_string = do_string_sub(str, pat, sub, expr, flg);
}
/*
* "swapfilelist()" function
*/
static void
f_swapfilelist(typval_T *argvars UNUSED, typval_T *rettv)
{
if (rettv_list_alloc(rettv) == FAIL)
return;
recover_names(NULL, FALSE, rettv->vval.v_list, 0, NULL);
}
/*
* "swapinfo(swap_filename)" function
*/
static void
f_swapinfo(typval_T *argvars, typval_T *rettv)
{
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
if (rettv_dict_alloc(rettv) == OK)
get_b0_dict(tv_get_string(argvars), rettv->vval.v_dict);
}
/*
* "swapname(expr)" function
*/
static void
f_swapname(typval_T *argvars, typval_T *rettv)
{
buf_T *buf;
rettv->v_type = VAR_STRING;
if (in_vim9script() && check_for_buffer_arg(argvars, 0) == FAIL)
return;
buf = tv_get_buf(&argvars[0], FALSE);
if (buf == NULL || buf->b_ml.ml_mfp == NULL
|| buf->b_ml.ml_mfp->mf_fname == NULL)
rettv->vval.v_string = NULL;
else
rettv->vval.v_string = vim_strsave(buf->b_ml.ml_mfp->mf_fname);
}
/*
* "synID(lnum, col, trans)" function
*/
static void
f_synID(typval_T *argvars UNUSED, typval_T *rettv)
{
int id = 0;
#ifdef FEAT_SYN_HL
linenr_T lnum;
colnr_T col;
int trans;
int transerr = FALSE;
if (in_vim9script()
&& (check_for_lnum_arg(argvars, 0) == FAIL
|| check_for_number_arg(argvars, 1) == FAIL
|| check_for_bool_arg(argvars, 2) == FAIL))
return;
lnum = tv_get_lnum(argvars); // -1 on type error
col = (linenr_T)tv_get_number(&argvars[1]) - 1; // -1 on type error
trans = (int)tv_get_bool_chk(&argvars[2], &transerr);
if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count
&& col >= 0 && col < (long)STRLEN(ml_get(lnum)))
id = syn_get_id(curwin, lnum, col, trans, NULL, FALSE);
#endif
rettv->vval.v_number = id;
}
/*
* "synIDattr(id, what [, mode])" function
*/
static void
f_synIDattr(typval_T *argvars UNUSED, typval_T *rettv)
{
char_u *p = NULL;
#ifdef FEAT_SYN_HL
int id;
char_u *what;
char_u *mode;
char_u modebuf[NUMBUFLEN];
int modec;
if (in_vim9script()
&& (check_for_number_arg(argvars, 0) == FAIL
|| check_for_string_arg(argvars, 1) == FAIL
|| check_for_opt_string_arg(argvars, 2) == FAIL))
return;
id = (int)tv_get_number(&argvars[0]);
what = tv_get_string(&argvars[1]);
if (argvars[2].v_type != VAR_UNKNOWN)
{
mode = tv_get_string_buf(&argvars[2], modebuf);
modec = TOLOWER_ASC(mode[0]);
if (modec != 't' && modec != 'c' && modec != 'g')
modec = 0; // replace invalid with current
}
else
{
#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
if (USE_24BIT)
modec = 'g';
else
#endif
if (t_colors > 1)
modec = 'c';
else
modec = 't';
}
switch (TOLOWER_ASC(what[0]))
{
case 'b':
if (TOLOWER_ASC(what[1]) == 'g') // bg[#]
p = highlight_color(id, what, modec);
else // bold
p = highlight_has_attr(id, HL_BOLD, modec);
break;
case 'f': // fg[#] or font
p = highlight_color(id, what, modec);
break;
case 'i':
if (TOLOWER_ASC(what[1]) == 'n') // inverse
p = highlight_has_attr(id, HL_INVERSE, modec);
else // italic
p = highlight_has_attr(id, HL_ITALIC, modec);
break;
case 'n':
if (TOLOWER_ASC(what[1]) == 'o') // nocombine
p = highlight_has_attr(id, HL_NOCOMBINE, modec);
else // name
p = get_highlight_name_ext(NULL, id - 1, FALSE);
break;
case 'r': // reverse
p = highlight_has_attr(id, HL_INVERSE, modec);
break;
case 's':
if (TOLOWER_ASC(what[1]) == 'p') // sp[#]
p = highlight_color(id, what, modec);
// strikeout
else if (TOLOWER_ASC(what[1]) == 't' &&
TOLOWER_ASC(what[2]) == 'r')
p = highlight_has_attr(id, HL_STRIKETHROUGH, modec);
else // standout
p = highlight_has_attr(id, HL_STANDOUT, modec);
break;
case 'u':
if (STRLEN(what) >= 9)
{
if (TOLOWER_ASC(what[5]) == 'l')
// underline
p = highlight_has_attr(id, HL_UNDERLINE, modec);
else if (TOLOWER_ASC(what[5]) != 'd')
// undercurl
p = highlight_has_attr(id, HL_UNDERCURL, modec);
else if (TOLOWER_ASC(what[6]) != 'o')
// underdashed
p = highlight_has_attr(id, HL_UNDERDASHED, modec);
else if (TOLOWER_ASC(what[7]) == 'u')
// underdouble
p = highlight_has_attr(id, HL_UNDERDOUBLE, modec);
else
// underdotted
p = highlight_has_attr(id, HL_UNDERDOTTED, modec);
}
else
// ul
p = highlight_color(id, what, modec);
break;
}
if (p != NULL)
p = vim_strsave(p);
#endif
rettv->v_type = VAR_STRING;
rettv->vval.v_string = p;
}
/*
* "synIDtrans(id)" function
*/
static void
f_synIDtrans(typval_T *argvars UNUSED, typval_T *rettv)
{
int id;
#ifdef FEAT_SYN_HL
if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL)
return;
id = (int)tv_get_number(&argvars[0]);
if (id > 0)
id = syn_get_final_id(id);
else
#endif
id = 0;
rettv->vval.v_number = id;
}
/*
* "synconcealed(lnum, col)" function
*/
static void
f_synconcealed(typval_T *argvars UNUSED, typval_T *rettv)
{
#if defined(FEAT_SYN_HL) && defined(FEAT_CONCEAL)
linenr_T lnum;
colnr_T col;
int syntax_flags = 0;
int cchar;
int matchid = 0;
char_u str[NUMBUFLEN];
#endif
rettv_list_set(rettv, NULL);
if (in_vim9script()
&& (check_for_lnum_arg(argvars, 0) == FAIL
|| check_for_number_arg(argvars, 1) == FAIL))
return;
#if defined(FEAT_SYN_HL) && defined(FEAT_CONCEAL)
lnum = tv_get_lnum(argvars); // -1 on type error
col = (colnr_T)tv_get_number(&argvars[1]) - 1; // -1 on type error
CLEAR_FIELD(str);
if (rettv_list_alloc(rettv) == OK)
{
if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count
&& col >= 0 && col <= (long)STRLEN(ml_get(lnum))
&& curwin->w_p_cole > 0)
{
(void)syn_get_id(curwin, lnum, col, FALSE, NULL, FALSE);
syntax_flags = get_syntax_info(&matchid);
// get the conceal character
if ((syntax_flags & HL_CONCEAL) && curwin->w_p_cole < 3)
{
cchar = syn_get_sub_char();
if (cchar == NUL && curwin->w_p_cole == 1)
cchar = (curwin->w_lcs_chars.conceal == NUL) ? ' '
: curwin->w_lcs_chars.conceal;
if (cchar != NUL)
{
if (has_mbyte)
(*mb_char2bytes)(cchar, str);
else
str[0] = cchar;
}
}
}
list_append_number(rettv->vval.v_list,
(syntax_flags & HL_CONCEAL) != 0);
// -1 to auto-determine strlen
list_append_string(rettv->vval.v_list, str, -1);
list_append_number(rettv->vval.v_list, matchid);
}
#endif
}
/*
* "synstack(lnum, col)" function
*/
static void
f_synstack(typval_T *argvars UNUSED, typval_T *rettv)
{
#ifdef FEAT_SYN_HL
linenr_T lnum;
colnr_T col;
int i;
int id;
#endif
rettv_list_set(rettv, NULL);
if (in_vim9script()
&& (check_for_lnum_arg(argvars, 0) == FAIL
|| check_for_number_arg(argvars, 1) == FAIL))
return;
#ifdef FEAT_SYN_HL
lnum = tv_get_lnum(argvars); // -1 on type error
col = (colnr_T)tv_get_number(&argvars[1]) - 1; // -1 on type error
if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count
&& col >= 0 && col <= (long)STRLEN(ml_get(lnum))
&& rettv_list_alloc(rettv) == OK)
{
(void)syn_get_id(curwin, lnum, col, FALSE, NULL, TRUE);
for (i = 0; ; ++i)
{
id = syn_get_stack_item(i);
if (id < 0)
break;
if (list_append_number(rettv->vval.v_list, id) == FAIL)
break;
}
}
#endif
}
/*
* "tabpagebuflist()" function
*/
static void
f_tabpagebuflist(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
{
tabpage_T *tp;
win_T *wp = NULL;
if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
return;
if (argvars[0].v_type == VAR_UNKNOWN)
wp = firstwin;
else
{
tp = find_tabpage((int)tv_get_number(&argvars[0]));
if (tp != NULL)
wp = (tp == curtab) ? firstwin : tp->tp_firstwin;
}
if (wp != NULL && rettv_list_alloc(rettv) == OK)
{
for (; wp != NULL; wp = wp->w_next)
if (list_append_number(rettv->vval.v_list,
wp->w_buffer->b_fnum) == FAIL)
break;
}
}
/*
* "tagfiles()" function
*/
static void
f_tagfiles(typval_T *argvars UNUSED, typval_T *rettv)
{
char_u *fname;
tagname_T tn;
int first;
if (rettv_list_alloc(rettv) == FAIL)
return;
fname = alloc(MAXPATHL);
if (fname == NULL)
return;
for (first = TRUE; ; first = FALSE)
if (get_tagfname(&tn, first, fname) == FAIL
|| list_append_string(rettv->vval.v_list, fname, -1) == FAIL)
break;
tagname_free(&tn);
vim_free(fname);
}
/*
* "taglist()" function
*/
static void
f_taglist(typval_T *argvars, typval_T *rettv)
{
char_u *fname = NULL;
char_u *tag_pattern;
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_opt_string_arg(argvars, 1) == FAIL))
return;
tag_pattern = tv_get_string(&argvars[0]);
rettv->vval.v_number = FALSE;
if (*tag_pattern == NUL)
return;
if (argvars[1].v_type != VAR_UNKNOWN)
fname = tv_get_string(&argvars[1]);
if (rettv_list_alloc(rettv) == OK)
(void)get_tags(rettv->vval.v_list, tag_pattern, fname);
}
/*
* "type(expr)" function
*/
static void
f_type(typval_T *argvars, typval_T *rettv)
{
int n = -1;
switch (argvars[0].v_type)
{
case VAR_NUMBER: n = VAR_TYPE_NUMBER; break;
case VAR_STRING: n = VAR_TYPE_STRING; break;
case VAR_PARTIAL:
case VAR_FUNC: n = VAR_TYPE_FUNC; break;
case VAR_LIST: n = VAR_TYPE_LIST; break;
case VAR_DICT: n = VAR_TYPE_DICT; break;
case VAR_FLOAT: n = VAR_TYPE_FLOAT; break;
case VAR_BOOL: n = VAR_TYPE_BOOL; break;
case VAR_SPECIAL: n = VAR_TYPE_NONE; break;
case VAR_JOB: n = VAR_TYPE_JOB; break;
case VAR_CHANNEL: n = VAR_TYPE_CHANNEL; break;
case VAR_BLOB: n = VAR_TYPE_BLOB; break;
case VAR_INSTR: n = VAR_TYPE_INSTR; break;
case VAR_CLASS: n = VAR_TYPE_CLASS; break;
case VAR_OBJECT: n = VAR_TYPE_OBJECT; break;
case VAR_TYPEALIAS: n = VAR_TYPE_TYPEALIAS; break;
case VAR_UNKNOWN:
case VAR_ANY:
case VAR_VOID:
internal_error_no_abort("f_type(UNKNOWN)");
n = -1;
break;
}
rettv->vval.v_number = n;
}
/*
* "virtcol({expr}, [, {list} [, {winid}]])" function
*/
static void
f_virtcol(typval_T *argvars, typval_T *rettv)
{
colnr_T vcol_start = 0;
colnr_T vcol_end = 0;
pos_T *fp;
switchwin_T switchwin;
int winchanged = FALSE;
int len;
if (in_vim9script()
&& (check_for_string_or_list_arg(argvars, 0) == FAIL
|| (argvars[1].v_type != VAR_UNKNOWN
&& (check_for_bool_arg(argvars, 1) == FAIL
|| check_for_opt_number_arg(argvars, 2) == FAIL))))
return;
if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN)
{
tabpage_T *tp;
win_T *wp;
// use the window specified in the third argument
wp = win_id2wp_tp((int)tv_get_number(&argvars[2]), &tp);
if (wp == NULL || tp == NULL)
goto theend;
if (switch_win_noblock(&switchwin, wp, tp, TRUE) != OK)
goto theend;
check_cursor();
winchanged = TRUE;
}
int fnum = curbuf->b_fnum;
fp = var2fpos(&argvars[0], FALSE, &fnum, FALSE);
if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count
&& fnum == curbuf->b_fnum)
{
// Limit the column to a valid value, getvvcol() doesn't check.
if (fp->col < 0)
fp->col = 0;
else
{
len = (int)STRLEN(ml_get(fp->lnum));
if (fp->col > len)
fp->col = len;
}
getvvcol(curwin, fp, &vcol_start, NULL, &vcol_end);
++vcol_start;
++vcol_end;
}
theend:
if (argvars[1].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[1]))
{
if (rettv_list_alloc(rettv) == OK)
{
list_append_number(rettv->vval.v_list, vcol_start);
list_append_number(rettv->vval.v_list, vcol_end);
}
else
rettv->vval.v_number = 0;
}
else
rettv->vval.v_number = vcol_end;
if (winchanged)
restore_win_noblock(&switchwin, TRUE);
}
/*
* "visualmode()" function
*/
static void
f_visualmode(typval_T *argvars, typval_T *rettv)
{
char_u str[2];
if (in_vim9script() && check_for_opt_bool_arg(argvars, 0) == FAIL)
return;
rettv->v_type = VAR_STRING;
str[0] = curbuf->b_visual_mode_eval;
str[1] = NUL;
rettv->vval.v_string = vim_strsave(str);
// A non-zero number or non-empty string argument: reset mode.
if (non_zero_arg(&argvars[0]))
curbuf->b_visual_mode_eval = NUL;
}
/*
* "wildmenumode()" function
*/
static void
f_wildmenumode(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
{
if (wild_menu_showing || ((State & MODE_CMDLINE) && cmdline_pum_active()))
rettv->vval.v_number = 1;
}
/*
* "windowsversion()" function
*/
static void
f_windowsversion(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = vim_strsave((char_u *)windowsVersion);
}
/*
* "wordcount()" function
*/
static void
f_wordcount(typval_T *argvars UNUSED, typval_T *rettv)
{
if (rettv_dict_alloc(rettv) == FAIL)
return;
cursor_pos_info(rettv->vval.v_dict);
}
/*
* "xor(expr, expr)" function
*/
static void
f_xor(typval_T *argvars, typval_T *rettv)
{
if (in_vim9script()
&& (check_for_number_arg(argvars, 0) == FAIL
|| check_for_number_arg(argvars, 1) == FAIL))
return;
rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL)
^ tv_get_number_chk(&argvars[1], NULL);
}
#endif // FEAT_EVAL