mirror of
https://github.com/vim/vim
synced 2025-03-19 16:25:11 +01:00
Problem: wrong return type of blob2str() Solution: update return to list of string (Yegappan Lakshmanan) closes: #16461 Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
12309 lines
301 KiB
C
12309 lines
301 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_base64_encode(typval_T *argvars, typval_T *rettv);
|
|
static void f_base64_decode(typval_T *argvars, typval_T *rettv);
|
|
static void f_bindtextdomain(typval_T *argvars, typval_T *rettv);
|
|
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_getcellpixels(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_getregionpos(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_id(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->tt_type == VAR_OBJECT
|
|
|| 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_list_string_dict[] = {arg_list_string, arg_dict_any};
|
|
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_buffer[] = {arg_number, arg_buffer};
|
|
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 arg2_blob_dict[] = {arg_blob, arg_dict_any};
|
|
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_list_regionpos(int argcount UNUSED,
|
|
type2_T *argtypes UNUSED,
|
|
type_T **decl_type)
|
|
{
|
|
*decl_type = &t_list_any;
|
|
return &t_list_list_list_number;
|
|
}
|
|
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
|
|
},
|
|
{"base64_decode", 1, 1, FEARG_1, arg1_string,
|
|
ret_blob, f_base64_decode},
|
|
{"base64_encode", 1, 1, FEARG_1, arg1_blob,
|
|
ret_string, f_base64_encode},
|
|
{"bindtextdomain", 2, 2, 0, arg2_string,
|
|
ret_bool, f_bindtextdomain},
|
|
{"blob2list", 1, 1, FEARG_1, arg1_blob,
|
|
ret_list_number, f_blob2list},
|
|
{"blob2str", 1, 2, FEARG_1, arg2_blob_dict,
|
|
ret_list_string, f_blob2str},
|
|
{"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},
|
|
{"filecopy", 2, 2, FEARG_1, arg2_string,
|
|
ret_number_bool, f_filecopy},
|
|
{"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},
|
|
{"getcellpixels", 0, 0, 0, NULL,
|
|
ret_list_any, f_getcellpixels},
|
|
{"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},
|
|
{"getcmdcomplpat", 0, 0, 0, NULL,
|
|
ret_string, f_getcmdcomplpat},
|
|
{"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},
|
|
{"getcmdprompt", 0, 0, 0, NULL,
|
|
ret_string, f_getcmdprompt},
|
|
{"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},
|
|
{"getregionpos", 2, 3, FEARG_1, arg3_list_list_dict,
|
|
ret_list_regionpos, f_getregionpos},
|
|
{"getregtype", 0, 1, FEARG_1, arg1_string,
|
|
ret_string, f_getregtype},
|
|
{"getscriptinfo", 0, 1, 0, arg1_dict_any,
|
|
ret_list_dict_any, f_getscriptinfo},
|
|
{"getstacktrace", 0, 0, 0, NULL,
|
|
ret_list_dict_any, f_getstacktrace},
|
|
{"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, 2, FEARG_1, arg2_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},
|
|
{"id", 1, 1, FEARG_1, NULL,
|
|
ret_string, f_id},
|
|
{"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_setbuf", 2, 2, FEARG_1, arg2_number_buffer,
|
|
ret_number_bool, PROP_FUNC(f_popup_setbuf)},
|
|
{"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, 2, FEARG_1, arg2_string_dict,
|
|
ret_any,
|
|
#ifdef FEAT_PYTHON3
|
|
f_py3eval
|
|
#else
|
|
NULL
|
|
#endif
|
|
},
|
|
{"pyeval", 1, 2, FEARG_1, arg2_string_dict,
|
|
ret_any,
|
|
#ifdef FEAT_PYTHON
|
|
f_pyeval
|
|
#else
|
|
NULL
|
|
#endif
|
|
},
|
|
{"pyxeval", 1, 2, FEARG_1, arg2_string_dict,
|
|
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},
|
|
{"str2blob", 1, 2, FEARG_1, arg2_list_string_dict,
|
|
ret_blob, f_str2blob},
|
|
{"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
|
|
|
|
// Base64 character set
|
|
static const char_u base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
|
|
// Base64 decoding table (initialized in init_base64_dec_table() below)
|
|
static char_u base64_dec_table[256];
|
|
|
|
/*
|
|
* Initialize the base64 decoding table
|
|
*/
|
|
static void
|
|
init_base64_dec_table(void)
|
|
{
|
|
static int base64_dec_tbl_initialized = FALSE;
|
|
|
|
if (base64_dec_tbl_initialized)
|
|
return;
|
|
|
|
// Unsupported characters are set to 0xFF
|
|
vim_memset(base64_dec_table, 0xFF, sizeof(base64_dec_table));
|
|
|
|
// Initialize the index for the base64 alphabets
|
|
for (size_t i = 0; i < sizeof(base64_table) - 1; i++)
|
|
base64_dec_table[(char_u)base64_table[i]] = (char_u)i;
|
|
|
|
// base64 padding character
|
|
base64_dec_table['='] = 0;
|
|
|
|
base64_dec_tbl_initialized = TRUE;
|
|
}
|
|
|
|
/*
|
|
* Encode the bytes in "blob" using base-64 encoding.
|
|
*/
|
|
static char_u *
|
|
base64_encode(blob_T *blob)
|
|
{
|
|
size_t input_len = blob->bv_ga.ga_len;
|
|
size_t encoded_len = ((input_len + 2) / 3) * 4;
|
|
char_u *data = blob->bv_ga.ga_data;
|
|
|
|
char_u *encoded = alloc(encoded_len + 1);
|
|
if (encoded == NULL)
|
|
return NULL;
|
|
|
|
size_t i, j;
|
|
for (i = 0, j = 0; i < input_len;)
|
|
{
|
|
int_u octet_a = i < input_len ? data[i++] : 0;
|
|
int_u octet_b = i < input_len ? data[i++] : 0;
|
|
int_u octet_c = i < input_len ? data[i++] : 0;
|
|
|
|
int_u triple = (octet_a << 16) | (octet_b << 8) | octet_c;
|
|
|
|
encoded[j++] = base64_table[(triple >> 18) & 0x3F];
|
|
encoded[j++] = base64_table[(triple >> 12) & 0x3F];
|
|
encoded[j++] = (!octet_b && i >= input_len) ? '='
|
|
: base64_table[(triple >> 6) & 0x3F];
|
|
encoded[j++] = (!octet_c && i >= input_len) ? '='
|
|
: base64_table[triple & 0x3F];
|
|
}
|
|
encoded[j] = NUL;
|
|
|
|
return encoded;
|
|
}
|
|
|
|
/*
|
|
* Decode the string "data" using base-64 encoding.
|
|
*/
|
|
static void
|
|
base64_decode(const char_u *data, blob_T *blob)
|
|
{
|
|
size_t input_len = STRLEN(data);
|
|
|
|
if (input_len == 0)
|
|
return;
|
|
|
|
if (input_len % 4 != 0)
|
|
{
|
|
// Invalid input length
|
|
semsg(_(e_invalid_argument_str), data);
|
|
return;
|
|
}
|
|
|
|
init_base64_dec_table();
|
|
|
|
size_t decoded_len = (input_len / 4) * 3;
|
|
if (data[input_len - 1] == '=')
|
|
decoded_len--;
|
|
if (data[input_len - 2] == '=')
|
|
decoded_len--;
|
|
|
|
size_t i, j;
|
|
for (i = 0, j = 0; i < input_len;)
|
|
{
|
|
int_u sextet_a = base64_dec_table[(char_u)data[i++]];
|
|
int_u sextet_b = base64_dec_table[(char_u)data[i++]];
|
|
int_u sextet_c = base64_dec_table[(char_u)data[i++]];
|
|
int_u sextet_d = base64_dec_table[(char_u)data[i++]];
|
|
|
|
if (sextet_a == 0xFF || sextet_b == 0xFF || sextet_c == 0xFF
|
|
|| sextet_d == 0xFF)
|
|
{
|
|
// Invalid character
|
|
semsg(_(e_invalid_argument_str), data);
|
|
ga_clear(&blob->bv_ga);
|
|
return;
|
|
}
|
|
|
|
int_u triple = (sextet_a << 18) | (sextet_b << 12)
|
|
| (sextet_c << 6) | sextet_d;
|
|
|
|
if (j < decoded_len)
|
|
{
|
|
ga_append(&blob->bv_ga, (triple >> 16) & 0xFF);
|
|
j++;
|
|
}
|
|
if (j < decoded_len)
|
|
{
|
|
ga_append(&blob->bv_ga, (triple >> 8) & 0xFF);
|
|
j++;
|
|
}
|
|
if (j < decoded_len)
|
|
{
|
|
ga_append(&blob->bv_ga, triple & 0xFF);
|
|
j++;
|
|
}
|
|
|
|
if (j == decoded_len)
|
|
{
|
|
// Check for invalid padding bytes (based on the
|
|
// "Base64 Malleability in Practice" ACM paper).
|
|
if ((data[input_len - 2] == '=' && ((sextet_b & 0xF) != 0))
|
|
|| ((data[input_len - 1] == '=') && ((sextet_c & 0x3) != 0)))
|
|
{
|
|
semsg(_(e_invalid_argument_str), data);
|
|
ga_clear(&blob->bv_ga);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* "base64_decode(string)" function
|
|
*/
|
|
static void
|
|
f_base64_decode(typval_T *argvars, typval_T *rettv)
|
|
{
|
|
if (check_for_string_arg(argvars, 0) == FAIL)
|
|
return;
|
|
|
|
if (rettv_blob_alloc(rettv) == FAIL)
|
|
return;
|
|
|
|
char_u *str = tv_get_string_chk(&argvars[0]);
|
|
if (str != NULL)
|
|
base64_decode(str, rettv->vval.v_blob);
|
|
}
|
|
|
|
/*
|
|
* "base64_encode(blob)" function
|
|
*/
|
|
static void
|
|
f_base64_encode(typval_T *argvars, typval_T *rettv)
|
|
{
|
|
if (check_for_blob_arg(argvars, 0) == FAIL)
|
|
return;
|
|
|
|
rettv->v_type = VAR_STRING;
|
|
rettv->vval.v_string = NULL;
|
|
|
|
blob_T *blob = argvars->vval.v_blob;
|
|
if (blob != NULL)
|
|
rettv->vval.v_string = base64_encode(blob);
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
/*
|
|
* "bindtextdomain(package, path)" function
|
|
*/
|
|
static void
|
|
f_bindtextdomain(typval_T *argvars, typval_T *rettv)
|
|
{
|
|
rettv->v_type = VAR_BOOL;
|
|
rettv->vval.v_number = VVAL_TRUE;
|
|
|
|
if (check_for_nonempty_string_arg(argvars, 0) == FAIL
|
|
|| check_for_nonempty_string_arg(argvars, 1) == FAIL)
|
|
return;
|
|
|
|
if (strcmp((const char *)argvars[0].vval.v_string, VIMPACKAGE) == 0)
|
|
semsg(_(e_invalid_argument_str), tv_get_string(&argvars[0]));
|
|
else
|
|
{
|
|
if (bindtextdomain((const char *)argvars[0].vval.v_string, (const char *)argvars[1].vval.v_string) == NULL)
|
|
{
|
|
do_outofmem_msg((long)0);
|
|
rettv->vval.v_number = VVAL_FALSE;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* "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 *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
|
|
|
|
if (argvars[0].v_type == VAR_STRING)
|
|
{
|
|
char_u *p = func;
|
|
tofree = trans_function_name(&p, NULL, FALSE, TFN_INT|TFN_QUIET);
|
|
if (tofree == NULL)
|
|
{
|
|
emsg_funcname(e_unknown_function_str, func);
|
|
return;
|
|
}
|
|
func = tofree;
|
|
}
|
|
|
|
if (argvars[2].v_type != VAR_UNKNOWN)
|
|
{
|
|
if (check_for_dict_arg(argvars, 2) == FAIL)
|
|
goto done;
|
|
|
|
selfdict = argvars[2].vval.v_dict;
|
|
}
|
|
|
|
(void)func_call(func, &argvars[1], partial, selfdict, rettv);
|
|
|
|
done:
|
|
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 = ml_get_len(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 != MAXCOL && --col < 0)
|
|
col = 0;
|
|
curwin->w_cursor.col = col;
|
|
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 = curbuf->b_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 = argvars[0].vval.v_partial == NULL;
|
|
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 = object_empty(argvars[0].vval.v_object);
|
|
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;
|
|
size_t 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 if (STRCMP(what, "arity") == 0)
|
|
{
|
|
int required = 0, optional = 0, varargs = FALSE;
|
|
char_u *name = partial_name(pt);
|
|
|
|
get_func_arity(name, &required, &optional, &varargs);
|
|
|
|
rettv->v_type = VAR_DICT;
|
|
if (rettv_dict_alloc(rettv) == OK)
|
|
{
|
|
dict_T *dict = rettv->vval.v_dict;
|
|
|
|
// Take into account the arguments of the partial, if any.
|
|
// Note that it is possible to supply more arguments than the function
|
|
// accepts.
|
|
if (pt->pt_argc >= required + optional)
|
|
required = optional = 0;
|
|
else if (pt->pt_argc > required)
|
|
{
|
|
optional -= pt->pt_argc - required;
|
|
required = 0;
|
|
}
|
|
else
|
|
required -= pt->pt_argc;
|
|
|
|
dict_add_number(dict, "required", required);
|
|
dict_add_number(dict, "optional", optional);
|
|
dict_add_bool(dict, "varargs", varargs);
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* "getcellpixels()" function
|
|
*/
|
|
static void
|
|
f_getcellpixels(typval_T *argvars UNUSED, typval_T *rettv)
|
|
{
|
|
if (rettv_list_alloc(rettv) == FAIL)
|
|
return;
|
|
|
|
#if defined(FEAT_GUI)
|
|
if (gui.in_use)
|
|
{
|
|
// success pixel size and no gui.
|
|
list_append_number(rettv->vval.v_list, (varnumber_T)gui.char_width);
|
|
list_append_number(rettv->vval.v_list, (varnumber_T)gui.char_height);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
struct cellsize cs;
|
|
#if defined(UNIX)
|
|
mch_calc_cell_size(&cs);
|
|
#else
|
|
// Non-Unix CUIs are not supported, so set this to -1x-1.
|
|
cs.cs_xpixel = -1;
|
|
cs.cs_ypixel = -1;
|
|
#endif
|
|
|
|
// failed get pixel size.
|
|
if (cs.cs_xpixel == -1)
|
|
return;
|
|
|
|
// success pixel size and no gui.
|
|
list_append_number(rettv->vval.v_list, (varnumber_T)cs.cs_xpixel);
|
|
list_append_number(rettv->vval.v_list, (varnumber_T)cs.cs_ypixel);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* "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;
|
|
}
|
|
|
|
static int
|
|
getregionpos(
|
|
typval_T *argvars,
|
|
typval_T *rettv,
|
|
pos_T *p1,
|
|
pos_T *p2,
|
|
int *inclusive,
|
|
int *region_type,
|
|
oparg_T *oap)
|
|
{
|
|
int fnum1 = -1, fnum2 = -1;
|
|
char_u *type;
|
|
buf_T *findbuf;
|
|
char_u default_type[] = "v";
|
|
int block_width = 0;
|
|
int is_select_exclusive;
|
|
int l;
|
|
|
|
if (rettv_list_alloc(rettv) == FAIL)
|
|
return FAIL;
|
|
|
|
if (check_for_list_arg(argvars, 0) == FAIL
|
|
|| check_for_list_arg(argvars, 1) == FAIL
|
|
|| check_for_opt_dict_arg(argvars, 2) == FAIL)
|
|
return FAIL;
|
|
|
|
if (list2fpos(&argvars[0], p1, &fnum1, NULL, FALSE) != OK
|
|
|| list2fpos(&argvars[1], p2, &fnum2, NULL, FALSE) != OK
|
|
|| fnum1 != fnum2)
|
|
return FAIL;
|
|
|
|
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)
|
|
{
|
|
char_u *p = type + 1;
|
|
|
|
if (*p != NUL && ((block_width = getdigits(&p)) <= 0 || *p != NUL))
|
|
{
|
|
semsg(_(e_invalid_value_for_argument_str_str), "type", type);
|
|
return FAIL;
|
|
}
|
|
*region_type = MBLOCK;
|
|
}
|
|
else
|
|
{
|
|
semsg(_(e_invalid_value_for_argument_str_str), "type", type);
|
|
return FAIL;
|
|
}
|
|
|
|
findbuf = fnum1 != 0 ? buflist_findnr(fnum1) : curbuf;
|
|
if (findbuf == NULL || findbuf->b_ml.ml_mfp == NULL)
|
|
{
|
|
emsg(_(e_buffer_is_not_loaded));
|
|
return FAIL;
|
|
}
|
|
|
|
if (p1->lnum < 1 || p1->lnum > findbuf->b_ml.ml_line_count)
|
|
{
|
|
semsg(_(e_invalid_line_number_nr), p1->lnum);
|
|
return FAIL;
|
|
}
|
|
if (p1->col == MAXCOL)
|
|
p1->col = ml_get_buf_len(findbuf, p1->lnum) + 1;
|
|
else if (p1->col < 1 || p1->col > ml_get_buf_len(findbuf, p1->lnum) + 1)
|
|
{
|
|
semsg(_(e_invalid_column_number_nr), p1->col);
|
|
return FAIL;
|
|
}
|
|
|
|
if (p2->lnum < 1 || p2->lnum > findbuf->b_ml.ml_line_count)
|
|
{
|
|
semsg(_(e_invalid_line_number_nr), p2->lnum);
|
|
return FAIL;
|
|
}
|
|
if (p2->col == MAXCOL)
|
|
p2->col = ml_get_buf_len(findbuf, p2->lnum) + 1;
|
|
else if (p2->col < 1 || p2->col > ml_get_buf_len(findbuf, p2->lnum) + 1)
|
|
{
|
|
semsg(_(e_invalid_column_number_nr), p2->col);
|
|
return FAIL;
|
|
}
|
|
|
|
curbuf = findbuf;
|
|
curwin->w_buffer = curbuf;
|
|
virtual_op = virtual_active();
|
|
|
|
// NOTE: Adjustment 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))
|
|
// When backing up to previous line, inclusive becomes false.
|
|
*inclusive = !unadjust_for_sel_inner(p2);
|
|
// If p2 is on NUL (end of line), inclusive becomes false.
|
|
if (*inclusive && !virtual_op && *ml_get_pos(p2) == NUL)
|
|
*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);
|
|
oap->motion_type = MBLOCK;
|
|
oap->inclusive = TRUE;
|
|
oap->op_type = OP_NOP;
|
|
oap->start = *p1;
|
|
oap->end = *p2;
|
|
oap->start_vcol = MIN(sc1, sc2);
|
|
if (block_width > 0)
|
|
oap->end_vcol = oap->start_vcol + block_width - 1;
|
|
else if (is_select_exclusive && ec1 < sc2 && 0 < sc2 && ec2 > ec1)
|
|
oap->end_vcol = sc2 - 1;
|
|
else
|
|
oap->end_vcol = MAX(ec1, ec2);
|
|
}
|
|
|
|
// Include the trailing byte of a multi-byte char.
|
|
l = mb_ptr2len((char_u *)ml_get_pos(p2));
|
|
if (l > 1)
|
|
p2->col += l - 1;
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*
|
|
* "getregion()" function
|
|
*/
|
|
static void
|
|
f_getregion(typval_T *argvars, typval_T *rettv)
|
|
{
|
|
pos_T p1, p2;
|
|
int inclusive = TRUE;
|
|
int region_type = -1;
|
|
oparg_T oa;
|
|
|
|
buf_T *save_curbuf;
|
|
int save_virtual;
|
|
char_u *akt = NULL;
|
|
linenr_T lnum;
|
|
|
|
save_curbuf = curbuf;
|
|
save_virtual = virtual_op;
|
|
|
|
if (getregionpos(argvars, rettv,
|
|
&p1, &p2, &inclusive, ®ion_type, &oa) == FAIL)
|
|
return;
|
|
|
|
for (lnum = p1.lnum; lnum <= p2.lnum; lnum++)
|
|
{
|
|
int ret = 0;
|
|
struct block_def bd;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
// getregionpos() may change curbuf and virtual_op
|
|
curbuf = save_curbuf;
|
|
curwin->w_buffer = curbuf;
|
|
virtual_op = save_virtual;
|
|
}
|
|
|
|
static void
|
|
add_regionpos_range(typval_T *rettv, pos_T p1, pos_T p2)
|
|
{
|
|
list_T *l1, *l2, *l3;
|
|
|
|
l1 = list_alloc();
|
|
if (l1 == NULL)
|
|
return;
|
|
|
|
if (list_append_list(rettv->vval.v_list, l1) == FAIL)
|
|
{
|
|
vim_free(l1);
|
|
return;
|
|
}
|
|
|
|
l2 = list_alloc();
|
|
if (l2 == NULL)
|
|
{
|
|
vim_free(l1);
|
|
return;
|
|
}
|
|
|
|
if (list_append_list(l1, l2) == FAIL)
|
|
{
|
|
vim_free(l1);
|
|
vim_free(l2);
|
|
return;
|
|
}
|
|
|
|
l3 = list_alloc();
|
|
if (l3 == NULL)
|
|
{
|
|
vim_free(l1);
|
|
vim_free(l2);
|
|
return;
|
|
}
|
|
|
|
if (list_append_list(l1, l3) == FAIL)
|
|
{
|
|
vim_free(l1);
|
|
vim_free(l2);
|
|
vim_free(l3);
|
|
return;
|
|
}
|
|
|
|
list_append_number(l2, curbuf->b_fnum);
|
|
list_append_number(l2, p1.lnum);
|
|
list_append_number(l2, p1.col);
|
|
list_append_number(l2, p1.coladd);
|
|
|
|
list_append_number(l3, curbuf->b_fnum);
|
|
list_append_number(l3, p2.lnum);
|
|
list_append_number(l3, p2.col);
|
|
list_append_number(l3, p2.coladd);
|
|
}
|
|
|
|
/*
|
|
* "getregionpos()" function
|
|
*/
|
|
static void
|
|
f_getregionpos(typval_T *argvars, typval_T *rettv)
|
|
{
|
|
pos_T p1, p2;
|
|
int inclusive = TRUE;
|
|
int region_type = -1;
|
|
int allow_eol = FALSE;
|
|
oparg_T oa;
|
|
int lnum;
|
|
|
|
buf_T *save_curbuf;
|
|
int save_virtual;
|
|
|
|
save_curbuf = curbuf;
|
|
save_virtual = virtual_op;
|
|
|
|
if (getregionpos(argvars, rettv,
|
|
&p1, &p2, &inclusive, ®ion_type, &oa) == FAIL)
|
|
return;
|
|
|
|
if (argvars[2].v_type == VAR_DICT)
|
|
allow_eol = dict_get_bool(argvars[2].vval.v_dict, "eol", FALSE);
|
|
|
|
for (lnum = p1.lnum; lnum <= p2.lnum; lnum++)
|
|
{
|
|
pos_T ret_p1, ret_p2;
|
|
char_u *line = ml_get(lnum);
|
|
colnr_T line_len = ml_get_len(lnum);
|
|
|
|
if (region_type == MLINE)
|
|
{
|
|
ret_p1.col = 1;
|
|
ret_p1.coladd = 0;
|
|
ret_p2.col = MAXCOL;
|
|
ret_p2.coladd = 0;
|
|
}
|
|
else
|
|
{
|
|
struct block_def bd;
|
|
|
|
if (region_type == MBLOCK)
|
|
block_prep(&oa, &bd, lnum, FALSE);
|
|
else
|
|
charwise_block_prep(p1, p2, &bd, lnum, inclusive);
|
|
|
|
if (bd.is_oneChar) // selection entirely inside one char
|
|
{
|
|
if (region_type == MBLOCK)
|
|
{
|
|
ret_p1.col = mb_prevptr(line, bd.textstart) - line + 1;
|
|
ret_p1.coladd = bd.start_char_vcols
|
|
- (bd.start_vcol - oa.start_vcol);
|
|
}
|
|
else
|
|
{
|
|
ret_p1.col = p1.col + 1;
|
|
ret_p1.coladd = p1.coladd;
|
|
}
|
|
}
|
|
else if (region_type == MBLOCK && oa.start_vcol > bd.start_vcol)
|
|
{
|
|
// blockwise selection entirely beyond end of line
|
|
ret_p1.col = MAXCOL;
|
|
ret_p1.coladd = oa.start_vcol - bd.start_vcol;
|
|
bd.is_oneChar = TRUE;
|
|
}
|
|
else if (bd.startspaces > 0)
|
|
{
|
|
ret_p1.col = mb_prevptr(line, bd.textstart) - line + 1;
|
|
ret_p1.coladd = bd.start_char_vcols - bd.startspaces;
|
|
}
|
|
else
|
|
{
|
|
ret_p1.col = bd.textcol + 1;
|
|
ret_p1.coladd = 0;
|
|
}
|
|
|
|
if (bd.is_oneChar) // selection entirely inside one char
|
|
{
|
|
ret_p2.col = ret_p1.col;
|
|
ret_p2.coladd = ret_p1.coladd + bd.startspaces + bd.endspaces;
|
|
}
|
|
else if (bd.endspaces > 0)
|
|
{
|
|
ret_p2.col = bd.textcol + bd.textlen + 1;
|
|
ret_p2.coladd = bd.endspaces;
|
|
}
|
|
else
|
|
{
|
|
ret_p2.col = bd.textcol + bd.textlen;
|
|
ret_p2.coladd = 0;
|
|
}
|
|
}
|
|
|
|
if (!allow_eol && ret_p1.col > line_len)
|
|
{
|
|
ret_p1.col = 0;
|
|
ret_p1.coladd = 0;
|
|
}
|
|
else if (ret_p1.col > line_len + 1)
|
|
ret_p1.col = line_len + 1;
|
|
|
|
if (!allow_eol && ret_p2.col > line_len)
|
|
{
|
|
ret_p2.col = ret_p1.col == 0 ? 0 : line_len;
|
|
ret_p2.coladd = 0;
|
|
}
|
|
else if (ret_p2.col > line_len + 1)
|
|
ret_p2.col = line_len + 1;
|
|
|
|
ret_p1.lnum = lnum;
|
|
ret_p2.lnum = lnum;
|
|
add_regionpos_range(rettv, ret_p1, ret_p2);
|
|
}
|
|
|
|
// getregionpos() may change curbuf and virtual_op
|
|
curbuf = save_curbuf;
|
|
curwin->w_buffer = curbuf;
|
|
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, ®len))
|
|
{
|
|
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 defined(HAVE_BIND_TEXTDOMAIN_CODESET)
|
|
char *prev = NULL;
|
|
#endif
|
|
|
|
if (check_for_nonempty_string_arg(argvars, 0) == FAIL
|
|
|| check_for_opt_string_arg(argvars, 1) == FAIL)
|
|
return;
|
|
|
|
rettv->v_type = VAR_STRING;
|
|
|
|
if (argvars[1].v_type == VAR_STRING &&
|
|
argvars[1].vval.v_string != NULL &&
|
|
*(argvars[1].vval.v_string) != NUL)
|
|
{
|
|
#if defined(HAVE_BIND_TEXTDOMAIN_CODESET)
|
|
prev = bind_textdomain_codeset((const char *)argvars[1].vval.v_string, (char *)p_enc);
|
|
#endif
|
|
|
|
#if defined(HAVE_DGETTEXT)
|
|
rettv->vval.v_string = vim_strsave((char_u *)dgettext((const char *)argvars[1].vval.v_string, (const char *)argvars[0].vval.v_string));
|
|
#else
|
|
textdomain((const char *)argvars[1].vval.v_string);
|
|
rettv->vval.v_string = vim_strsave((char_u *)_(argvars[0].vval.v_string));
|
|
textdomain(VIMPACKAGE);
|
|
#endif
|
|
|
|
#if defined(HAVE_BIND_TEXTDOMAIN_CODESET)
|
|
if (prev != NULL)
|
|
bind_textdomain_codeset((const char *)argvars[1].vval.v_string, prev);
|
|
#endif
|
|
}
|
|
else
|
|
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_con_gui",
|
|
#if defined(FEAT_CON_DIALOG) && defined(FEAT_GUI_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);
|
|
}
|
|
|
|
/*
|
|
* "id()" function
|
|
* Identity. Return address of item as a hex string, %p format.
|
|
* Currently only valid for object/container types.
|
|
* Return empty string if not an object.
|
|
*/
|
|
#ifdef VMS // VMS does not have defined uintptr_t
|
|
# if defined(HAVE_NO_LONG_LONG)
|
|
typedef unsigned int uintptr_t;
|
|
# else
|
|
typedef unsigned long long uintptr_t;
|
|
# endif
|
|
#endif // VMS
|
|
|
|
static void
|
|
f_id(typval_T *argvars, typval_T *rettv)
|
|
{
|
|
char numbuf[NUMBUFLEN];
|
|
char *p = numbuf;
|
|
|
|
switch (argvars[0].v_type)
|
|
{
|
|
case VAR_LIST:
|
|
case VAR_DICT:
|
|
case VAR_OBJECT:
|
|
case VAR_JOB:
|
|
case VAR_CHANNEL:
|
|
case VAR_BLOB:
|
|
// Assume pointer value in typval_T vval union at common location.
|
|
if (argvars[0].vval.v_object != NULL)
|
|
{
|
|
// "v" gets the address as an integer
|
|
uintptr_t v = (uintptr_t)(void *)argvars[0].vval.v_object;
|
|
// Build a hex string from the item's address; it is in
|
|
// reverse order. Ignore trailing zeros.
|
|
for (; p < numbuf + sizeof(uintptr_t) * 2 && v != 0;
|
|
++p, v >>= 4)
|
|
*p = "0123456789abcdef"[v & 0xf];
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
*p = NUL;
|
|
|
|
rettv->v_type = VAR_STRING;
|
|
rettv->vval.v_string = vim_strsave((char_u *)numbuf);
|
|
}
|
|
|
|
/*
|
|
* "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))
|
|
{
|
|
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))
|
|
{
|
|
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);
|
|
|
|
int called_emsg_start = called_emsg;
|
|
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;
|
|
|
|
if (called_emsg != called_emsg_start)
|
|
return -1;
|
|
}
|
|
|
|
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);
|
|
|
|
int called_emsg_start = called_emsg;
|
|
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;
|
|
|
|
if (called_emsg != called_emsg_start)
|
|
return -1;
|
|
}
|
|
|
|
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].vval.v_string == NUL))
|
|
|| (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_OBJECT:
|
|
rettv->vval.v_number = object_len(argvars[0].vval.v_object);
|
|
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_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)
|
|
{
|
|
#ifdef FEAT_DIFF
|
|
// in diff mode, prevent that the window scrolls
|
|
// and keep the topline
|
|
if (curwin->w_p_diff && switchwin.sw_curwin->w_p_diff)
|
|
skip_update_topline = TRUE;
|
|
#endif
|
|
check_cursor();
|
|
fp = var2fpos(&argvars[0], TRUE, &fnum, FALSE);
|
|
}
|
|
#ifdef FEAT_DIFF
|
|
if (curwin->w_p_diff && switchwin.sw_curwin->w_p_diff)
|
|
skip_update_topline = FALSE;
|
|
#endif
|
|
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(®match, 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, ®match, 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, ®match, 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];
|
|
dict_T *locals;
|
|
|
|
if (check_restricted() || check_secure())
|
|
return;
|
|
|
|
if (in_vim9script()
|
|
&& (check_for_string_arg(argvars, 0) == FAIL
|
|
|| check_for_opt_dict_arg(argvars, 1) == FAIL))
|
|
return;
|
|
|
|
if (p_pyx == 0)
|
|
p_pyx = 3;
|
|
|
|
if (argvars[1].v_type == VAR_DICT)
|
|
{
|
|
locals = argvars[1].vval.v_dict;
|
|
}
|
|
else if (argvars[1].v_type != VAR_UNKNOWN)
|
|
{
|
|
emsg(_(e_dictionary_required));
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
locals = NULL;
|
|
}
|
|
|
|
str = tv_get_string_buf(&argvars[0], buf);
|
|
do_py3eval(str, locals, rettv);
|
|
}
|
|
#endif
|
|
|
|
#ifdef FEAT_PYTHON
|
|
/*
|
|
* "pyeval()" function
|
|
*/
|
|
static void
|
|
f_pyeval(typval_T *argvars, typval_T *rettv)
|
|
{
|
|
char_u *str;
|
|
char_u buf[NUMBUFLEN];
|
|
dict_T *locals;
|
|
|
|
if (check_restricted() || check_secure())
|
|
return;
|
|
|
|
if (in_vim9script() && (
|
|
check_for_string_arg(argvars, 0) == FAIL ||
|
|
check_for_opt_dict_arg(argvars, 1) == FAIL ) )
|
|
return;
|
|
|
|
if (p_pyx == 0)
|
|
p_pyx = 2;
|
|
|
|
if (argvars[1].v_type == VAR_DICT)
|
|
{
|
|
locals = argvars[1].vval.v_dict;
|
|
}
|
|
else if (argvars[1].v_type != VAR_UNKNOWN)
|
|
{
|
|
emsg( "Invalid argument: must be dict" );
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
locals = NULL;
|
|
}
|
|
|
|
str = tv_get_string_buf(&argvars[0], buf);
|
|
do_pyeval(str, locals, 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
|
|
|| check_for_opt_dict_arg(argvars, 1) == 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)
|
|
{
|
|
struct {
|
|
union {
|
|
UINT32_T number;
|
|
char_u bytes[sizeof(UINT32_T)];
|
|
} contents;
|
|
} buf;
|
|
|
|
if (srand_seed_for_testing_is_used)
|
|
{
|
|
*x = srand_seed_for_testing;
|
|
return;
|
|
}
|
|
|
|
if (mch_get_random(buf.contents.bytes, sizeof(buf.contents.bytes)) == OK)
|
|
{
|
|
*x = buf.contents.number;
|
|
return;
|
|
}
|
|
|
|
// The system's random number generator 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, ®len))
|
|
{
|
|
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;
|
|
size_t patlen;
|
|
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
|
|
|
|
patlen = STRLEN(pat);
|
|
|
|
// Repeat until {skip} returns FALSE.
|
|
for (;;)
|
|
{
|
|
subpatnum = searchit(curwin, curbuf, &pos, NULL, dir, pat, patlen, 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;
|
|
size_t patlen;
|
|
size_t spatlen;
|
|
size_t epatlen;
|
|
size_t pat2size;
|
|
size_t pat2len;
|
|
size_t pat3size;
|
|
size_t pat3len;
|
|
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).
|
|
spatlen = STRLEN(spat);
|
|
epatlen = STRLEN(epat);
|
|
pat2size = spatlen + epatlen + 17;
|
|
pat2 = alloc(pat2size);
|
|
if (pat2 == NULL)
|
|
goto theend;
|
|
pat3size = spatlen + STRLEN(mpat) + epatlen + 25;
|
|
pat3 = alloc(pat3size);
|
|
if (pat3 == NULL)
|
|
goto theend;
|
|
pat2len = vim_snprintf((char *)pat2, pat2size, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat);
|
|
if (*mpat == NUL)
|
|
{
|
|
STRCPY(pat3, pat2);
|
|
pat3len = pat2len;
|
|
}
|
|
else
|
|
pat3len = vim_snprintf((char *)pat3, pat3size, "\\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;
|
|
patlen = pat3len;
|
|
for (;;)
|
|
{
|
|
searchit_arg_T sia;
|
|
|
|
CLEAR_FIELD(sia);
|
|
sia.sa_stop_lnum = lnum_stop;
|
|
n = searchit(curwin, curbuf, &pos, NULL, dir, pat, patlen, 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, FALSE);
|
|
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, SMT_ALL, 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(®match, 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, STRLEN(str), pat, sub, expr, flg, NULL);
|
|
}
|
|
|
|
/*
|
|
* "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)ml_get_len(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)ml_get_len(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)ml_get_len(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_TYPEALIAS: n = VAR_TYPE_TYPEALIAS; break;
|
|
case VAR_CLASS:
|
|
{
|
|
class_T *cl = argvars[0].vval.v_class;
|
|
if (cl != NULL && IS_ENUM(cl))
|
|
n = VAR_TYPE_ENUM;
|
|
else
|
|
n = VAR_TYPE_CLASS;
|
|
break;
|
|
}
|
|
case VAR_OBJECT:
|
|
{
|
|
object_T *obj = argvars[0].vval.v_object;
|
|
|
|
if (obj == NULL)
|
|
n = VAR_TYPE_OBJECT;
|
|
else
|
|
{
|
|
class_T *cl = argvars[0].vval.v_object->obj_class;
|
|
if (IS_ENUM(cl))
|
|
n = VAR_TYPE_ENUMVALUE;
|
|
else
|
|
n = VAR_TYPE_OBJECT;
|
|
}
|
|
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)ml_get_len(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
|